uC/OS-II main函数的大致流程如下:
然后调用OS_ENTER_CRITICAL(),进入临界段,在临界段中的代码执行不允许被中断。这个宏是用户自定义的,一般是进行关中断操作。
OSTaskCreate不允许在中断中调用,因此会判断OSIntNesting是否大于0,如果大于0,表示正在中断嵌套,返回OS_ERR_TASK_CREATE_ISR错误。
接着判断该prio是否已经有Task存在,由于uC/OS-II只支持每一个优先级一个Task,因此如果该prio已经有进程存在,OSTaskCreate会返回OS_PRIO_EXIST错误。
相反,如果该prio先前没有Task存在,则将OSTCBPrioTbl[prio]置1,表示该prio已被占用,然后调用OSTaskStkInit初始化堆栈,调用OS_TCBInit初始化TCB,如果OSRunning为TRUE表示OS正在运行,则调用OS_Sched进行进程调度;否则返回。
下面来看看OSTaskStkInit和OS_TCBInit这两个函数。
OSTaskStkInit是一个用户自定义的函数,因为uC/OS-II在设计时无法知道当前处理器在进行进程调度时需要保存那些信息,OSTaskStkInit就是初始化堆栈,让Task看起来就好像刚刚进入中断并保存好寄存器的值一样,当OS_Sched调度到该Task时,只需切换到该堆栈中,将寄存器值Pop出来,然后执行一个中断返回指令IRET即可。
OSTaskStkInit的原型如下:
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
和OSTaskCreate类似,task是进程入口地址,pdata是参数地址,ptos是堆栈指针,而opt只是作为一个预留的参数Option而保留。返回的是调整以后的堆栈指针。
在OSTaskStkInit中,一般是将pdata入栈,flag入栈,task入栈,然后将各寄存器依次入栈。
然后调用OS_EXIT_CRITICAL离开临界段,接着对该TCB进行初始化:
将OSTCBStkPtr初始化为该Task当前堆栈指针;
OSTCBPrio设置为该Task的prio;
OSTCBStat设置为OS_STAT_RDY,表示就绪状态;
OSTCBDly设置为0,当该Task调用OSTimeDly时会初始化这个变量为Delay的时钟数,然后Task转入OS_STAT_状态。这个变量在OSTimeTick中检查,如果大于0表示还需要进行Delay,则进行减1;如果等于零表示无须进行Delay,可以马上运行,转入OS_STAT_RDY状态。
OSTCBBitY和OSTCBBitX的作用我们在等会专门来讨论。
紧接着就要将该TCB插入OSTCBList列表中,先调用OS_ENTER_CRITICAL进入临界段,将该TCB插入到OSTCBList成为第一个节点,然后调整OSRdyGrp和OSRdyTbl,(这两个变量一会和OSTCBBitX/OSTCBBitY一起讨论),最后将OSTaskCtr计数器加一,调用OS_EXIT_CRITICAL退出临界段。
OSMapTbl和OSUnMapTbl
刚才我们看到TCB数据结构中的OSTCBBitX/OSTCBBitY以及OSRdyGrp/OSRdyTbl的使用,这里专门来讨论讨论这几个变量的用法。
uC/OS-II将64个优先级的进程分为8组,每组8个。刚好可以使用8个INT8U的数据进行表示,于是这就是OSRdyGrp和OSRdyTbl的由来,OSRdyGrp表示组别,从0到7,从前面我们可以看到OSRdyGrp和OSRdyTbl是这么被赋值的:
这里就涉及到OSMapTbl数组和OSUnMapTbl数组的用法了。我们先看看OSMapTbl和OSUnMapTbl的定义:
于是我们提前来看看OS_Sched()中获取最高优先级所使用的方法:
于是乎,我们就可以对上面那一小段很模糊的代码做一下总结:
prio只有6位,高3位代表着某一个Group保存在OSTCBY中,OSTCBBitY表示该Group所对应的Bit,将OSRdyGrp的该位置1表示该Group中有进程是Ready的;低3位代表着该Group中的第几个进程,保存在OSTCBX中,OSTCBBitX表示该进程在该Group中所对应的Bit,OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX就等于将该进程所对应的Bit置1了。
main(){
OSInit();
TaskCreate(...);
OSStart();
}
OSInit()函数用来初始化内核,必须首先调用。建立两个任务:空闲任务(其他任务都未就绪时运行),统计任务(计算CPU的利用率)。
void OSInit (void)
{
OSInitHookBegin(); /* 调用用户特定的初始化代码(通过一个接口函数实现用户要求的插件式进入系统中)*/
OS_InitMisc(); /* 初始化变量*/
OS_InitRdyList(); /* 初始化就绪列表*/
OS_InitTCBList(); /* 初始化OS_TCB空闲列表*/
OS_InitEventList(); /* 初始化OS_EVENT空闲列表*/
#if (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) //允许事件标志
OS_FlagInit(); /* 初始化事件标志结构*/
#endif
#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0) //允许内存管理
OS_MemInit(); /* 初始化内存管理器*/
#endif
#if (OS_Q_EN > 0) && (OS_MAX_QS > 0) //允许消息队列
OS_QInit(); /* 初始化消息队列结构*/
#endif
OS_InitTaskIdle(); /*创建空闲任务*/
#if OS_TASK_STAT_EN > 0
OS_InitTaskStat(); /* 创建统计任务*/
#endif
#if OS_TMR_EN > 0 //允许时间管理
OSTmr_Init(); /* 初始化时间管理器*/
#endif
OSInitHookEnd(); /*调用用户特定的初始化代码(参考OSInitHookBegin())*/
#if OS_DEBUG_EN > 0 //允许Debug
OSDebugInit(); //初始化调试器
#endif
}
进入OSInit()来围观一下~~
首先是OS_InitMisc (),OS_InitMisc 的主要作用是对一些其他的变量进行初始化,主要包括下面这些变量:
static void OS_InitMisc (void)
{
#if OS_TIME_GET_SET_EN > 0
OSTime = 0L; /* 32位的系统时钟清零*/
#endif
OSIntNesting = 0; /* 中断嵌套层数计数器清零*/
OSLockNesting = 0; /* 调度器锁的嵌套层数计数器清零*/
OSTaskCtr = 0; /* 任务数清零*/
OSRunning = OS_FALSE; /* 指明多任务未开始*/
OSCtxSwCtr = 0; /* 任务切换次数计数器清零*/
OSIdleCtr = 0L; /* 32位空闲计数器清零*/
#if OS_TASK_STAT_EN > 0 /* 运行统计任务*/
OSIdleCtrRun = 0L;
OSIdleCtrMax = 0L;
OSStatRdy = OS_FALSE; /* 统计任务未就绪*/
#endif
}
接下来初始化任务就绪队列,任务的切换依靠任务队列。OS_InitRdyList()函数主要涉及就虚队列,任务就绪表,任务指针和任务控制块指针的初始化。
static void OS_InitRdyList (void)
{
INT16U i;
INT8U *prdytbl;
OSRdyGrp = 0x00; /* 清空就绪队列 */
prdytbl = &OSRdyTbl[0];<span style="white-space:pre"> </span> <pre name="code" class="cpp">
for (i = 0; i < OS_RDY_TBL_SIZE; i++) { /*清空任务就绪表*/
*prdytbl++ = 0x00; } OSPrioCur = 0; /* 当前任务指针初始化 */ OSPrioHighRdy = 0; /* 最高优先级任务指针初始化 */ OSTCBHighRdy = (OS_TCB *)0; /* 最高级任务控制块指针初始化 */ OSTCBCur = (OS_TCB *)0; /* 当前任务控制块指针初始化 */}
任务控制块队列初始化~~~
static void OS_InitTCBList (void)
{
INT8U i;
OS_TCB *ptcb1;
OS_TCB *ptcb2;
OSTCBList = (OS_TCB *)0; /* TCB初始化*/
for (i = 0; i < (OS_LOWEST_PRIO + 1); i++) { /* 清空任务优先级表*/
OSTCBPrioTbl[i] = (OS_TCB *)0;
}
ptcb1 = &OSTCBTbl[0];
ptcb2 = &OSTCBTbl[1];
for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++) { /* 初始化TCB指针*/
ptcb1->OSTCBNext = ptcb2;
ptcb1++;
ptcb2++;
}
ptcb1->OSTCBNext = (OS_TCB *)0; /* 将最后指针的Next设为空*/
OSTCBFreeList = &OSTCBTbl[0];
}
事件空闲列表初始化:
static void OS_InitEventList (void)
{
#if (OS_EVENT_EN > 0) && (OS_MAX_EVENTS > 0)
#if (OS_MAX_EVENTS > 1)
INT16U i;
OS_EVENT *pevent1;
OS_EVENT *pevent2;
pevent1 = &OSEventTbl[0];
pevent2 = &OSEventTbl[1];
for (i = 0; i < (OS_MAX_EVENTS - 1); i++) { /*初始化事件控制块 */
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr = pevent2;
pevent1++;
pevent2++;
}
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr = (OS_EVENT *)0;
OSEventFreeList = &OSEventTbl[0];
#else
OSEventFreeList = &OSEventTbl[0];
OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED;
OSEventFreeList->OSEventPtr = (OS_EVENT *)0;
#endif
#endif
}
中间部分略过~~接下来看OS_InitTaskIdle和OS_InitTaskStat。
空闲任务和统计任务建立的代码基本一样,只是统计任务的优先级比空闲任务大1,
static void OS_InitTaskIdle (void)
{
#if OS_TASK_NAME_SIZE > 7 //
INT8U err;
#endif
#if OS_TASK_CREATE_EXT_EN > 0 //使用扩展的OSTaskCreateExt来创建
#if OS_STK_GROWTH == 1 //任务堆栈从底部向顶部增长的方向有两种:表示从大到小,表示从小到大
(void)OSTaskCreateExt(OS_TaskIdle,
(void *)0, /* 没有参数传给OS_TaskIdle() */
&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], /*设置堆栈顶*/
OS_TASK_IDLE_PRIO, /* 优先级设置为最低*/
OS_TASK_IDLE_ID, //设置ID
&OSTaskIdleStk[0], /* 设置栈底*/
OS_TASK_IDLE_STK_SIZE, //设置栈大小
(void *)0, /* 没有TCB扩展数据结构
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* 允许堆栈检测和清空堆栈*/
#else
(void)OSTaskCreateExt(OS_TaskIdle,
(void *)0, /* No arguments passed to OS_TaskIdle() */
&OSTaskIdleStk[0], /* Set Top-Of-Stack
OS_TASK_IDLE_PRIO, /* Lowest priority level
OS_TASK_IDLE_ID,
&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], /* Set Bottom-Of-Stack */
OS_TASK_IDLE_STK_SIZE,
(void *)0, /* No TCB extension
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */
#endif
#else //使用不带扩展性的OSTaskCreate创建
#if OS_STK_GROWTH == 1
(void)OSTaskCreate(OS_TaskIdle,
(void *)0,
&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1],
OS_TASK_IDLE_PRIO);
#else
(void)OSTaskCreate(OS_TaskIdle,
(void *)0,
&OSTaskIdleStk[0],
OS_TASK_IDLE_PRIO);
#endif
#endif
//设置任务名称
#if OS_TASK_NAME_SIZE > 14
OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)"uC/OS-II Idle", &err);
#else
#if OS_TASK_NAME_SIZE > 7
OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)"OS-Idle", &err);
#endif
#endif
}
初始化基本完成了,然后我们返回main函数
OSTaskCreate负责创建Task所需的数据结构,该函数原形如下所示:
INT8U OSTaskCreate (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT8U prio)
参数说明:
task:函数指针,指向该Task所开始的函数,当这个Task第一次被调度运行时将会从task处开始运行。
p_arg:传给task的参数指针;
ptos:堆栈指针,指向栈顶(堆栈从上往下)或栈底(堆栈从下往上);
prio:进程的优先级,uC/OS-II共支持最大64个优先级,其中最低的两个优先级给Idle和Stat进程,并且各个Task的优先级必须不同。
接下来,我们看看这个函数的执行流程:
#if OS_ARG_CHK_EN > 0
if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */
return (OS_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) { /* Make sure we don't create the task from within an ISR */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_CREATE_ISR);
}
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority */
OSTCBPrioTbl[prio] = (OS_TCB *)1; /* Reserve the priority to prevent others from doing ... */
/* ... the same thing until task is created. */
OS_EXIT_CRITICAL();
psp = (OS_STK *)OSTaskStkInit(task, p_arg, ptos, 0); /* Initialize the task's stack */
err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);
if (err == OS_NO_ERR) {
if (OSRunning == TRUE) { /* Find highest priority task if multitasking has started */
OS_Sched();
}
} else {
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */
OS_EXIT_CRITICAL();
}
return (err);
}
OS_EXIT_CRITICAL();
return (OS_PRIO_EXIST);
OS_LOWEST_PRIO在ucos-ii.h中被定义为63,表示Task的优先级从0到63,共64级。首先判断prio是否超过最低优先级,如果是,则返回OS_PRIO_INVALID错误。防止优先级低于63。
然后调用OS_ENTER_CRITICAL(),进入临界段,在临界段中的代码执行不允许被中断。这个宏是用户自定义的,一般是进行关中断操作。
OSTaskCreate不允许在中断中调用,因此会判断OSIntNesting是否大于0,如果大于0,表示正在中断嵌套,返回OS_ERR_TASK_CREATE_ISR错误。
接着判断该prio是否已经有Task存在,由于uC/OS-II只支持每一个优先级一个Task,因此如果该prio已经有进程存在,OSTaskCreate会返回OS_PRIO_EXIST错误。
相反,如果该prio先前没有Task存在,则将OSTCBPrioTbl[prio]置1,表示该prio已被占用,然后调用OSTaskStkInit初始化堆栈,调用OS_TCBInit初始化TCB,如果OSRunning为TRUE表示OS正在运行,则调用OS_Sched进行进程调度;否则返回。
下面来看看OSTaskStkInit和OS_TCBInit这两个函数。
OSTaskStkInit是一个用户自定义的函数,因为uC/OS-II在设计时无法知道当前处理器在进行进程调度时需要保存那些信息,OSTaskStkInit就是初始化堆栈,让Task看起来就好像刚刚进入中断并保存好寄存器的值一样,当OS_Sched调度到该Task时,只需切换到该堆栈中,将寄存器值Pop出来,然后执行一个中断返回指令IRET即可。
OSTaskStkInit的原型如下:
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
和OSTaskCreate类似,task是进程入口地址,pdata是参数地址,ptos是堆栈指针,而opt只是作为一个预留的参数Option而保留。返回的是调整以后的堆栈指针。
在OSTaskStkInit中,一般是将pdata入栈,flag入栈,task入栈,然后将各寄存器依次入栈。
OS_TCBInit初始化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->OSTCBPendTO = FALSE; /* Clear the Pend timeout flag */
ptcb->OSTCBDly = 0; /* Task is not delayed */
#if OS_TASK_CREATE_EXT_EN > 0
ptcb->OSTCBExtPtr = pext; /* Store pointer to TCB extension */
ptcb->OSTCBStkSize = stk_size; /* Store stack size */
ptcb->OSTCBStkBottom = pbos; /* Store pointer to bottom of stack */
ptcb->OSTCBOpt = opt; /* Store task options */
ptcb->OSTCBId = id; /* Store task ID */
#else
pext = pext; /* Prevent compiler warning if not used */
stk_size = stk_size;
pbos = pbos;
opt = opt;
id = id;
#endif
#if OS_TASK_DEL_EN > 0
ptcb->OSTCBDelReq = OS_NO_ERR;
#endif
ptcb->OSTCBY = (INT8U)(prio >> 3); /* Pre-compute X, Y, BitX and BitY */
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = (INT8U)(prio & 0x07);
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
#if OS_EVENT_EN
ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Task is not pending on an event */
#endif
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_NO_ERR);
}
OS_EXIT_CRITICAL();
return (OS_NO_MORE_TCB);
}
首先调用OS_ENTER_CRITICAL进入临界段,首先从OSTCBFreeList中拿出一个TCB,如果OSTCBFreeList为空,则返回OS_NO_MORE_TCB错误。
然后调用OS_EXIT_CRITICAL离开临界段,接着对该TCB进行初始化:
将OSTCBStkPtr初始化为该Task当前堆栈指针;
OSTCBPrio设置为该Task的prio;
OSTCBStat设置为OS_STAT_RDY,表示就绪状态;
OSTCBDly设置为0,当该Task调用OSTimeDly时会初始化这个变量为Delay的时钟数,然后Task转入OS_STAT_状态。这个变量在OSTimeTick中检查,如果大于0表示还需要进行Delay,则进行减1;如果等于零表示无须进行Delay,可以马上运行,转入OS_STAT_RDY状态。
OSTCBBitY和OSTCBBitX的作用我们在等会专门来讨论。
紧接着就要将该TCB插入OSTCBList列表中,先调用OS_ENTER_CRITICAL进入临界段,将该TCB插入到OSTCBList成为第一个节点,然后调整OSRdyGrp和OSRdyTbl,(这两个变量一会和OSTCBBitX/OSTCBBitY一起讨论),最后将OSTaskCtr计数器加一,调用OS_EXIT_CRITICAL退出临界段。
OSMapTbl和OSUnMapTbl
刚才我们看到TCB数据结构中的OSTCBBitX/OSTCBBitY以及OSRdyGrp/OSRdyTbl的使用,这里专门来讨论讨论这几个变量的用法。
uC/OS-II将64个优先级的进程分为8组,每组8个。刚好可以使用8个INT8U的数据进行表示,于是这就是OSRdyGrp和OSRdyTbl的由来,OSRdyGrp表示组别,从0到7,从前面我们可以看到OSRdyGrp和OSRdyTbl是这么被赋值的:
<span style="white-space:pre"> </span>OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
也就是OSTCBBitY保存的是组别,OSTCBBitX保存的是组内的偏移。而这两个变量是这么被初始化的:
<span style="white-space:pre"> </span>ptcb->OSTCBY = (INT8U)(prio >> 3);
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = (INT8U)(prio & 0x07);
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
由于prio不会大于64,prio为6位值,因此OSTCBY为prio高3位,不会大于8,OSTCBX为prio低3位。
这里就涉及到OSMapTbl数组和OSUnMapTbl数组的用法了。我们先看看OSMapTbl和OSUnMapTbl的定义:
INT8U const OSMapTbl[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
INT8U const OSUnMapTbl[256] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */
};
OSMapTbl分别是一个INT8U的八个位,而OSUnMap数组中的值就是从0x00到0xFF的八位中,每一个值所对应的最低位的值。我们在调度的时候只需将OSRdyGrp的值代入OSUnMapTbl数组中,得到OSUnMapTbl[OSRdyGrp]的值就是哪个优先级最高的Group有Ready进程存在,再使用该Group对应OSRdyTbl[]数组中的值一样带入OSUnMapTbl中就可以得出哪个Task是优先级最高的。
于是我们提前来看看OS_Sched()中获取最高优先级所使用的方法:
y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
显然,先得到的y就是存在最高优先级的Group,然后OSUnMapTbl[OSRdyTbl[y]]就是Group中的偏移,因此OSPrioHighRdy最高优先级就应该是Group<<3再加上这个偏移。
于是乎,我们就可以对上面那一小段很模糊的代码做一下总结:
prio只有6位,高3位代表着某一个Group保存在OSTCBY中,OSTCBBitY表示该Group所对应的Bit,将OSRdyGrp的该位置1表示该Group中有进程是Ready的;低3位代表着该Group中的第几个进程,保存在OSTCBX中,OSTCBBitX表示该进程在该Group中所对应的Bit,OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX就等于将该进程所对应的Bit置1了。