很多情况下,耗时长的任务并不能设置为最低优先级任务,而划分步骤来执行的方法不但繁琐而且每一步执行的时间也是不确定的(其他低优先级任务获得CPU使用权的时间也会是不确定的)。笔者在用μC/OS—II开发一款车载信息娱乐系统的时候就碰到了这样的问题,因此设计了一种优先级和时间片相结合的调度法(也就是基于μC/0S—II的时间片调度法)。
1 调度原理
这种调度法给处于就绪态的每一个任务都分配一个时间片(优先级越高分配的时间片越长,空闲任务得不到时间片的分配),内核按照任务的优先级依次调度处于就绪态的任务,即当就绪态中最高优先级的任务用完自己的时间片后,CPU控制权转让给就绪态中优先级第二高的任务。该任务用完自己的时间片后,CPU控制权又转让给下一优先级的就绪态任务……当就绪态的每一个任务都被调度一次之后将重新为它们分配时间片,然后又开始新一轮的调度……
其中要注意的是,在调度过程中如果有一个比当前任务优先级更高的任务由其他态变成了就绪态(被创建或获取了一个信号量等),当前任务的CPU控制权将被剥夺;空闲任务仍然是等到其他任务都退出就绪态才获得CPU的使用权。
图1解释了该调度法的调度过程(其中任务1优先级最高,任务2次之,任务3最低)。
①任务2和任务3都处于就绪态,任务1在等待一个信号量,优先级中的任务2获得CPU使用权。
②任务2的时间片用完,优先级低的任务3获得CPU使用权。
③任务3的时间片用完,任务2重新获得CPU的使用权。
④任务2的时间片还没用完时中断来临,中断服务程序获得CPU使用权。
⑤中断服务程序发送了一个任务1等待的信号量,中断服务完成后优先级高的任务1获得CPU使用权。
⑥任务1的时间片用完,任务2继续运行。
⑦任务2的时间片用完,任务3获得CPU使用权。
⑧任务3的时间片用完,重新分配时间片,新一轮调度开始。
2 实现方法
在调度算法的实现过程中,力求做到3点:
①尽可能少地改动μC/0S—II原有的代码;
②增加的代码在风格上保持与原有的相一致;
③兼容原有的优先级调度法(可以很方便地选择优先级调度法或是时间片调度法)。
注:对于该小节中出现的代码,如果是笔者增加的部分都用黑体表示。
2.1 数据结构中增加的变量
在进程控制块中增加两项:
Typedef struct os_tcb{
……
#if OS_TASK_TIME_SLICE-EN>0
/*条件编译,OS_TASK_TIME_SLICE_EN在os_cfg.h中定义,凡是涉及与时间片调度相关的代码都用条件编译。这样,可以通过更改配置文件很方便地选择任务调度法*/
INT16U OSTCBTimeSlice;
/*任务的时间片大小,在任务创建时被初始化,运行过程中保持不变*/
INT16U OSTCBCounter;
/*任务运行剩余时间计数器,每一轮调度开始时该变量被赋值(等于OSTCBTimeSlice),运行过程中不断递减。当其等于0时任务被剥夺CPU使用权*/
#endif
}
由于当前任务的时间片使用完时,该任务将被从就绪表OSRdyGrp以及OSRdyTbl[OS_RDY_TBL_SIZE]中清除;新一轮调度开始时它又必须被恢复,因此笔者在uC0S_II.h文件中增加以下变量(不妨把它们称为“时间片调度表”)分别用于保存OSRdyGrp和OSRdyTbl[OS_RDY_TBL_SIZE]。
OS_EXT INT8U OSTSSGrp;
OS_EXT INT8U OSTSSTbl[OS_RDY_TBL_SIZE];