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_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错误。
<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位。
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是优先级最高的。
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再加上这个偏移。