μC/OS-II的内核结构
读者可以看出,任务优先级的低三位用于确定任务在总就绪表OSRdyTbl[]中的所在位。接下去的三位用于确定是在OSRdyTbl[]数组的第几个元素。OSMapTbl[]是在ROM中的(见文件OS_CORE.C)屏蔽字,用于限制OSRdyTbl[]数组的元素下标在0到7之间,见表3.1
本文引用地址:https://www.eepw.com.cn/article/201610/305744.htm
图3.3μC/OS-Ⅱ就绪表
如果一个任务被删除了,则用程序清单3.6中的代码做求反处理。
程序清单L3.6从就绪表中删除一个任务
if((OSRdyTbl[prio>>3]=~OSMapTbl[prio0x07])==0)
OSRdyGrp=~OSMapTbl[prio>>3];
以上代码将就绪任务表数组OSRdyTbl[]中相应元素的相应位清零,而对于OSRdyGrp,
只有当被删除任务所在任务组中全组任务一个都没有进入就绪态时,才将相应位清零。也
就是说OSRdyTbl[prio>>3]所有的位都是零时,OSRdyGrp的相应位才清零。为了找到那个
进入就绪态的优先级最高的任务,并不需要从OSRdyTbl[0]开始扫描整个就绪任务表,只
需要查另外一张表,即优先级判定表OSUnMapTbl([256])(见文件 OS_CORE.C)。OSRdyTbl[]
中每个字节的8位代表这一组的8个任务哪些进入就绪态了,低位的优先级高于高位。利用
这个字节为下标来查OSUnMapTbl这张表,返回的字节就是该组任务中就绪态任务中优先级
最高的那个任务所在的位置。这个返回值在0到7之间。确定进入就绪态的优先级最高的任
务是用以下代码完成的,如程序清单L3.7所示。
程序清单 L3.7 找出进入就绪态的优先级最高的任务
y=OSUnMapTbl[OSRdyGrp];
x=OSUnMapTbl[OSRdyTbl[y]];
prio=(y3)+x;
例如,如果OSRdyGrp的值为二进制01101000,查OSUnMapTbl[OSRdyGrp]得到的值是
3,它相应于OSRdyGrp中的第3位bit3,这里假设最右边的一位是第0位bit0。类似地,
如果OSRdyTbl[3]的值是二进制11100100,则OSUnMapTbl[OSRdyTbc[3]]的值是2,即第2
位。于是任务的优先级Prio就等于26(3*8+2)。利用这个优先级的值。查任务控制块优
先级表OSTCBPrioTbl[],得到指向相应任务的任务控制块OS_TCB的工作就完成了。
3.5 任务调度(TaskScheduling)
μC/OS-Ⅱ总是运行进入就绪态任务中优先级最高的那一个。确定哪个任务优先级最
高,下面该哪个任务运行了的工作是由调度器(Scheduler)完成的。任务级的调度是由函
数OSSched()完成的。中断级的调度是由另一个函数OSIntExt()完成的,这个函数将在以
后描述。OSSched()的代码如程序清单L3.8所示。
程序清单L3.8任务调度器(theTaskScheduler)
voidOSSched(void)
{
INT8Uy;
OS_ENTER_CRITICAL();
if((OSLockNesting|OSIntNesting)==0){(1)
y=OSUnMapTbl[OSRdyGrp];(2)
OSPrioHighRdy=(INT8U)((y3)+OSUnMapTbl[OSRdyTbl[y]]);(2)
if(OSPrioHighRdy!=OSPrioCur){(3)
OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];(4)
OSCtxSwCtr++;(5)
OS_TASK_SW();(6)
}
}
OS_EXIT_CRITICAL();
}
μC/OS-Ⅱ任务调度所花的时间是常数,与应用程序中建立的任务数无关。如程序清单
中[L3.8(1)]条件语句的条件不满足,任务调度函数OSSched()将退出,不做任务调度。这
个条件是:如果在中断服务子程序中调用OSSched(),此时中断嵌套层数
OSIntNesting>0,或者由于用户至少调用了一次给任务调度上锁函数OSSchedLock(),使
OSLockNesting>0。如果不是在中断服务子程序调用OSSched(),并且任务调度是允许的,
即没有上锁,则任务调度函数将找出那个进入就绪态且优先级最高的任务[L3.8(2)],进入
就绪态的任务在就绪任务表中有相应的位置位。一旦找到那个优先级最高的任务,
OSSched()检验这个优先级最高的任务是不是当前正在运行的任务,以此来避免不必要的任
务调度[L3.8(3)]。注意,在μC/OS中曾经是先得到OSTCBHighRdy然后和OSTCBCur做比
较。因为这个比较是两个指针型变量的比较,在8位和一些16位微处理器中这种比较相对
较慢。而在μC/OS-Ⅱ中是两个整数的比较。并且,除非用户实际需要做任务切换,在查任
务控制块优先级表OSTCBPrioTbl[]时,不需要用指针变量来查OSTCBHighRdy。综合这两项
改进,即用整数比较代替指针的比较和当需要任务切换时再查表,使得μC/OS-Ⅱ比μC/OS
在8位和一些16位微处理器上要更快一些。
为实现任务切换,OSTCBHighRdy必须指向优先级最高的那个任务控制块OS_TCB,这是
通过将以OSPrioHighRdy为下标的OSTCBPrioTbl[]数组中的那个元素赋给OSTCBHighRdy来
实现的[L3.8(4)]。接着,统计计数器OSCtxSwCtr加1,以跟踪任务切换次数[L3.8(5)]。
最后宏调用OS_TASK_SW()来完成实际上的任务切换[L3.8(6)]。
任务切换很简单,由以下两步完成,将被挂起任务的微处理器寄存器推入堆栈,然后
将较高优先级的任务的寄存器值从栈中恢复到寄存器中。在μC/OS-Ⅱ中,就绪任务的栈结
构总是看起来跟刚刚发生过中断一样,所有微处理器的寄存器都保存在栈中。换句话说,
μC/OS-Ⅱ运行就绪态的任务所要做的一切,只是恢复所有的CPU寄存器并运行中断返回指
令。为了做任务切换,运行OS_TASK_SW(),人为模仿了一次中断。多数微处理器有软中断
指令或者陷阱指令TRAP来实现上述操作。中断服务子程序或陷阱处理(Traphardler),
也称作事故处理(exceptionhandler),必须提供中断向量给汇编语言函数OSCtxSw()。
OSCtxSw()除了需要OS_TCBHighRdy指向即将被挂起的任务,还需要让当前任务控制块
OSTCBCur指向即将被挂起的任务,参见第8章,移植μC/OS-Ⅱ,有关于OSCtxSw()的更详
评论