1.任务调度
任务调度,简单的理解就是确定下一个要执行的任务。uC/OS-III中的任务调度通过任务调度器实现。uC/OS-III是一个可剥夺型的、基于优先级的内核。
2.调度点
任务调度,说白了其实就是在某些时刻进行任务的调度,这些调度的时刻称为调度点。
在uC/OS-III中的任务调度点有以下几种:
(1)任务释放信号量或给另一个任务或向另一个任务发消息
(2)任务调用延时函数OSTimeDly()或OSTimeDlyHMSM()
(3)任务等待事件发生而事件还未发生
(4)任务取消等待
(5)创建任务
(6)删除任务
(7)删除一个内核对象
(8)任务改变自身的优先级或者其他任务的优先级
(9)任务通过调用OSTaskSuspend()将自身挂起
(10)任务“解除挂起”某一调用OSTaskSuspend()挂起的任务
(11)退出所有的嵌套中断
(12)通过OSSchedUnlock()给调度器解锁
(13)任务调用OSSchedRoundRobinYield()放弃其执行时间片
(14)用户调用OSSched()
3.时间片轮转调度
这里需要说明一下的是,在uC/OS-III中加入了时间片轮转调度。这里只是解释一下它,具体的调度细节需要参考《嵌入式实时操作系统》P101。
当两个或更多的任务拥有相同的优先级时,uC/OS-III允许一个任务运行一段指定的时间(又叫时间片),然后轮转到下一个任务,这个过程叫做时间片轮转调度。
4.调度实现细节
任务调度通过两个函数实现:OSSched()和OSIntExit()。其中OSSched()是任务级的调度器,而在中断服务程序中必须调用OSIntExit()。
(1)OSSched()函数的源码位于文件os_core.c文件,358行 - 387行
void OSSched (void)
{
CPU_SR_ALLOC(); // 宏定义,声明变量cpu_sr,用来临时存放CPU的状态寄存器的值
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 确认不是在中断服务程序中被调用的
return;
}
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 确认调度器没有上锁
return;
}
CPU_INT_DIS(); // 禁止中断
OSPrioHighRdy = OS_PrioGetHighest(); // 在就绪优先级位映射表中查找就绪态的优先级最高的任务的优先级
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; // 用查找的优先级作为索引,获取最高优先级任务的任务控制块OS_TCB
if (OSTCBHighRdyPtr == OSTCBCurPtr) { // 确保最高优先级的任务不是当前任务
CPU_INT_EN(); // 使能中断
return;
}
#if OS_CFG_TASK_PROFILE_EN > 0u
OSTCBHighRdyPtr->CtxSwCtr++; // Inc. # of context switches to this task
#endif
OSTaskCtxSwCtr++; // Increment context switch counter
OS_TASK_SW(); // 执行任务级的任务切换
CPU_INT_EN(); // 使能中断
}
(2)OSIntExit()函数的代码位于os_core.c文件中,275行 - 315行
void OSIntExit (void)
{
CPU_SR_ALLOC(); // 宏定义,声明变量cpu_sr,用来临时保存CPU的状态寄存器的值
if (OSRunning != OS_STATE_OS_RUNNING) { // 确保OS已启动
return;
}
CPU_INT_DIS(); // 禁止中断
if (OSIntNestingCtr == (OS_NESTING_CTR)0) { // 确保OSIntNestingCtr不会向下溢出
CPU_INT_EN(); // 使能中断
return;
}
OSIntNestingCtr--;
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 确保已退出所有的中断嵌套
CPU_INT_EN(); // 使能中断
return;
}
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 确保调度器未上锁
CPU_INT_EN(); // 使能中断
return;
}
OSPrioHighRdy = OS_PrioGetHighest(); // 在就绪优先级位映射表中查找就绪态的优先级最高的任务的优先级
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; // 用查找的优先级作为索引,获取最高优先级任务的任务控制块OS_TCB
if (OSTCBHighRdyPtr == OSTCBCurPtr) { // 确保最高优先级的任务不是当前任务
CPU_INT_EN(); // 使能中断
return;
}
#if OS_CFG_TASK_PROFILE_EN > 0u
OSTCBHighRdyPtr->CtxSwCtr++; // Inc. # of context switches for this new task
#endif
OSTaskCtxSwCtr++; // Keep track of the total number of ctx switches
OSIntCtxSw(); // 执行中断级的任务切换
CPU_INT_EN(); // 使能中断
}
(3)OS_SchedRoundRobin()函数位于文件os_core.c中,2491行 - 2544行
该函数用来执行时间片轮转调度的。当选择“直接发布(derect post)”模式时,OS_SchedRoundRobin()由OSTimeTick()调用;当选择“延迟发送(deferred post)”模式时,OS_SchedRoundRobin()由OS_IntQTask()调用。
void OS_SchedRoundRobin (OS_RDY_LIST *p_rdy_list)
{
OS_TCB *p_tcb;
CPU_SR_ALLOC(); // 宏定义,定义变量cpu_sr,用来临时保存CPU的状态寄存器的值
if (OSSchedRoundRobinEn != DEF_TRUE) { // 确保时间片轮转调度允许
return;
}
CPU_CRITICAL_ENTER(); // 进入临界区
p_tcb = p_rdy_list->HeadPtr; // 获取当前任务的任务控制块OS_TCB
if (p_tcb == (OS_TCB *)0) { // 确保任务不为空
CPU_CRITICAL_EXIT(); // 退出临界区
return;
}
if (p_tcb == &OSIdleTaskTCB) { // 确保当前任务不是空闲任务
CPU_CRITICAL_EXIT(); // 退出临界区
return;
}
if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { // 递减时间片计数器
p_tcb->TimeQuantaCtr--;
}
if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { // 查看任务的时间片是否使用完
CPU_CRITICAL_EXIT(); // 退出临界区
return;
}
if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) { // 查看当前任务优先级下的就绪任务列表表项的任务数是否大于2
CPU_CRITICAL_EXIT(); // 退出临界区
return;
}
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 查看任务调度器是否上锁
CPU_CRITICAL_EXIT(); // 退出临界区
return;
}
OS_RdyListMoveHeadToTail(p_rdy_list); // 把当前任务的任务控制块OS_TCB移动到该任务就绪表的尾部
p_tcb = p_rdy_list->HeadPtr; // 从任务就绪表的头部获取新任务的任务控制块OS_TCB
if (p_tcb->TimeQuanta == (OS_TICK)0) { // 查看新任务控制块OS_TCB的时间片的长度是否未设置
p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta; // 时间片长度设置成按照默认值
} else {
p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; // 载入新任务的时间长度
}
CPU_CRITICAL_EXIT(); // 退出临界区
}