使用過(guò)ucosii的朋友應(yīng)該都會(huì)知道,單片機(jī)+嵌入式實(shí)時(shí)操作系統(tǒng)能夠做到盡可能最大化的利用cpu資源,通過(guò)加入實(shí)時(shí)操作系統(tǒng)能夠做出更加強(qiáng)大的產(chǎn)品和應(yīng)用。
不知道使用過(guò)ucosii的朋友有沒有去了解過(guò)它進(jìn)行任務(wù)調(diào)度的原理和實(shí)現(xiàn)方式呢?
我個(gè)人結(jié)合ucosii的源碼和自己的理解,分享一些有關(guān)ucosii的任務(wù)管理和調(diào)度的實(shí)現(xiàn)。
1、ucos-ii 任務(wù)創(chuàng)建與任務(wù)調(diào)度
1.1、任務(wù)的創(chuàng)建
當(dāng)你調(diào)用 OSTaskCreate( ) 進(jìn)行任務(wù)的創(chuàng)建的時(shí)候,會(huì)初始化任務(wù)的堆棧、保存cpu的寄存器、創(chuàng)建任務(wù)的控制塊(OS_TCB)等的操作;
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority  */        OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ...  */                                             /* ... the same thing until task is created.              */        OS_EXIT_CRITICAL();        psp = OSTaskStkInit(task, p_arg, ptos, 0u);             /* Initialize the task's stack         */        err = OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u);        if (err == OS_ERR_NONE) {            if (OSRunning == OS_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);    }** 注意:ucosii不支持兩個(gè)及以上相同的任務(wù)優(yōu)先級(jí)的任務(wù),ucosiii支持時(shí)間片輪轉(zhuǎn)。**
ucosii 的任務(wù)控制塊是任務(wù)中很重要,它記錄了任務(wù)的信息,包括優(yōu)先級(jí)、延時(shí)時(shí)間、狀態(tài)等信息??刂茐K定義如下:
typedef structos_tcb {    OS_STK          *OSTCBStkPtr;           /* Pointer to current top of stack                         */
    void            *OSTCBExtPtr;           /* Pointer to user definable data for TCB extension        */    OS_STK          *OSTCBStkBottom;        /* Pointer to bottom of stack                              */    INT32U           OSTCBStkSize;          /* Size of task stack (in number of stack elements)        */    INT16U           OSTCBOpt;              /* Task options as passed by OSTaskCreateExt()             */    INT16U           OSTCBId;               /* Task ID (0..65535)                                      */
    struct os_tcb   *OSTCBNext;             /* Pointer to next     TCB in the TCB list                 */    struct os_tcb   *OSTCBPrev;             /* Pointer to previous TCB in the TCB list                 */
    OS_EVENT        *OSTCBEventPtr;         /* Pointer to          event control block                 */
    OS_EVENT       **OSTCBEventMultiPtr;    /* Pointer to multiple event control blocks                */
    void            *OSTCBMsg;              /* Message received from OSMboxPost() or OSQPost()         */
    OS_FLAG_NODE    *OSTCBFlagNode;         /* Pointer to event flag node                              */    OS_FLAGS         OSTCBFlagsRdy;         /* Event flags that made task ready to run                 */
    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;                /* Bit position in group  corresponding to task priority   */    INT8U            OSTCBY;                /* Index into ready table corresponding to task priority   */    OS_PRIO          OSTCBBitX;             /* Bit mask to access bit position in ready table          */    OS_PRIO          OSTCBBitY;             /* Bit mask to access bit position in ready group          */
    INT8U            OSTCBDelReq;           /* Indicates whether a task needs to delete itself         */
    INT32U           OSTCBCtxSwCtr;         /* Number of time the task was switched in                 */    INT32U           OSTCBCyclesTot;        /* Total number of clock cycles the task has been running  */    INT32U           OSTCBCyclesStart;      /* Snapshot of cycle counter at start of task resumption   */    OS_STK          *OSTCBStkBase;          /* Pointer to the beginning of the task stack              */    INT32U           OSTCBStkUsed;          /* Number of bytes used from the stack                     */
    INT8U           *OSTCBTaskName;
    INT32U           OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];} OS_TCB;2、任務(wù)調(diào)度實(shí)現(xiàn)
2.1、將任務(wù)優(yōu)先級(jí)進(jìn)行分組
因?yàn)閡cosii最大優(yōu)先級(jí)數(shù)量為64個(gè),所以可以分成8組,每組8個(gè)優(yōu)先級(jí)。
當(dāng)一個(gè)任務(wù)被創(chuàng)建成功之后,它的組號(hào)由優(yōu)先級(jí)的高三位決定(bit5 bit4 bit3),它在組內(nèi)的編號(hào)由優(yōu)先級(jí)的低三位決定(bit2 bit1 bit0),如下:
        ptcb->OSTCBY             = (INT8U)(prio >> 3u);    // 組        ptcb->OSTCBX             = (INT8U)(prio & 0x07u);  // 組內(nèi)編號(hào)2.2、任務(wù)就緒表
ucosii對(duì)任務(wù)優(yōu)先級(jí)的調(diào)度管理是通過(guò)查詢?nèi)蝿?wù)就緒表進(jìn)行的。任務(wù)就緒表里面保存著當(dāng)前所有任務(wù)的就緒狀態(tài),如下:
OSRdyTbl[8]
說(shuō)明:1)它是uint8的數(shù)據(jù)類型。它的長(zhǎng)度是8,每一個(gè)元素代表一個(gè)組,比如 OSRdyTbl[0]代表第0組, OSRdyTbl[1]代表第1組,OSRdyTbl[2]代表第2組……以此類推。
2)每一個(gè)元素中的每一個(gè)位(bit)代表組內(nèi)的任務(wù)的就緒狀態(tài)(1為就緒,0為未就緒)。說(shuō)明:
1)當(dāng)優(yōu)先級(jí)為12 的任務(wù)就緒時(shí),那么對(duì)應(yīng)的OSRdyTbl[1]的第4位bit,絕對(duì)等于1;
當(dāng)整個(gè)系統(tǒng)中,當(dāng)只有優(yōu)先級(jí)為12的任務(wù)就緒,其他所有任務(wù)都沒有就緒時(shí),那么OSRdyTbl[1] 絕對(duì)等于0x10。
2)當(dāng)優(yōu)先級(jí)為0和1的任務(wù)就緒時(shí),那么對(duì)應(yīng)的OSRdyTbl[0]的第0位bit以及第1位bit,都絕對(duì)等于1;
當(dāng)整個(gè)系統(tǒng)中,當(dāng)只有優(yōu)先級(jí)為0和1的任務(wù)就緒,其他所有任務(wù)都沒有就緒時(shí),那么OSRdyTbl[0] 絕對(duì)等于0x03。
2.3、任務(wù)釋放CPU使用權(quán)
當(dāng)任務(wù)中調(diào)用 OSTimeDly( ) 時(shí),會(huì)讓任務(wù)進(jìn)入休眠的狀態(tài),交出CPU的執(zhí)行權(quán)給到其他就緒任務(wù)去執(zhí)行,這個(gè)過(guò)程就發(fā)生了任務(wù)的切換。
簡(jiǎn)單而言就是會(huì)把任務(wù)就緒表 OSRdyTbl 中對(duì)應(yīng)的任務(wù)優(yōu)先級(jí)在組內(nèi)的編號(hào)狀態(tài)改變,從而使任務(wù)自身進(jìn)入休眠狀態(tài)。代碼如下:
if (ticks > 0u) {                            /* 0 means no delay!                                  */        OS_ENTER_CRITICAL();        y            =  OSTCBCur->OSTCBY;        /* Delay current task                                 */        OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;        if (OSRdyTbl[y] == 0u) {            OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;        }        OSTCBCur->OSTCBDly = ticks;              /* Load ticks in TCB                                  */        OS_EXIT_CRITICAL();        OS_Sched();                              /* Find next task to run!                             */    }在上面的代碼中發(fā)現(xiàn)了一個(gè)東西:OSRdyGrp。這個(gè)有什么用呢?
OSRdyGrp:管理任務(wù)就緒組的
OSRdyGrp是INT8U類型的,它每一個(gè)bit代表一個(gè)組,只要這個(gè)組內(nèi)有任何一個(gè)任務(wù)就緒了,那對(duì)應(yīng)的這個(gè)bit就會(huì)被設(shè)置為1,表示這個(gè)組內(nèi)目前有就緒的任務(wù)。否者對(duì)應(yīng)的位為0。
舉個(gè)例子,如下:
1)系統(tǒng)中只有任務(wù)0就緒了,那么OSRdyGrp 便等于 0x01(二進(jìn)制00000001)。2)系統(tǒng)中有任務(wù)0和任務(wù)63都就緒了,那么OSRdyGrp 便等于 0x81(二進(jìn)制10000001)。2.4、任務(wù)實(shí)現(xiàn)調(diào)度切換操作
發(fā)生一次任務(wù)調(diào)度是通過(guò) OS_Sched() 進(jìn)行的。源碼如下:
void  OS_Sched (void){    OS_CPU_SR  cpu_sr = 0u;
    OS_ENTER_CRITICAL();    if (OSIntNesting == 0u) {                          /* Schedule only if all ISRs done and ...       */        if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */            OS_SchedNew();            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy     */                OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */                OSCtxSwCtr++;                          /* Increment context switch counter             */                OS_TASK_SW();                          /* Perform a context switch                     */            }        }    }    OS_EXIT_CRITICAL();}這里的過(guò)程如下:
(1)先通過(guò) OS_SchedNew() 找到當(dāng)前處于就緒狀態(tài)的最高優(yōu)先級(jí)的任務(wù),如下:
y             = OSUnMapTbl[OSRdyGrp];OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);(2)然后通過(guò) OS_TASK_SW() 進(jìn)行任務(wù)切換,它的過(guò)程如下:
 只是一個(gè)宏,它實(shí)際替換的是 OSCtxSw()#define  OS_TASK_SW()         OSCtxSw()
2)OSCtxSw()是由匯編實(shí)現(xiàn)的OSCtxSw    PUSH    {R4, R5}        LDR     R4, =NVIC_INT_CTRL    ;觸發(fā)PendSV異常 (causes context switch)        LDR     R5, =NVIC_PENDSVSET        STR     R5, [R4]    POP     {R4, R5}        BX      LR就這樣,上下文就完成了一次切換。
                        電子發(fā)燒友App
                    
                
                
          
        
        




           
            
            
                
            
評(píng)論