uCOS版本:V2.91
使用过uCOS的人应该都知道,每一个uCOS的任务都有一个特定的优先级,就像人的身份证一样,是唯一的,这个优先级在创建的时候就有直到这个任务被删除,整个生命周期都是存在的。优先级越低,任务的优先级就越高。
本文主要讲述,优先级存在哪里,存放的原理,以及任务切换时是如何查找到优先级高的任务的。
uCOS的任务优先级存储主要用到以下几个变量:OSRdyGrp,OSRdyTbl,OSUnMapTbl
OSRdyGrp:这是一个INT8U类型的变量,这个变量用来表征哪一组优先级已准备好
OSRdyTbl:这是一个INT8U类型的数组,所有的优先级都保存在这个表中。
OSUnMapTbl:这个表用来查找最高优先级使用。
以下介绍均认为最大任务数为64.
首先要介绍的是OSRdyGrp和OSRdyTbl。OSRdyGrp类型为OS_PRIO,也就是INT8U。
OSRdyGrp表示优先级分组,在UCos中,任务优先级分为8组,每组有8个任务。OSRdyGrp的每一位表示一个分组,例如:OSRdyGrp的值为0x01即二进制位00000001,则表示优先级为0-7的任务中有一个或多个处于就绪状态。
OSRdyTbl为任务就绪表,这个表中存储了所有任务的就绪状态,表类型为OS_PRIO,也就是INT8U,定义为
这里OS_RDY_TBL_SIZE值为8,所以OSRdyTbl数组相当于一个8行8列的表格,如下:
下面看整个流程。
基本的思路就是把所有就绪的任务优先级存储起来,然后在任务切换时查找优先级最高的任务去执行即可。
任务的优先级存储是任务的创建时进行存储的。
在os_task.c文件中OSTaskCreate(),其中在调用OS_TCBInit()函数进行任务控制快点额初始化中的后面部分,有如下语句:
在这里看下,OSTCBBitX和OSTCBBitY这两个变量的来历。首先他们的类型是OS_PRIO,也就是INT8U,这两个变量的赋值在OS_TCBInit()函数的大概中间位置,这里我们假设最大任务数为64,所以我们只看前面两行和后面两行,
第一行,由任务的优先级右移3位,相当于缩小8倍,用于任务优先级的分组,上面已经讲到任务优先级分为8组。第五行,将这个值赋给OSTCBBitY,最终赋值给OSRdyGrp,意为只要该组有任务就绪,则该位置位。
第二行,取优先级低3位,相当于对8取余,用来计算该优先级在当组的哪一个位置。第6行,将这个值赋给OSTCBBitX,最终设定在OSRdyTbl表中。
此时任务优先级储存完成。
接下来看在任务切换的时候如何查找已就绪的最高优先级。
具体实现在os_core.c文件中的OS_SchedNew()函数中,
我们都知道,在ucos中,任务优先级越低,任务的优先级越高,所以我们要做的就是查找OSRdyTbl表中被置位的最小位置。ucos中,查找的方法是先找到就绪的组,然后再找到这个组中优先级最高的任务,比如说现在有,3个任务就绪,分别是5,20,45,则OSRdyGrp的值为00100101.我们知道,最高优先级是5.要想找到这个5,先找到这个5所在的组,相当于找OSRdyGrp变量所有位中被置位的最低位,这里被置位的最低位是第0位,所以为第0组,查找方法为查表。这里介绍最后一个数组变量OSUnMapTbl,这个数组中存储的是相应的数组下标被置位的最低位。比如5,最低被置位的是第0位,所以OSUnMapTbl[5] = 0,再比如8,最低被置位的是第1位,所以OSUnMapTbl[8] = 1.这样在查找最低位的时候就可以通过查表进行了。
下面举个栗子讲述一下整个流程:
初始化时创建3个任务,优先级分别为5,20,50
优先级为5:
OSTCBY= prio >> 3; // OSTCBY= 0x00
OSTCBX= prio & 0x07; // OSTCBX= 0x05
OSTCBBitY = 1uL << OSTCBY; // OSTCBBitY = 0x01
OSTCBBitX = 1uL << OSTCBX; // OSTCBBitX = 0x05
OSRdyGrp |= OSTCBBitY ; // OSRdyGrp = 0x01 00000001
OSRdyTbl[OSTCBY] |= OSTCBBitX; // OSRdyTbl[0] = 00100000
优先级为20:
OSTCBY= prio >> 3; // OSTCBY= 0x02 00000010
OSTCBX= prio & 0x07; // OSTCBX= 0x04 00000100
OSTCBBitY = 1uL << OSTCBY; // OSTCBBitY = 0x04 00000100
OSTCBBitX = 1uL << OSTCBX; // OSTCBBitX = 0x10 00010000
OSRdyGrp |= OSTCBBitY ; // OSRdyGrp = 0x05 00000101
OSRdyTbl[OSTCBY] |= OSTCBBitX; // OSRdyTbl[2] = 00010000
优先级为50:
OSTCBY= prio >> 3; // OSTCBY= 0x06 00000110
OSTCBX= prio & 0x07; // OSTCBX= 0x02 00000010
OSTCBBitY = 1uL << OSTCBY; // OSTCBBitY = 0x00 01000000
OSTCBBitX = 1uL << OSTCBX; // OSTCBBitX = 0x04 00000100
OSRdyGrp |= OSTCBBitY ; // OSRdyGrp = 0x45 01000101
OSRdyTbl[OSTCBY] |= OSTCBBitX; // OSRdyTbl[6] = 00000100
注意观察,此时,OSRdyGrp 的值已经从开始的0x00,变为现在的0x45,01000101,任务就绪表如下:
此时,任务就绪表存储已完成,接下来进行任务切换时候的查找。
y = OSUnMapTbl[OSRdyGrp]; // y = OSUnMapTbl[0x45] = OSUnMapTbl[69] = 0
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
//OSPrioHighRdy = 0+OSUnMapTbl[0x20]
= 0+OSUnMapTbl[32]
= 0+5
= 5
这样就找到了最高优先级5