5就绪表 每个就绪的任务都放在就绪表中,就绪表中有2个变量OSRdyGrp和OSRdyTbl【】。OSRdyGrp中任务按优先级分组,8个任务一组,8位表示8组任务中每组是否有进入就绪态的任务。进入就绪态后,就绪表OSRdyTbl【】中相应元素的相应位置1。因此,使任务进入就绪态的程序为: OSRdyGrp |= OSMapTbl[prio>>3]; OSRdyTbl[prio>>3] |= OSMapTbl[prio & 0x07]; OSMapTbl[]是屏蔽字,将0-7的下标转换成各自位置1的8位值 通过优先级判定表OSUnMapTbl查找任务的优先级,即OSUnMapTbl[OSRdyGrp]×8+OSUnMapTbl[OSRdyTbl[OSUnMapTbl[OSRdyGrp]]]。得到优先级后,查任务控制块优先级表OSTCBPrioTbl【】得到指向相应任务的任务控制块OS_TCB。
6任务调度 任务级的调度是由OSSched完成,中断级的调度是由函数OSIntExt完成的。任务调度函数将找出优先级最高的进入就绪态的任务,检查该任务是否是当前正在运行的任务,如果不是才进行任务调度。为了实现任务的切换将该任务的控制块从任务控制块优先级表中取出并赋给OSTCBHighRdy,将统计切换次数的变量加1来跟踪任务切换次数。最后就可以使用宏调用OS_TASK_SW完成实际上的任务切换 任务切换:将被挂起任务的寄存器推入堆栈再将准备运行的任务的寄存器从栈中恢复到寄存器。因此UCOS运行就绪态任务要做的就是恢复所有的CPU寄存器并运行中断返回指令。这里是一段重点,为了实现任务切换,运行OS_TASK_SW人为模仿了一次中断。在ARM中由软中断指令来实现上述操作。必须给汇编语言函数OSCtxSw提供中断向量。OSCtxSw除了需要OS_TCBHighRdy指向即将被挂起的任务,还需让当前任务控制块OSTCBCur指向即将被挂起的任务。OSCtxSw挂起了正在执行的任务而让CPU执行更重要的任务。 7任务级的任务切换OSCtxSw OSCtxSw是宏调用通常含有软中断指令,切换是靠中断级代码完成的。UCOS将与实际处理器相关的软件中断机制封装起来易于移植。
8 给调度器上锁和开锁 上锁函数OSSchedlock,调用该函数可以禁止任务调度,保持该任务对CPU的使用权,不过中断还是可以识别,中断服务也能得到,因为中断是开着的,中断和调度是两个意思。其中变量OSLockNesting跟踪OSSchedLock函数被调用的次数所以允许嵌套函数。如果OSLockNesting=0调度重新得到允许。
9空闲任务 UCOS-II中总要建立一个空闲任务,主要是计数,然后有一个用户定义的函数OSTaskIdleHook。
10统计任务 除了空闲任务还有一个统计运行时间的任务OSTaskStat,它告诉用户应用程序使用了多少CPU时间,用百分比表示。
11UCOS-II中的中断 中断服务子程序的编写:保存全部CPU寄存器;调用OSIntEnter或者OSIntNesting直接加1;如果是中断的第一层,立即将堆栈指针保存到这个任务;如果需要重新允许中断,必须清中断源,重新开中断;用户设定;调用脱离中断函数OSIntExit,标志着中断服务子程序的结束。 OSIntExit是使中断离开的函数,当中断嵌套层数计数器和锁定嵌套计数器都为0才重新开始调度,然后选择一个优先级最高的任务。最后应该调用中断切换函数OSIntCtxSw而不应该调用任务切换函数OS_TASK_SW。因为在中断之前已经把CPU寄存器存入到中断了的任务堆栈中不需要再用。这部分也牵涉到后面的移植部分。
12时钟节拍 在调用OSStart之后应做的第一件事情就是初始化定时器中断。时钟节拍服务是通过在中断服务子程序中调用OSTimeTick实现的。 时钟节拍中断服务子程序: OSTickISR(void) { 保存CPU寄存器的值 调用OSIntEnter或是将OSIntNesting加1 如果OSIntNesting等于1则将当前堆栈指针存入当前任务控制块的堆栈中 调用OSTimeTick 清发出中断设备的中断 重新允许中断 调用OSIntExit 恢复处理器寄存器的值 执行中断返回指令 } 其中OSTimeTick通过OSTimeTickHook函数进行扩展。除此之外最大的任务就是给每个用户任务控制块OS_TCB中的时间延时项OSTCBDly减1
13UCOS-II初始化 OSInit函数 void OSInit (void)
{
#if OS_VERSION >= 204
OSInitHookBegin(); /* Call port specific initialization code */
#endif OS_InitMisc(); /* Initialize miscellaneous variables */ OS_InitRdyList(); /* Initialize the Ready List */
OS_InitTCBList(); /* Initialize the free list of OS_TCBs */
OS_InitEventList(); /* Initialize the free list of OS_EVENTs */ #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
OS_FlagInit(); /* Initialize the event flag structures */
#endif #if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
OS_MemInit(); /* Initialize the memory manager */
#endif #if (OS_Q_EN > 0) && (OS_MAX_QS > 0)
OS_QInit(); /* Initialize the message queue structures */
#endif OS_InitTaskIdle(); /* Create the Idle Task */
#if OS_TASK_STAT_EN > 0
OS_InitTaskStat(); /* Create the Statistic Task */
#endif #if OS_VERSION >= 204
OSInitHookEnd(); /* Call port specific init. code */
#endif
} 初始化中会建立两个任务,并且初始化5个空的数据结构缓冲区:任务控制缓冲池、事件控制块缓冲池、消息队列缓冲池、标志控制块缓冲池、存储控制块缓冲池,缓冲池的容量在OS_CFG.H中定义
14UCOS-II的启动 OSInit初始化UCOS-II; 通过调用OSTaskCreate或者OSTaskCreateExt创建至少一个任务; OSStart开始多任务调度,永远不会返回; OSStart的主要任务: 从任务就绪表中找到用户建立的优先级最高任务的任务控制块;
|