就绪表和就绪组
ucosii内核在进行任务调度的时候,必须查找就绪任务中最高优先级的任务。
ucosii采用的就绪表和就绪组的位图算法。该方法无论有多少就绪任务,每次查找到就绪任务的时间是确定的。
ucosii的具体方法是定义了两张位图表(以下代码都在ucosii.h中)
#define OS_LOWEST_PRIO 63 (os_cfg.h)
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 8u + 1u)
typedef INT8U OS_PRIO;
OS_PRIO OSRdyGrp;
OS_PRIO OSRdyTbl[OS_RDY_TBL_SIZE];
将0-63优先级分为8组,优先级0-7分为0组,依此类推。
OSRdyGrp是8位的数据,从0到7位分别代表着0-7组优先级任务的状态。若是某位对应的组中有任务就绪,那么该为就置为1,否则置为0。
OSRdyTbl[OS_RDY_TBL_SIZE] 中的数组的下表对应于任务的组,数组的元素有8位,每一位对应于该组中优先级任务的状态,若是该任务就绪就置为1,否则置为0。
OS_RDY_TBL_SIZE是根据我们配置的任务数量(OS_LOWEST_PRIO )来确定的,若我们可以确定系统中
例如:优先级为5的任务就绪 则该任务在0组。若该优先级进入到就绪状态中,则将OSRdyGrp 的0位置为1,同时将数组元素OSRdyTbl[0]中的5位置1。
若从该任务从就绪任务中移除,则先检查0组中是否还有其他任务处于就绪状态,若是没有,则OSRdyGrp的0位置0,否则该位保持1不变。同时将OSRdyTbl[0]中的5位置0。
任务就绪,就绪表和就绪组的更新,源码如下所示:
OSRdyGrp|= ptcb->OSTCBBitY; (task.c OS_TCBInit( ) )
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OSTCBBitX,OSTCBBitY在之的任务控制块(TCB)前中有提到过,其计算方式为:
ptcb->OSTCBY = (INT8U)(prio >> 3u); //右移3位,相当于除8,结果就是该优先级对应于哪一个组
ptcb->OSTCBX = (INT8U)(prio & 0x07u); //保留该数据的最低3位,相当于除8的余数,也就是在该优先级在所属优先级
组中的具体位置
ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY); //对应于OSTCBY位置的位置为1,其余位为0,用于在之后更新就绪表和就绪数组时候用
ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);//对应于OSTCBX位置的位置为1,其余位为0,用于在之后更新就绪表时候用
OSRdyGrp|= ptcb->OSTCBBitY; //通过OR运算,将对应组的位置为1
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;//通过OR运算,将该优先级在数组中对应位置的位置为1。
通过就绪表和就绪组中查找就绪任务最高优先级,源码如下:
y = OSUnMapTbl[OSRdyGrp]; (os_core.c OS_SchedNew())
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
y = OSUnMapTbl[OSRdyGrp];
通过数组下标OSRdyGrp查找OSUnMapTbl优先级判定表,找到就绪任务中有具有最高优先级最高的
组(就是就绪任务中最高优先级的高三位
)。
也就是说,查找OSRdyGrp中从第0位到第7位中第一次出现非零的位的位置。
OSUnMapTbl[256]这张表也是根据当数值为0-255时,第一个出现的非零位。
OSUnMapTbl[256] 表定义如下:
INT8U const OSUnMapTbl[256] = {
0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F */
我们就分析第一行中的8个数字 从左到右表示数值0-7时,第一个出现的非零位的位数。
当数值为0的时候,表示为二进制的时候为0000 0000b,没有出现为1的位,记录为0。
当数值为1的时候,为0000 0001b,第一次出现1的位置为第0位。
当数值为2的时候,为0000 0010b,第一次出现1的位置为第1位。
当数值为3的时候,为0000 0011b,第一次出现1的位置为第0位。
当数值为4的时候,为0000 0100b,第一次出现1的位置为第2位。
当数值为5的时候,为0000 0101b,第一次出现1的位置为第0位。
当数值为6的时候,为0000 0110b,第一次出现1的位置为第1位。
当数值为7的时候,为0000 0111b,第一次出现1的位置为第0位。
后面的也一样,有兴趣、时间的自己去验证。
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 */
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 */
};
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
y << 3u就是将查找OSUnMapTbl的结果左移三位,相当于乘以8。
OSRdyTbl[y]找到就绪组中对应组的元素。再通过查找OSUnMapTbl,得到对应组中优先级的最高优先级的位置。
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);也等同于将任务所处的组*8+任务在对应组中的位置。
之前一直不理解为什么要用这么一张表,要查找最高优先级的任务,我们可以直接去判断的数据的位是否为0。
比如说: int i;
for(i = 0,;i < 8;i++)
{
if(OSRdyGrp&&(0x01 <<i))
{break;}
}
这样的方法在功能上是可以实现。
后来通过查找资料之后,就可以理解了,源码的方法最大的好处就是,不论什么情况下,这个查找方法都是确定的开销,执行的指令数目是恒定的。
但是我说的那种方法,OSRdyGrp的值不同,执行的代码数量是不一样的,开销也不一样。对于实时系统来说,确定性和稳定性是很重要的,而且通过这种查表的方法,速度比我说的方法要快得多,可以说用空间换取时间。
任务转为非就绪,就绪表和就绪组的更新,任务的源码如下所示
OSRdyTbl[ptcb->OSTCBY] &= (OS_PRIO)~ptcb->OSTCBBitX; //通过AND运算将就绪组中就绪组中相应的位清零
if (OSRdyTbl[ptcb->OSTCBY] == 0u) { //若是删除的优先级所在的整个组中都没有了就绪任务,则通过AND运算将就绪表中对应的位清零
OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;