1.任务控制块TCB
2.调度算法
优先级列表OSPrioTbl[OS_PRIO_TBL_SIZE]:如图,每位代表一个优先级,被置“1”表示此优先级对应的任务就绪了。找出优先级最高的就绪任务就是找出第一个被置1的位所在的位置。
就绪表OSRdyLis[OS_CFG_PRIO_MAX]:此数组下标表示任务优先级,其内容是一个结构体;如图:
同一优先级且已经就绪的任务通过其TCB的NextPtr与PrevPtr域链接为一个链表,上述的结构体用TailPtr指向链表的尾(链表中最后一个TCB),HeadPtr指向链表的头。OSRdyLis的下标则对应任务的优先级。比如OSRdyLis[3]中headPtr指向所有就绪的且优先级为3的TCB组成的链表的头。
OSSched 、OSIntExit引发任务调度。OSSched是任务级调度;OSIntExit是中断级任务调度;
OSSched 及OSIntExit调度过程:首先查找优先级列表OSPrioTbl中最高优先级(假设为5),接着找到就绪表中OSRdyLis[5].HeadPtr指向的TCB,如果此TCB指针与当前运行的任务TCB不同,则通过上下文切换,转到此TCB对应的任务,完成调度。任务调度时总是运行就绪表中指向TCB链表头。所以时间片轮转就是把就绪表中TCB链表的头移到尾(为嘛不用循环队列?),然后调用OSSched完成。轮转借用了TCB中TimeQuanta、TimeQuantaCtr来判断时间片是否到期。
3.上下文切换
OSSched流程:
(1) 判断是否允许调度(是否在中断,是否锁调度器等)
(2) 进入临界区,关中断(需要保存当前cup中断状态)
(3) 获得当前就绪任务中最高的优先级,再获得此优先级对应任务TCB指针
(4) 当此TCB与当前正在运行的任务相同则不调度,返回(先恢复cup中断状态)
(5) 两者不相同则调用OS_TASK_SW宏,触发可挂起系统调用(PendSV)中断(或同类型中断)
(6) 开中断,跳转到中断OS_CPU_PendSVHandler
a. 关中断
b. 获得当前任务SP
c. 保存未自动入栈的CPU寄存器
d. 当前任务SP存入堆栈
e. 调用OSTaskSwHook钩子函数
f. 获得就绪任务中优先级最高并赋值到当前优先级(OSPrioCur = OSPrioHighRdy)
g. 获得对应的任务(OSTCBCurPtr = OSTCBHighRdyPtr)
h. 从新任务TCB中得到SP地址
i. 从新任务堆栈中恢复不自动保存的寄存器(与存入顺序一致)
j. 中断返回(弹出自动保存的CPU寄存器)
(7) 执行新任务
OSIntExit是中断级的任务切换,与任务级切换差不多,要考虑本身已经处在中断了(当前任务的cpu寄存器已经保存)。CortexM3 移植例子直接触发任务级切换用的OS_CPU_PendSVHandler,因为PendSV的优先级最低,所以虽然被触发但必须等到其他中断退出后才进入PendSV完成调度。OSIntExit使用OSIntCtxSw宏实现切换。
4. 任务挂起状态
在对象被提交时会检查对应挂起队列并把挂起队列中的任务置于就绪态。
等待对象超时检测是在系统任务OSTimeTick中完成(在等待对象时设置了超时时间,则等待对象的任务会被加入到时基列表OSCfg_TickWheel[])。