uC/OS-II的任务调度的思想是:“近似地每时每刻让优先级最高的就绪任务处于运行状态”。
调度器的定义:在多任务系统中,令CPU中止当前正在运行的任务转而去运行另一个任务的工作叫做任务切换,而按照某种规则进行任务切换的工作叫做任务的调度。
uC/OS-II中,任务调度由任务调度器来完成。任务调度器的主要工作有两项:一是在任务就绪表中查找具有最高优先级别的就绪任务;二是实现任务的切换。
- 调度器的种类
- 任务级:函数OSSched()实现。
- 中断级:函数OSIntExt()实现。
调度器把任务切换的工作分为两个步骤:第一步是获得待运行任务的TCB指针;第二步进行断点数据的切换。
获得待运行就绪任务控制块的指针
由于操作系统是通过任务的任务控制块TCB来管理任务,因此调度器真正实施任务切换之前的主要工作就是要获得待运行任务的任务控制块指针和当前任务的任务控制块指针。
- 因为被中止的任务的任务控制块指针就存放在全局变量OSTCBCur中,所以调度器这部分的工作主要是获得待运行任务的任务控制块指针。
- 任务级调度器OSSched()的源代码如下:
void OSSched(void)
{
#if OS_CRITICAL_MOTHOD == 3
OS_CPU_SR cpu_sr;
#endif
INT8U y;
OS_ENTER_CRITICAL();
if((OSLockNesting | OSIntNesting ) == 0)
{
y = OSUnMapTbl[OSRdyGrp]; //得到最高优先任务
OSPrioHighRdy = (INT8U) ((y<<3) + UnMapTbl[OSRdyTbl[y]]); //得到任务控制块指针
if(OSPrioHighRdy != OSPrioCur)
{
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
OSCtxSwCtr++; //统计任务切换次数的计数器加1
OS_TASK_SW(); //任务切换
}
}
OS_EXIT_CRITICAL();
}
调度器OSSched在确认未被上锁并且不是中断服务程序调用调度器的情况下,首先从任务就绪表中查得最高优先级别就绪任务的优先级别OSPrioHighRdy;然后在确认这个就绪任务不是当前正在运行的任务(OSPrioCur是存放正在运行任务的优先级别的变量)的条件下,用OSPrioHighRdy作为下标去访问数组OSTCBPrioTbl[],把数组元素OSTCBPrioTbl[OSPrioHighRdy]的值(即待运行就绪任务的任务控制块指针)赋给指针变量OSTCBHighRdy。接下来就依据OSTCBHighRdy和OSTCBCur这两个指针分别指向待运行任务控制块和当前任务控制块,从而在宏OS_TASK_SW中实施任务切换。过程如下图所示
调度器在任务切换前获得的两个指针OSTCBCur和OSTCBHighRdy
任务切换宏OS_TASK_SW
简单来说,任务切换就是中止正在运行的任务(当前任务),转而去运行另一个任务的操作。 如果把任务被中止运行时的位置叫做断点,把当时存放在CPU的PC、PSW和通用寄存器等各寄存器中的数据叫做断点数据,那么任务恢复运行时,必须在断点处以断点数据作为初始数据接着运行,才能实现“无缝”的接续运行。
任务在被中止运行时保护断点数据的动作如图2-11所示。
任务保护断点时的压栈动作
- 如何实现使被中止运行的任务在恢复运行时可以实现“无缝”的接续运行?
要实现“无缝”的接续运行,则必须在任务被中止时就把任务的断点数据保存到堆栈中;而在被重新运行时,则要把堆栈中的这些断点数据恢复到CPU的各寄存器中。
由上图可知,一个被中止的任务能否正确地在断点处恢复运行,其关键在于是否能正确的在CPU各寄存器中恢复断点数据;而能够正确恢复断点数据的关键是CPU的堆栈指针SP是否有正确的指向。为了防止被中止任务堆栈指针的丢失,被中止任务在保存断点时,要把当时CPU的SP的值保存到该任务控制块的成员OSTCBStkPtr中。
综上所述,任务的切换就是断点数据的切换,断点数据的切换也就是CPU堆栈指针的切换,被中止运行任务的任务堆栈指针要保护到任务的任务控制块中,待运行任务的任务的任务堆栈指针要由该任务控制块转存到CPU的SP中。
调度器在任务切换时工作过程如图2-12所示。
图2-12 调度器进行任务切换的动作
为了完成上述操作,OSCtxSw要依次做如下工作:
- 把被中止任务的断点指针保存到任务堆栈中;
- 把CPU通用寄存器的内容保存到任务堆栈中;
- 把被中止任务的任务堆栈指针当前值保存到该任务的任务控制块的OSTCBStkPtr中;
- 获得待运行任务的任务控制块;
- 使CPU通过任务控制块获得待运行任务的任务堆栈指针;
- 把待运行任务的任务堆栈指针中通用寄存器的内容恢复到CPU的通用寄存器中;
- 使CPU获得待运行任务的断点指针。
- 断点的保护和恢复
想办法引发一次中断,并让中断向量指向OSCtxSw(),利用系统在跳转到中断服务程序时会自动把断点指针压入堆栈的功能,把断点指针存入堆栈,而利用中断返回指令IRET能把断点指针推入CPU的PC寄存器的功能,恢复待运行任务的断点,这样就可以实现断点的保存和恢复了。