优先级位图法
1 本次算法涉及到的参数变量:
OSTCBX: 主要存放优先级行数
OSTCBY: 主要存放优先级列数
OSTCBBitX: 主要存放优先级行位数(n行就n行为1)
OSTCBBitY: 主要存放优先级列位数(n列就n列为1)
例:OSTCBX=5,表示第5行,然后用OSTCBBitX表示行位数:00100000
该图为不同优先级下对应的行列数 x,y。
2 如何从优先级得到行列数呢?
首先我们可以知道,我们的优先级设定是在63以内的。以优先级15为例。如何得到行列数?
优先级 15,其二进制:00001111。这里需要说明一点,优先级是0~63,因此在任意情况下的优先级化成二进制都是6位, 即00xxxxxx
- 所以我们只需操作优先级的低六位即可。在低六位的情况下对高三位,低三位进行操作
15 >> 00001111 >> 001111 >> 001 111 - 优先级15,在上图所示为第1行,第7列
- 那001 111 刚好就对应对应起来了。因此001为行,111为列。其代码的编写就是
ptcb->OSTCBY = (INT8U)(prio >> 3u); //左移三位,00001111变成00000001,即1行
ptcb->OSTCBX = (INT8U)(prio & 0x07u);//和00000111相与,00001111变成00000111,即7列
ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);//1左移OSTCBY得到00000010
ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);//1左移OSTCBX得到10000000
这里解释一下(1uL << ptcb->OSTCBY)与OSMAPTbl[ptcb->OSTCBY]功能是一样的,我这边的工程里面没有设立OSMAPTbl[]数组。
3 既然明白了任务优先级与行列的关系,那该如何将优先级存入行列中?
这里用到两个参数
OSRdyGrp :代表行位数,n行n位
OSRdyTbl[]:列位数数组n列n位,数组里存放的是列位数
行列存放在这两个全局变量里面:
OSRdyGrp |= ptcb->OSTCBBitY; //OSRdyGrp直接等于行位数(行位数不等于行数)
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; //在对应的行数数组内存放列位数
行位数与行数的区别:第3行,行位数即00001000
那现在已经将行位数与列位数都存放到全局变量中了。那我们来举例看看你弄懂了吗?
优先级:23 二进制表示:00 010 111
1. ptcb->OSTCBY = (INT8U)(prio >> 3u) =00000010 = 2行
ptcb->OSTCBX = (INT8U)(prio & 0x07u)=00000111= 7列
ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY)= 00000100行位数
ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX)=10000000列位数
2. OSRdyGrp |= ptcb->OSTCBBitY=00000100行位数对应2行
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX= OSRdyTbl[2]=10000000列位数对应7列
再次声明行数与行位数的关系 n行n位数,3行,那行位数=00001000
请注意:ptcb为当前任务的结构体指针
总结一下:
①OSRdyGrp 与OSRdyTbl[ptcb->OSTCBY]存放的是行列的位数
②OSRdyTbl[ptcb->OSTCBY]= ptcb->OSTCBBitX,表示在第n行下存了列位数,例如OSRdyTbl[3]=00001000,该任务下第三行存放列数位3列
4 探究一下,存放行列数之后如何取出?
这里会涉及到ucos系统的设计原理。
- 首先UCOS会在创建任务时将优先级,栈顶地址,等等参数放置在某结构体指针对应的OS_TCB的变量里,同时该结构体指针是OSTCBFreeList链表中的一员。这个任务就对于了该结构体指针。可以这样理解:任务—结构体指针—指针指向的参数
- 当在我们在写ucos时,会创建多个任务,那这些任务的结构体指针全部按顺序排列会在链表里。同时这些结构体指针会指向对应的OSTCB参数,那这些OSTCB参数就包括优先级,栈地址。即不同的任务有不同的结构体指针,不同的结构体指针就对应着不同的优先级,栈地址等等参数。
任务1—结构体指针1—指针指向的参数
任务2—结构体指针2—指针指向的参数
- 当我们要做任务切换时,只要这些链表里面最高的优先级对应的任务结构体指针就行了,把这个结构体指针放到就绪列表,就做出了任务切换。
进入正题:如何找出优先级?
① 已知我们之前将优先级通过
OSTCBX:
OSTCBY:
OSTCBBitX:
OSTCBBitY:
注意:这四个参数是存放在结构体指针对应的参数里面的,不是全局变量,因为每个任务的优先级不同,所以存放的值就不同。
上面这四个参数将优先级存放在OSRdyGrp,OSRdyTbl[]中(这两个参数为全局变量)因为方便之后查找最高优先级。
②通过OSRdyGrp,OSRdyTbl[]查找。
下面的数组是个解码数组,将OSRdyGrp,OSRdyTbl[]的值转化优先级(十进制)
INT8U const OSUnMapTbl[256] = {
0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F */
6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F */
7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF */
6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u /* 0xF0 to 0xFF */
};
优先级prio通过什么方法转到OSRdyGrp,OSRdyTbl[]。那我们就逆着这个方法转换回去。
就以优先级23为例, 二进制表示:00 010 111
OSRdyGrp |= ptcb->OSTCBBitY=00000100行位数对应2行
OSRdyTbl[ptcb->OSTCBY] | = ptcb->OSTCBBitX= OSRdyTbl[2]=10000000列位数对应7列
那OSRdyGrp=00000100=4
通过解码数据解码
y = OSUnMapTbl[OSRdyGrp]=OSUnMapTbl[00000100]=OSUnMapTbl[4]=2表示第2行;
OSPrioHighRdy = (INT8U)((y<<3u)+OSUnMapTbl[OSRdyTbl[y]])=(INT8U)(2<<3u)+OSUnMapTbl[OSRdyTbl[2]]=0x00000010<<3+OSUnMapTbl[10000000]=00010000+7=16+7=23;
注意:OSRdyTbl[2]=10000000
那结果出来了,按照原来的方法逆回去就能得到优先级。再通过优先级找到任务的结构体指针就能实现切换任务操作。
最后一个问题,如果我们同时定义了多个任务,那如何从中找出最高优先级?
其实源码的编写者写的很巧妙。那就是或运算符。
已知OSRdyGrp,OSRdyTbl[]是全局变量,那么或运算就合理了,每当新建一个任务时都会产生一个优先级,这个优先级按照优先级位图法,利用与运算写入OSRdyGrp,OSRdyTbl[]中,那这两个变量里面就包含了很多优先级。
举个例子:优先级:23,对应OSRdyGrp等于00000100;
在此之后我再新建几个任务,这几个任务的优先级分别是9,13,目前存在三个任务其优先级为9,13,23。那按照位图法OSRdyGrp等于00000110;这里面第2位是23的行位数,第一位是9,13的行位数。之后我们逆着方法通过UnMapTbl[]就能从OSRdyGrp,OSRdyTbl[]找到最高优先级。