操作系统最重要的任务之一就是进程调度,它涉及三个主要方面:
- 何时进行调度
- 如何选择下一个活动进程
- 如何实现进程切换
每种操作系统差别很大,本文对uC/OS的进程调度机制进行一些细致分析。
何时进行调度
uC/OS是嵌入式实时操作系统,实时操作系统的重点就是给定事件的响应时间尽可能短。因此任务调度的发生时机比较多,包括:
- 操作系统启动时,会发生调度;
- 高优先级任务建立后,会发生调度;
- 通过调整优先级将一个低优先级任务变为当前最高优先级任务后,该任务会立即执行;
- 主动挂起的高优先级任务被恢复后,会立即执行;
- 调用延时函数的高优先级任务到期后,或被取消延时后,会立即执行;
- 因等待某一事件(信号量、邮箱、事件、队列等)而挂起的高优先级任务,当事件到来时会立即执行;
- 当最高优先级任务等待某个事件而挂起时,会选择当前就绪态的最高优先级任务,该任务立即执行;
- 锁定任务调度功能后,解锁时会发生调度;
- 任务删除时,会发生调度;
具体来说,这几个时机完成调度的细节是:
- 在调用OSStart()启动操作系统时,uC/OS会选择当前的一个最高优先级任务,然后调用OSStartHighRdy()切换到该任务。
- 在调用OSTaskCreate()或OSTaskCreateEx建立一个任务时,在完成TCB的基本初始化后,会调用OS_Sched()进行任务切换。
- 在调用OSTaskChangePrio()改变一个任务优先级时,在完成优先级变化后,符合一定条件下会调用OS_Sched()进行任务切换。
- 在某个任务调用OSTaskResume()恢复某个任务的执行时,符合一定条件下会调用OS_Sched()进行任务切换。
- 延时函数的正常到期是由定时中断触发的,在中断的恢复阶段,会调用OSIntCtxSw()切换到最高优先级任务。延时函数的非正常到期是由OSTimeDlyResume()的调用触发的,在其中会主动调用OS_Sched()进行任务切换。
- 在各种信号量、邮箱、事件和队列等的Post、PendAbort和Del函数中,都会调用OS_Sched()进行任务切换。
- 在各种信号量、邮箱、事件和队列等的Pend函数中,若当前任务需挂起,会调用OS_Sched()进行任务切换。
- 在OSSchedUnlock()函数解锁任务调度后,若任务调度已经使能,则会调用OS_Sched()进行任务切换。
- 在调用OSTaskDel()删除某个任务时,会调用OS_Sched()进行任务切换。
可见调度时机总结成一句话就是:满足调度条件,就毫不犹豫立即调度,绝不会等待和迟疑。从这里可以体现出uC/OS作为实时操作系统的重要特点。
如何选择下一个活动进程
uC/OS是实时操作系统,它划分了64个优先级(uC/OS-II),每个任务的优先级均不同,在发生调度时,永远都会选择处于就绪态的最高优先级的任务。
为了在有限时间内快速选择出一个就绪任务,uC/OS-II建立了两级就绪表,每个任务(即每个优先级)在一级就绪表OSRdyTbl中占一个比特,每8个比特又组成成一位形成OSRdyGrp。这样先从OSrdyGrp中找到最高优先级任务所在组,再从OSRdyTbl的该组找到该任务。
为了加速,又预先准备了一张优先级判定表OSUnMapTbl,从该表中可以一步直接寻址到最高优先级所在的比特。
================ os_core.c 38 55 ==================
INT8U const OSUnMapTbl[256] = {
0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u,