?? ucos-ii源碼分析.c
字號:
OK,到這里OSInit算高一個段落了,我們接著回到main往下看。
OSTaskCreate
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錯誤。
然后調用OS_ENTER_CRITICAL(),進入臨界段,在臨界段中的代碼執行不允許被中斷。這個宏是用戶自定義的,一般是進行關中斷操作,例如在x86中的CLI等。這個宏和OS_EXIT_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是這么被賦值的:
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
也就是OSTCBBitY保存的是組別,OSTCBBitX保存的是組內的偏移。而這兩個變量是這么被初始化的:
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 */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -