ucos任务调度

就绪表及优先级相关计算

任务就绪表由2个变量表示

OS_EXT  INT8U             OSRdyGrp;                        /* Ready list group                         */

OS_EXT  INT8U             OSRdyTbl[OS_RDY_TBL_SIZE];       /* Table of tasks which are ready to run    */

OSRdyGrp:共8bits,用于分组标志

OSRdyTbl[]:共8个字节,字节的每位表示任务的组内标志(每个字节表示一组)。

在OSRdyGrp中,任务按优先级分组,8个任务为一组。OSRdyGrp中的每一位表示8组任务中每一组中是否有进入就绪态的任务。任务进入就绪态时,就绪表OSRdyTbl[]中的相应元素的相应位也置位。就绪表OSRdyTbl[]数组的大小取决于OS_LOWEST_PR1O(见文件OS_CFG.H)。

任务优先级0-63,可用6bits表示。将其分为两部分:高3bits和低3bits。

其中,高3位用于表示该任务所在的组;低3位用于表示该任务组内的位置。

程序清单 L3.5 使任务进入就绪态

OSRdyGrp            |= OSMapTbl[prio >> 3];//组标志置位

OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];//组内标志置位

(prio>>3的值为任务所在的组,prio&0x07的值表示任务所在的位)


先看看 prio>>3 为什么要右移三位。
UCOS-II最多支持64个优先级,即从0到63。最低的优先级为63 转换成二进制:0x00111111
刚好占6位,那么可以把6为划分为高三位和低三位,就拿上面的优先级为31任务的例子来说:
31取其高三位 31>>3 = 3 ,看,是不是刚好表示OSRdyGrp的第3位?也就是说在OSRdyTbl[3]。
31取其低三位 31&0x07 = 7,看,是不是刚好表示OSRdyTbl的第7位?也就是说在OSRdyTbl[3]的第7位。
两个组合起来:31的优先级在OSRdyTbl的第3个元素中的第7位。
这就是为什么prio >> 3 和 prio & 0x07了 。


得到之后如何进一步得到OSRdyGrp的值和OSRdyTbl中第3个元素的值呢?
从上图来看,OSRdyGrp的第三位置1 所以OSRdyGrp = 0b0000 1000 = 0x08  而OSRdyTbl[3]的第七位被置1 所以等于 OSRdyTbl[3] = 0b1000 0000 = 0x80 。
那么我们继续分析它的算法,剩下的算法很简单啦,就是将得到的值和OSMapTbl表对应起来。典型的用空间换时间的方法。
OSRdyGrp[31>>3] = 0x08,OSRdyGrp[31&0x07] = 0x80
刚好和我们上图看的结果一样。那既然得到了 OSRdyGrp的值和OSRdyTbl[3]那为什么不直接复制过去呢,而是要与其相| (或),这是因为OSRdyGrp的每一位记录着每一个优先级组是否有任务就绪的状态,而OSRdyTbl的每一位都记录着每个优先级的就绪状态。如果直接赋值,则会覆盖所有的状态位,所以要用 或运算 来合并,这样就不会影响其他位的状态了。

其中OSRdyGrp、OSRdyTbl和OSMapTbl的表示如下图。

uCOS-Ⅱ中的就绪列表使用bitmap的方式进行管理,就绪时对应的bit位置1。

ucos任务调度

优先级prio的高3位作为组号,低3位作为组内的编号。bitmap的形象如上图所示。


假设优先级31的任务第一个加入了就绪任务表,此时OSRdyGrp和OSRdyTbl的情况:

 ucos任务调度


OSRdyGrp的第3位为1,表示第3优先级组有就绪任务。
OSRdyTbl的第7位为1,表示第31优先级的任务被就绪。
此时OSRdyGrp的其他位为零,OSRdyTbl的其他元素中的位都为零


图3.3μC/OS-Ⅱ就绪表

表 T3.1 OSMapTbl[]={0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

 

Index

Bit Mask (Binary)

0

00000001

1

00000010

2

00000100

3

00001000

4

00010000

5

00100000

6

01000000

7

10000000

 

程序清单 L3.6 从就绪表中删除一个任务

if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0)

    OSRdyGrp &= ~OSMapTbl[prio >> 3];

以上代码将就绪任务表数组OSRdyTbl[]中相应元素的相应位清零,而对于OSRdyGrp,只有当被删除任务所在任务组中全组任务一个都没有进入就绪态时,才将相应位清零。也就是说OSRdyTbl[prio>>3]所有的位都是零时,OSRdyGrp的相应位才清零。

 

程序清单 L3.7 找出进入就绪态的优先级最高的任务

y    = OSUnMapTbl[OSRdyGrp];//优先级最高的所在的组

x    = OSUnMapTbl[OSRdyTbl[y]];//组内优先级最高的位

prio = (y << 3) + x;//还原成优先级

为了找到那个进入就绪态的优先级最高的任务,并不需要从OSRdyTbl[0]开始扫描整个就绪任务表,只需要查另外一张表,即优先级判定表OSUnMapTbl([256])(见文件OS_CORE.C)。OSRdyTbl[]中每个字节的8位代表这一组的8个任务哪些进入就绪态了,低位的优先级高于高位。利用这个字节为下标来查OSUnMapTbl这张表,返回的字节就是该组任务中就绪态任务中优先级最高的那个任务所在的位置。这个返回值在0到7之间。

OSUnMapTbl中的每个元素表示0x000xFF的每个数的二进制表示中最低位1出现的位置