uc/OS-II在RAM中设立了一个记录表,系统中的每个任务在表中占一个位置,并用这个位置的状态(0和1)来表示任务是否处于就绪状态。这个表叫做任务就绪状态表。
就绪任务表实际上是一个类型为INT8U的数组OSRdyTbl[]。其中每个字节的每个位表示一个任务,这样一个数组元素就可以表示8个任务的就绪状态。以任务优先级别高低为顺序,比如OSRdyTbl[0]的第1位表示任务优先级0,OSRdyTbl[1]的第8位表示任务优先级15.当其位为1时任务处于就绪状态,0则为非就绪状态。
我们可以把数组元素的8个任务看成是一个任务组,为了便于就绪表的查找,uc/OS-II又定义了一个数据类型为INT8U的变量OSRdyGrp,并使该变量的每一个位对应OSRdyTbl[]的一个任务组(即数组的一个元素)。如果某任务组中有任务就绪,则在变量OSRdyGrp里把该任务组对应的位置置1,否则置为0。比如OSRdyGrp=0x00000011,则表示OSRdyTbl[0],OSRdyTbl[1]任务组中有任务就绪。
由于变量OSRdyGrp为8位,OSRdyTbl[]数组元素也有8位,所以算下来uc/OS-II就可以管理8x8=64个任务。下面是一张任务就绪表:
如何根据任务优先级别找到任务在就绪表中的位置。uc/os是这样设定的,由于优先级别是一个单字节的数字,而且其最大值不会超过63,即二进制形式的00111111,因此可把优先级别看成是一个6位的二进制数。这样就可以用高3位来指明变量OSRdyGrp的具体数据位,并用来确定就绪表数组元素的下表,即上图中的Y。用低3位来指明该数组元素的具体数据位,即上图的X。于是根据任务的优先级别就可以确定该任务在就绪表中的确切位置了。另外,uc/OS-II在初始化时创建任务就绪表,并把其中所有数据位都置位0。
就绪表的操作有两项:一是把应就绪的任务在就绪表中进行登记,二是把任务从就绪表中删除。
1.在就绪表中登记就绪任务
使用下面两行代码就可以把优先级别为prio的任务置位就绪状态。
OSRdyGrp |= OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07];
这就是根据上面的分析写出的代码。
其中OSMapTblp[]是uc/OS-II为加快运算速度定义的一个数组,数组如下:
INT8U const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
2.从就绪表中删除任务
优先级别为prio,代码如下
if((OSRdyTbl[prio>>3] &= ~OSMapTbl[prio&0x07]) == 0)
OSRdyGrp &= ~OSMapTbl[prio>>3];
其中如果OSRdyTbl[]数组元素每位为0的话,则相应OSRdyGrp的其中二进制位也要清零。
3.从就绪表中获取优先级别最高的就绪任务
其操作代码如下:
y = OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
prio = (y<<3) + x;
还有一种代码:
y = OSUnMapTbl[OSRdyGrp];
prio = (INT8U)((y<<3) + OSUnMapTbl[OSRdyTbl[y]]);
通过以上代码就可以找出最高优先级就绪任务的优先级别,其中OSUnMapTbl[]与OSMapTblp[]作用类似,是uc/OS-II为加快运算速度定义的一个数组,一共有256个元素。其数组如下:
INT8U const OSUnMapTbl[] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */
};
上面这个数组大有意思啊。刚开始我也好奇这是怎么来的,看了书然后仔细想想就想通了,不得不感叹写源码的人的高明啊。
解释如下,由上面的代码看出,使用数组OSUnMapTbl[]时是以OSRdyGrp为下标的,因此这个数组一共有256个元素。也就是说,无论OSRdyGrp值为多少,在数组OSUnMapTbl[]总能找到对应的元素值,而且这个元素值就是最高就绪任务优先级别的y。因为数组OSUnMapTbl[]各元素的值是基于这样一个思想来设置的:表示任务组的变量OSRdyGrp是一个8位二进制数,从这个数的最低位向高位查找,碰到的第一个为1的位所对应的就绪任务组一定是最高优先级别就绪任务所在的组,所以它的组号一定是最高优先级别就绪任务的级别(6位数)的高3位。比如OSRdyGrp中第一个为1的位是第3位,那么最高优先级别就绪任务级别的高3位一定是011,于是在数组OSUnMapTbl[]的256个元素值中,凡是其下标值的第3位为1的元素值均定义为3.
所以有了这样一个数组,在查找最高级就绪任务时,只要以变量OSRdyGrp为下标,就可直接在在数组OSUnMapTbl[]得到就绪任务的y值了;否则,就要编写一个循环程序在就绪表中进行查找,这样不但耗时,而且犯了实时系统的大忌——运算时间不可预测。
当然此数组也用来查找最高就绪任务的x值。当然是以OSRdyTbl[y]为下表来查找的。这个跟超找y坐标类似。比如OSRdyGrp为0x28(00101000),最高优先组在OSRdyTbl[3],而OSRdyTbl[3]的元素为01100000,则第一个为1的位是我们要找的最高优先级别的就绪任务。位5为1,则说明了x坐标为5,即最高任务优先级别的低3位肯定为5。于是在数组OSUnMapTbl[]的256个元素中,凡是其下标为5的位为1的元素值均定义为5.
uc/OS-II经常使用这种类似于就绪表形式的表来记录任务的某种状态。这算不算一种算法呢?