1 就绪表
1.1 OS_LOWEST_PRIO & OS_MAX_TASKS
每个任务有不同的优先级,从0到63,在OS_CFG.H里对最低的优先级有专门的宏定义OS_LOWEST_PRIO。可以通过修改这个宏定义,来修改系统任务的最低优先级,但是注意,最低优先级永远都是被空闲应用idle task占用的。另外还有一个宏定义OS_MAX_TASKS表示系统最多有几个任务。之所以在就绪表说这两个,是因为这两个对就绪表或多或少有影响。
1.2 OSRedyGrp & OSRdyTbl[OS_RDY_TBL_SIZE]
每个任务的就绪态标志都放入就绪表中的,就绪表中有两个变量OSRedyGrp和OSRdyTbl[]。在 OSRdyGrp 中,任务按优先级分组,8个任务为一组。OSRdyGrp 中的每一位表示 8 组任务中每一组中是否有进入就绪态的任务。任务进入就绪态时, 就绪表 OSRdyTbl[]中的相应元素的相应位也置位。就绪表 OSRdyTbl[]数组的大小取决于OS_LOWEST_PR1O(见文件 OS_CFG.H)。当用户的应用程序中任务数目比较少时,减少 OS_LOWEST_PR1O 的值可以降低μC/OS-Ⅱ对RAM(数据空间)的需求量。
引用中写的是OSRedyGrp,这个缩写怎么看怎么奇怪啊,去源码看了下,应该是OSRdyGrp,和OSRdyTbl[]一样都在文件ucos_ii.h里声明的,而且就单独一对放在一起,可见联系紧密了。
OS_EXT OS_PRIO OSRdyGrp; /* Ready list group */
OS_EXT OS_PRIO OSRdyTbl[OS_RDY_TBL_SIZE]; /* Table of tasks which are ready to run */
教程没有提,但源码里有OSRdyTbl数组的容量OS_RDY_TBL_SIZE。
这个变量也是在ucos_ii.h里定义的。和它一起被定义还有些其他表的大小。如下即定义OS_RDY_TBL_SIZE的源码。
#if OS_LOWEST_PRIO <= 63u
#define OS_EVENT_TBL_SIZE ((OS_LOWEST_PRIO) / 8u + 1u) /* Size of event table */
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 8u + 1u) /* Size of ready table */
#else
#define OS_EVENT_TBL_SIZE ((OS_LOWEST_PRIO) / 16u + 1u)/* Size of event table */
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 16u + 1u)/* Size of ready table */
#endif
最低任务优先级肯定是小于64的,至于另外大于64的目前我也不清楚是干什么的。两个表容量,一个就绪表容量,一个事件表容量,后者以后会介绍到的。注意到表容量,最低优先级/8+1。可见最低优先级越小,表的大小就越小,这就可以节省RAM了。至于除以8再加1,举个例子,64个优先级,最低优先级为63,63 / 8 + 1 = 8。任务是每8个为一组,所以要除以8。
说了这么多,那引文中的就能解释了。任务优先级最大有64个,那么OSRdyTbl是一个容量最大为8的数组,数组中每一个变量都是8位的,每一位都可以代表一个任务,那么总共就可以最多代表64个任务了。任务进入就绪态时,对应的位置1。那么OSRdyGrp,也是个8位的变量,可以说相当于一个目录,当 OSRdyTbl的8个任务组中,有哪一组有任务进入就绪态了,那么对应的OSRdyGrp对应的位会置1。换句话说,OSRdyGrp的每一位,是OSRdyTbl中的每一个8位变量中,8个位的逻辑或值。
1.3 使任务进入就绪态
1.3.1 教程中给任务就绪标志位置1
OSRdyGrp |= OSMapTbl[prio >> 3];
OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
上述两行代码,实际上就是把OSRdyGrp和OSRdyTbl对应的位置1。OSMapTbl的值如下:
Index | Bit Mask(Binary) |
---|---|
0 | 00000001 |
1 | 00000010 |
2 | 00000100 |
3 | 00001000 |
4 | 00010000 |
5 | 00100000 |
6 | 01000000 |
7 | 10000000 |
上两行代码中,prio代表优先级。假如有64个优先级,优先级prio为50(110010),那么从0开始数就是第51个任务,8个一组就是第7组的第三个任务。
先看第一行代码,那么右移三位相当于除以8,即prio >> 3 = 6。即OSMapTbl [prio >>3] = 01000000,那么第一行代码即是把OSRdyGrp的bit6置1。
第二行代码,prio & 0x07 即 prio % 8 = 2,即OSMapTbl[prio & 0x07] = 00000100。第二行代码即OSRdyTbl[6]的bit2置1。
总的来说,就是OSRdyTbl数组中的第7组任务中的第2个任务的标志位1,以及对应的OSRdyTbl第七组的标志位置1。
看下面这幅图,X就是一组任务里的任务,Y就是总共的8组任务。
1.3.2 源码中给任务就绪标志位置1
我去翻了一下源码,发现源码里没有找到OSMapTbl这个数组。源码中是利用任务控制块OS_TCBs的OSTCBBitX和OSTCBBitY进行置位的。
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
其中ptcb是OSTCBPrioTbl[OS_LOWEST_PRIO + 1u],是个任务表吧。。之前在教程也没看到,只在源码看到过。OS_LOWEST_PRIO之前说过了是最低优先级,加个1的原因是因为有优先级0,那总共任务优先级有OS_LOWEST_PRIO+1个。OSTCBPrioTbl的类型就是指向OS_TCBs的指针,之前说过的这个结构体中含有OSTCBBitY和OSTCBBitX。这样看这两个变量就充当了OSMap的角色了。
同上一节的图一样,Y表示8个任务组,X表示每个任务组的8个任务。
1.3.3 找出进入就绪态优先级最高的任务
y = OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
prio = (y << 3) + x;
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 */
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,