uCOS-II学习笔记之就绪表

时间:2021-08-23 19:31:14

uCOS-II为了保证CPU总是执行优先级最高的任务,每当任务状态发生变化时,就需要判断当前任务是否为最高优先级,不是的话就需要进行上下文切换。如何在需要进行任务优先级比较时,快速的将就绪态任务中优先级最高的读出。一种最简单的数据结构就是定义一个unsigned int类型的变量。变量长度为4字节共32位,如果用1代表任务就绪,用0代表任务未就绪,用相应的位来代表任务优先级,那么这样总共能定义32个任务。

uCOS-II学习笔记之就绪表

uCOS-II学习笔记之就绪表

如上图所示,第一行代表该位的取值,第二行代表位,那么总共定义了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个任务,刚好满足要求。定义完之后的数据结构如下。

uCOS-II学习笔记之就绪表

uCOS-II学习笔记之就绪表

假设这个数组为data[],那么在上图中,data[0]=0xA6,data[6]=0X37,在第一行中,优先级为1、2、5、7的任务处于就绪状态,如何快速的读出处于就绪状态且优先级最高的任务,最笨的办法就是遍历这个数组,依次取出数组中的每个元素,每取出一个元素,读这个元素各个位的值,从低到高,直到找出第一个位为1的那一位。用元素的下标乘8再加上该位的值,就是优先级最高的任务。这种做法显然效率很低,而且执行这个算法的所需要的时间不定。

这个算法效率低下的原因就是把时间浪费在了读取那些未就绪任务的状态,假设就绪态任务中优先级最高为103,那么依次读取前102个任务的状态值浪费了大量的时间,如果能想办法跳过一些前面未就绪的任务,那么算法的效率就大大提升。如果将所有的任务分组,每八个为一组,总共八组,定义一个unsigned char类型的变量,假设为grp,用这个变量的每一位代表一个任务组。只要这个任务组中八个任务其中有一个处于就绪态,那么就将这个变量相应的位置1,如果所有的任务都未就绪,那么就将这个变量相应的位置0。这样就达到了前面所说跳过一些未就绪任务的想法,在遍历数组查找优先级时,从低到高首先去读grp每一位的值,如果发现某一位值为0,就代表这个组没有就绪态的任务,就跳过这个组。这样遍历data数组时,步长从1变为了8。

解决了前面的问题后,这个算法效率还是有些低,原因在于遍历。假设grp的值为0XA0(1010 0000),遍历的结果是优先级最高的就绪态任务在第五组,

for(i=0;i<32&&(!(num&(0x01<<i)));i++);

上面这句代码执行了五次,时间浪费在这里了。如果将代码执行的次数降低到最小,并且是确定的,那么效率将会进一步提升。如果定义一个长度为256的unsigned char数组(假设为indexOf1),将grp的值作为下标,该下标处元素的值为该下标值从低到高第一次出现1的位的位置。举例来说,0XA0(1010 0000)从低到高第一次出现1的位的位置为5,那么indexOf1[grp]=5。同理indexOf1[0]=0,indexOf1[1]=0,indexOf1[2]=1,... 于是在uCOS-II源码中定义了这样一个数组

uCOS-II学习笔记之就绪表

uCOS-II学习笔记之就绪表

这样就不用执行前面所述的那句代码,直接通过下标在数组中查找即可,代码执行的次数降到了最低而且次数是确定的。

那么总结一下查找最高优先级的算法,首先将grp作为下标,在indexOf1数组中查找grp第一次出现1的位的位置(假设为y),旨在跳过前面未就绪的任务组。以y为下标,取出就绪表data[]数组中对应元素,继续以该元素为下标,在indexO1数组中查找该元素第一次出现1的位的位置(假设为x),旨在跳过同组中前面未就绪的任务。这样总共跳过的未就绪的任务个数为 y组+x 个。每组的任务为8,那么跳过的任务数就是y*8+3,那么就绪任务中优先级最高的就是该值。以下面的这张就绪表为例:

uCOS-II学习笔记之就绪表

uCOS-II学习笔记之就绪表

grp的值为0XE0(1110 0000),表示0-4组没有就绪的任务,5-7有就绪的任务。首先以grp为下标,indexOf1[grp]=5,取出就绪表中data[5],值为0X98(1001 1000),以0X98为下标,在indexOf1中取出indexOf1[0X98]=3,那么总共跳过了5组+3个共43个任务,那么就绪任务中优先级最高的就是43。

在uCOS-II源码中,grp定义为OSRdyGrp,就绪表data[]定义为OSRdyTbl[OS_LOWST_PRIO/8+1],indexOf1[256]定义为OSUnMapTbl[256],

查找代码为:

y      = OSUnMapTbl[OSRdyGrp]; 
x = OSUnMapTbl[OSRdyTbl[y]];
prio = (y << 3) + x;