在ucosii任务三要素和ucosii在CM3中的移植学习中,对于ucosii的任务的基本要素,任务切换的基本流程已经有了认识。任务切换中有一部分是找到当前就绪任务中优先级最高的,今天我们就来看看是如何找到要切换的任务的。
在ucosii任务三要素提到一个指针数组OSTCBPrioTbl,这个指针数组中放的是每一个任务控制块(TCB)的地址,并且是以任务的优先级来进行索引的,所以,只要知道了下一个任务的优先级,我们就能找到这个任务的TCB,从而就能完成任务切换。
为了表示系统中所有任务的就绪状态,ucosii创建了一个任务就绪表,其实就是一个数组OSRdyTbl
OS_EXT OS_PRIO OSRdyTbl[OS_RDY_TBL_SIZE]; /* Table of tasks which are ready to run */
我们以当前最大任务数不超过64个为例
OSRdyTbl数组就包括8个元素OSRdyTbl[8],元素类型是8位无符号整型数据。每一个元素的每一位代表任务是否处于就绪态,1表示是就绪态,0表示没有处于就绪态。8个元素就能表示64个优先级的就绪状态。其中0号元素的第0位表示优先级为0的任务,其中0号元素的第1位表示优先级为0的任务,一次类推,如下图。
这样我们可以将其理解成一个8*8的一个矩阵。我们只要知道行号和列号就能知道优先级的值。
在unsoii中 OSRdyGrp变量来指示OSRdyTbl中有哪一个元素不为0,也就表示哪一行的某些位被置了 1,OSRdyGrp中的一位对应OSRdyTbl中的一行
我们先来看创建一个任务时如何给就绪列表OSRdyTbl赋值,假如我们创建一个优先级为29的任务,我们需要做的就是将OSRdyTbl中的第三个元素的第5bit置1,也就是第(29/8)行的第(29%8)bit置1,再换一种说法,行号和列好最大为8,其实就是将十进制29换成了八进制的35,3为行号,5为列号。
在任务控制块中是有行号和列号这两个值的
typedef struct os_tcb { OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */ struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */ struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */ ........ INT32U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */ INT8U OSTCBStat; /* Task status */ INT8U OSTCBStatPend; /* Task PEND status */ INT8U OSTCBPrio; /* Task priority (0 == highest) */ INT8U OSTCBX; /* 这个是OSRdyTbl元素的哪一位 Bit position in group corresponding to task priority */ INT8U OSTCBY; /* 这个可以理解成行号,OSRdyTbl中的那个元素Index into ready table corresponding to task priority */ OS_PRIO OSTCBBitX; /* 1<<OSTCBX,OSRdyTbl元素的值 Bit mask to access bit position in ready table */ OS_PRIO OSTCBBitY; /* 1<<OSTCBY,OSRdyGrp的值 Bit mask to access bit position in ready group */ } OS_TCB;
在任务控制块的初始化函数中对这几个值进行了赋值
INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt) { OS_TCB *ptcb; OS_ENTER_CRITICAL(); ptcb = OSTCBFreeList; /* Get a free TCB from the free TCB list */ if (ptcb != (OS_TCB *)0) { OSTCBFreeList = ptcb->OSTCBNext; /* Update pointer to free TCB list */ OS_EXIT_CRITICAL(); ptcb->OSTCBStkPtr = ptos; /* Load Stack pointer in TCB */ ptcb->OSTCBPrio = prio; /* Load task priority into TCB */ ptcb->OSTCBStat = OS_STAT_RDY; /* Task is ready to run */ ptcb->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */ ptcb->OSTCBDly = 0u; /* Task is not delayed */ #if OS_LOWEST_PRIO <= 63u /任务小于64个 8*8*/ /* Pre-compute X, Y */ ptcb->OSTCBY = (INT8U)(prio >> 3u); /*获取行*/ ptcb->OSTCBX = (INT8U)(prio & 0x07u); /*获取列*/ #else /*任务最大为256个 16*16*/ /* Pre-compute X, Y */ ptcb->OSTCBY = (INT8U)((INT8U)(prio >> 4u) & 0xFFu); ptcb->OSTCBX = (INT8U) (prio & 0x0Fu); #endif /* Pre-compute BitX and BitY */ ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY); ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX); ......... OSTCBInitHook(ptcb); OSTaskCreateHook(ptcb); /* Call user defined hook */ OS_ENTER_CRITICAL(); OSTCBPrioTbl[prio] = ptcb; ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */ ptcb->OSTCBPrev = (OS_TCB *)0; if (OSTCBList != (OS_TCB *)0) { OSTCBList->OSTCBPrev = ptcb; } OSTCBList = ptcb; OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run 赋值 */ OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OSTaskCtr++; /* Increment the #tasks counter */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); } OS_EXIT_CRITICAL(); return (OS_ERR_TASK_NO_MORE_TCB); }
上述步骤就完成了对任务就绪表的赋值工作。现在做反方向的工作,知道了OSRdyGrp和OSRdytbl的值,如何找到最高优先级。注意,这里是最高优先级,我们需要用到位图法,位图法是用空间换取时间的方法。
列举OSRdyGrp的几种情况
OSRdyGrp=1>>>>>>OSRdyGrp的第0bit被置1,可以从OSRdytbl第0行找哪一位被置1,从而找到最高优先级
OSRdyGrp=2>>>>>>OSRdyGrp的第1bit被置1,可以从OSRdytbl第1行找哪一位被置1,从而找到最高优先级
OSRdyGrp=3>>>>>>OSRdyGrp的第0bit和第1bit都被置1,应从OSRdytbl第1行找哪一位被置1
OSRdyGrp=4>>>>>>OSRdyGrp的第2bit被置1,应从OSRdytbl第2行找哪一位被置1
OSRdyGrp=4>>>>>>OSRdyGrp的第2bit和第1bit被置1,应从OSRdytbl第0行找哪一位被置1
以此类推
..........
OSRdyGrp=255>>>>>>OSRdyGrp的第0bit---第7bit都被置1,应从OSRdytbl第0行找哪一位被置1
也就是把OSRdyGrp的256种情况都列出来,每一种情况对应着要找出的一个结果,
ucosii中表示如下
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 */ };
找出8行,就需要2的8次方的一个数组,同理找8列的时候也用这个数组,8*8就是64个优先级了
看一下在ucosii中的实际例子
static void OS_SchedNew (void) { #if OS_LOWEST_PRIO <= 63u /* See if we support up to 64 tasks 小于64个任务时 */ INT8U y; y = OSUnMapTbl[OSRdyGrp]; /*这两行就是在找到最高优先级了*/ OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]); #else /* We support up to 256 tasks 最高支持256个任务 */ INT8U y; OS_PRIO *ptbl; if ((OSRdyGrp & 0xFFu) != 0u) { y = OSUnMapTbl[OSRdyGrp & 0xFFu]; } else { y = OSUnMapTbl[(OS_PRIO)(OSRdyGrp >> 8u) & 0xFFu] + 8u; } ptbl = &OSRdyTbl[y]; if ((*ptbl & 0xFFu) != 0u) { OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(*ptbl & 0xFFu)]); } else { OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u); } #endif }
对于256任务的情况,稍微会复杂一些,前面64个任务是8行*8列,256则是16*16.但我们没必要去做一个2的16次方的位图,太大了,65535个字节空间,太浪费。把256分成(2*8行)*(2*8)列就ok了,加上两个if else判断一下就ok了
#if OS_LOWEST_PRIO <= 63u typedef INT8U OS_PRIO; #else typedef INT16U OS_PRIO; #endif首先对于数据类型会有个变化,大于64个任务时,任务优先级的相关变量用16位表示。
#if OS_LOWEST_PRIO <= 63u /* Pre-compute X, Y */ ptcb->OSTCBY = (INT8U)(prio >> 3u); ptcb->OSTCBX = (INT8U)(prio & 0x07u); #else /* Pre-compute X, Y */ ptcb->OSTCBY = (INT8U)((INT8U)(prio >> 4u) & 0xFFu); ptcb->OSTCBX = (INT8U) (prio & 0x0Fu); #endif /* Pre-compute BitX and BitY */ ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY); ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);
小于64个任务时,优先级转换位8进制数,作为行号列号,大于64就直接使用16进制数就可以了。初始化就这样,很简单
寻找最高优先级任务时,把行和列都一分为二即可(2*8)*(2*8)
if ((OSRdyGrp & 0xFFu) != 0u) { y = OSUnMapTbl[OSRdyGrp & 0xFFu]; } else { y = OSUnMapTbl[(OS_PRIO)(OSRdyGrp >> 8u) & 0xFFu] + 8u; } ptbl = &OSRdyTbl[y]; if ((*ptbl & 0xFFu) != 0u) { OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(*ptbl & 0xFFu)]); } else { OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u); }