uCOS-II中就绪表到最高优先级的查表表格解读
接触过uCOS-II的都应该知道,任务调度中的就绪表由两部分组成:OSRdyGrp和OSRdyTbl。在需要进行任务切换时,通过:
y=OSUnMapTbl[OSRdyGrp]; // 获得优先级别的D5,D4,D3位, 先找出是哪一组
x=OSUnMapTbl[OSRdyTbl[y]]; //获得优先级的D2,D1,D0位, 再从哪一组中找出是哪一位
prio = (y<<3) + x; // 获得就绪任务的优先级别
即可以查到当前就绪态的任务中的最高优先级任务。
在此,便有一个疑问:为什么通过查找OSUnMapTbl中的数据,便能间接得到当前就绪态的任务中的最高优先级呢?
首先,来看看OSRdyGrp和OSRdyTbl。
接着,再看看OSUnMapTbl的数据内容:
INT8U const OSUnMapTbl[256]={
0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
.....
};
表格中的数据是如何得到的呢?其实这些数据就是0~255数据字节从低位到高位中(即从左到右)第一个被置1的位的位置。具体过程如下:(大家可以对照源码中OSUnMapTbl 数组)
…OSUnMapTbl[0] = 0
…OSUnMapTbl[1] = 0
…OSUnMapTbl[2] = 1
…OSUnMapTbl[3] = 0
…OSUnMapTbl[4] = 2
…OSUnMapTbl[5] = 0
…OSUnMapTbl[6] = 1
…OSUnMapTbl[7] = 0
…OSUnMapTbl[8] = 3
……
…OSUnMapTbl[15] = 0
…OSUnMapTbl[16] = 4
…OSUnMapTbl[31] = 5
…OSUnMapTbl[47] = 4
…OSUnMapTbl[63] = 6
……
…OSUnMapTbl[240] = 4
……
…OSUnMapTbl[255] = 0
Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 0 0 0
0x00 没有被置1的位,故位置为0
0x01 首个被置1的位置为bit0,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 0 0 1
0x02 首个被置1的位置为bit1,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 0 1 0
0x03 首个被置1的位置为bit0,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 0 1 1
0x04 首个被置1的位置为bit2,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 1 0 0
0x05 首个被置1的位置为bit0,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 1 0 1
0x06 首个被置1的位置为bit1,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 1 1 0
0x07 首个被置1的位置为bit0,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 1 1 1
0x08 首个被置1的位置为bit3,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 1 0 0 0
① 首先将就绪任务组OSRdyGrp 用来在OSUnMapTbl[]中进行查表,找出OSRdyGrp 的位中首次置1位置,确定出当前就绪表中最高优先级是哪一组任务,即优先级的D5,D4,D3位。
② OSUnMapTbl[OSRdyGrp]得到的当前就绪表中最高优先级所在的组属性值y ,再通过y 从就绪表中取出当前最高就绪任务所在就绪组的值OSRdyTbl[y]。
③ 再将OSRdyTbl[y]来在OSUnMapTbl[]中进行查表,找出OSRdyTbl[y]的位中首次置1的位置,确定出当前就绪任务组中最高优先级的到底是哪一个任务x ,即优先级的D2,D1,D0位。
④ 如此就得出了当前就绪表中最高优先级的标识prio = (y<<3) + x 。
UCOS II怎么查找当前就绪表中,优先级最高的任务
uCOS-II为了保证CPU总是执行优先级最高的任务,每当任务状态发生变化时,就需要判断当前任务是否为最高优先级,不是的话就需要进行上下文切换。如何在需要进行任务优先级比较时,快速的将就绪态任务中优先级最高的读出。一种最简单的数据结构就是
定义一个unsigned int类型的变量。变量长度为4字节共32位,如果用1代表任务就绪,用0代表任务未就绪,用相应的位来代表任务优先级,那么这样总共能定义32个任务。
如上图所示,第一行代表该位的取值,第二行代表位,那么总共定义了32个任务,优先级分别为0-31,其中优先级为0,、29、31的任务处于就绪状态。这个说白了就是定义了一个长度为32的bool型数组,下标代表任务的优先级,该下标对应的值便为任务的状态。
如果要读出就绪表中处于就绪状态并且优先级最高的任务,那么执行这条语句:
for(i=0;i<32&&(!(num&(0x01<<i)));i++);
其中num为定义的unsigned int 类型的变量,执行完这条语句之后,i的值就是就绪任务中优先级最高的任务所对应的优先级。这样做确实简单明了,但是定义的任务偏少,而且读出一个特定任务优先级所需要的时间是不定的,就是说上面读出优先级的那条语句的
执行次数是不定的,这显然不能满足实时性系统的要求。
所以就有了uCOS-II的神来之笔,unsigned int类型的变量可以看做是长度为32的bool型数组,如果将这个数组从一维定义到二维,那么定义的任务数量将翻倍增长。如果这个二维数组的列数为8,那么8位二进制数刚好可以用unsigned char来表示,那么定义一个一维
的unsigned char数组就可以表示这种二维的bool型数组,uCOS-II总共可以管理64个任务,那么unsigned char数组的长度为8,便可以定义出64个任务,刚好满足要求。定义完之后的数据结构如下。
假设这个数组为data[],那么在上图中,data[0]=0xA6,data[6]=0X37,在第一行中,优先级为1、2、5、7的任务处于就绪状态,如何快速的读出处于就绪状态且优先级最高的任务,最笨的办法就是遍历这个数组,依次取出数组中的每个元素,每取出一个元素,
读这个元素各个位的值,从低到高,直到找出第一个位为1的那一位。用元素的下标乘8再加上该位的值,就是优先级最高的任务。这种做法显然效率很低,而且执行这个算法的所需要的时间不定。
这个算法效率低下的原因就是把时间浪费在了读取那些未就绪任务的状态,假设就绪态任务中优先级最高为103,那么依次读取前102个任务的状态值浪费了大量的时间,如果能想办法跳过一些前面未就绪的任务,那么算法的效率就大大提升。
如果将所有的任务分组,每八个为一组,总共八组,定义一个unsigned char类型的变量,假设为grp,用这个变量的每一位代表一个任务组。只要这个任务组中八个任务其中有一个处于就绪态,那么就将这个grp变量相应的位置1,如果所有的任务都未就绪,那么
就将这个变量相应的位置0。这样就达到了前面所说跳过一些未就绪任务的想法,在遍历数组查找优先级时,从低到高首先去读grp每一位的值,如果发现某一位值为0,就代表这个组没有就绪态的任务,就跳过这个组。这样遍历data数组时,步长从1变为了8。
解决了前面的问题后,这个算法效率还是有些低,原因在于遍历。假设grp的值为0XA0(1010 0000),遍历的结果是优先级最高的就绪态任务在第五组,for(i=0;i<32&&(!(num&(0x01<<i)));i++);
上面这句代码执行了五次,时间浪费在这里了。如果将代码执行的次数降低到最小,并且是确定的,那么效率将会进一步提升。
任务就绪表
每个任务的就绪态标志放入在就绪表中,就绪表中有两个变量OSRdyGrp和OSRdyTb1[]。
在OSRdyGrp中,任务按优先级分组,8个任务为一组。OSRdyGrp中的每一位表示8组任务中每一组中是否有进入就绪态的任务。任务进入就绪态时,就绪表OSRdyTbl[]中相应元素的相应位也置位。
n 假设优先级为12的任务进入就绪态,12=1100b,则OSRdyTbl[1]的第4位置1,且OSRdyGrp的第1位置位,相应的数学表达式为:
OSRdyTbl[1]|=0x10;
OSRdyGrp|=0x02;
n 而优先级为21的任务就绪态,21=10101b,则OSRdyTbl[2]的第5位置1,且OSRdyGrp的第2位置1 ,相应的数学表达式为:
OSRdyTbl[1]|=0x20;
OSRdyGrp|=0x04;
转自:http://blog.sina.com.cn/s/blog_446cfdc60100ns3f.html