uC/OS-II的任务调度机制(在文件OS_CORE.C中)
uC/OS-II有两种任务调度器:任务级的调度器和中断级的调度器。任务级的调度器由函数OSSched()来实现;中断级的调度器由函数OSIntExit()来实现。
调度器OSSched()的前半部分叫调度部分,其职责就是寻找优先级别最高的就绪任务作为待运行任务。
if ((OSLockNesting | OSIntNesting) == 0)
在中断服务程序中不允许进行任务调度,所以每当进入中断服务程序就要把变量OSIntNesting加1,而当中断返回前则要把OSIntNesting减1,这样调度器就不会在中断服务程序中进行调度工作了。
调度器是否存在调度禁区(调度死区)以及这个禁区有多大,是直接影响内核实时性的一个重要因素。因为在一般操作系统中,是禁止在系统调用中进行调度的,而uC/OS-II则无此限制。因此调度禁区更小,可剥夺性也就显得更为强硬。
任务级调度器获得了最高级就绪任务的任务控制块指针之后,任务切换的工作是由宏OSCtxSw()来执行。(OSCtxSw()在文件OS_CPU_A.ASM中,由汇编编写)。中断级的调度器任务切换工作是由OSIntCtxSw()来完成的,通常也是用汇编语言来完成的(在文件OS_CPU_A.ASM中)。
只有就绪任务表的内容发生变化时才需要调度。uC/OS-II应在所有系统调用函数的末尾及中断服务程序结束之前调用调度器OSSched()。
uC/OS-II的初始化:void OSInit(void);
uC/OS-II的启动:void OSStart(void);
这两个函数在OS_CORE.C中。
需要注意的是OSIntNesting是一个记录中断嵌套层数的计数器变量,所以只有当其值为0的时候才允许调度。因此,尽管在嵌套中断中每个中断服务程序都调用了中断退出函数OSIntExit(),但并非每个嵌套中断结束前都会发生调度,而只有当变量OSIntNesting为0时才会发生调度。
临界段
处理器只有在开放中断期间才能响应中断请求,而在其他时间是不能响应中断请求的。因为在应用程序中经常有一些代码段必须不受任何干扰地连续运行,这样的代码段较临界段。因此为了使临界段在运行时不受中断所打断,在临界段代码前必须使处理器屏蔽中断请求,而在临界段代码后重新接触屏蔽,使处理器可以响应中断请求。
uC/OS-II中用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()这两个宏来实现中断的开放和关闭。而把与系统硬件相关的关中断和开中断的指令分别封装在这两个宏中。
uC/OS-II提示,不要再临界段调用uC/OS-II提供的功能函数,以免系统崩溃。因为在功能函数中通常也会用到这两个宏,而处理器的开中断和关中断质量是不能嵌套使用的。
uC/OS-II的时钟
uC/OS-II与大多数计算机系统一样,用硬件定时器产生一个周期为毫秒(ms)级的周期性中断来实现系统时钟。最小的时钟单位就是两次中断之间的间隔的时间,这个最小时钟单位叫做时钟节拍。
硬件定时器以时钟节拍为周期定时地产生中断,该中断的中断服务程序叫_OSTickISR()(在文件OS_CPU_A.ASM中用汇编编写)。中断服务程序通过调用函数OSTimeTick()来完成系统在每个时钟节拍需要完成的工作。
void OSTimeTick (void)
{
OS_TCB *ptcb;
OSTimeTickHook(); /* Call user definable hook */
ptcb = OSTCBList; /* Point at first TCB in TCB list */
while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { /* Go through all TCBs in TCB list */
OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0) { /* Delayed or waiting for event with TO */
if (--ptcb->OSTCBDly == 0) { /* Decrement nbr of ticks to end of delay */
if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) { /* Is task suspended? */
OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make task Rdy to Run (timed out)*/
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
} else { /* Yes, Leave 1 tick to prevent ... */
ptcb->OSTCBDly = 1; /* ... loosing the task when the ... */
} /* ... suspension is removed. */
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */
OSTime++;
OS_EXIT_CRITICAL();
}
uC/OS-II在每次响应定时中断时调用OSTimeTick()做了两件事情:一是把用来记录时间进程的计数器OSTime加1;二是遍历任务控制块链表中的所有任务控制块,把各个任务控制块中用来存放任务延时时限的OSTCBDly变量减1,并使该项为0,同时又不是被挂起的任务(即延时时间已到的任务)进入就绪状态。
OSTimeTick()是系统调用函数,为了使应用程序设计人员能在系统调用的函数中插入一些自己的工作,uC/OS-II提供了时钟节拍服务函数的OSTimeTickHook()钩子函数。以供用户在系统调用函数中书写自己的代码。
uC/OS-II的时间管理(在文件OS_TIME.C中)
uC/OS-II在时间管理上,主要是在任务的延时,取消延时,设置和获取系统时间等方面提供服务。
uC/OS-II规定:除了空闲任务之外的所有任务,必须在任务中的合适位置暂停运行一段时间,以给其他任务提供获取处理器的机会。也就是说,高优先级的任务要以延时运行的手段,自愿地出让处理器使用权。
uC/OS-II提供了任务延时函数
void OSTimeDly (INT16U ticks)和
INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli)
void OSTimeDly (INT16U ticks)
{
if (ticks > 0) { /* 0 means no delay! */
OS_ENTER_CRITICAL();
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { /* Delay current task */
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBDly = ticks; /* Load ticks in TCB */
OS_EXIT_CRITICAL();
OSSched(); /* Find next task to run! */
}
}
在延时函数中,会取消任务的就绪状态而进入延时等待状态,调用OSSched()函数实现任务的调度,实现处理器使用权的让出。
取消任务的延时
INT8U OSTimeDlyResume (INT8U prio)
获取和设置系统时间
INT32U OSTimeGet (void)
void OSTimeSet (INT32U ticks)