99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋(píng)果6袋
6
麥子學(xué)院

uCos的多任務(wù)如何實(shí)現(xiàn)

發(fā)布時(shí)間:2016-08-11 16:44  回復(fù):0  查看:3080   最后回復(fù):2016-08-11 16:44  

作為操作系統(tǒng)(OS),最基本的一項(xiàng)服務(wù)就是提供多線(xiàn)程,在實(shí)時(shí)操作系統(tǒng)uCos里,多線(xiàn)程被稱(chēng)為多任務(wù)(Task)。多任務(wù)并不是CPU能真正同時(shí)運(yùn)行多個(gè)程序,實(shí)際是靠CPU在多個(gè)任務(wù)之間轉(zhuǎn)換切換實(shí)現(xiàn)的,CPU輪番的服務(wù)于一系列的任務(wù),這樣CPU在宏觀(guān)上好像在同時(shí)執(zhí)行多個(gè)任務(wù),實(shí)際在微觀(guān)上CPU絕對(duì)是單任務(wù)的。這里要注意區(qū)別多線(xiàn)程和多核,如果系統(tǒng)里是有多個(gè)CPU,則可以實(shí)現(xiàn)真正的多線(xiàn)程了。

按照上面的思路,多任務(wù)的實(shí)現(xiàn),就是要實(shí)現(xiàn)CPU在不同的任務(wù)之間切換。按照uCos作者的話(huà)說(shuō):就是不斷的保存,恢復(fù)CPU的那些寄存器。接下來(lái),我們就來(lái)學(xué)習(xí)uCos的多任務(wù)吧。

我們知道uCos的多任務(wù)(這里以?xún)蓚€(gè)任務(wù)為例)的程序模型如下:

void  Task_A(void *p_arg)

{

   while(1)

   {    

        OSTimeDly(10);  //任務(wù)里必須有類(lèi)似的主動(dòng)釋放CPU的函數(shù)

......       

   }

}

 

void  Task_B(void *p_arg)

{

   while(1)

   {    

        OSTimeDly(10);           

       ......

        }

}

使用uCos,程序?qū)⒃谶@兩個(gè)任務(wù)之間輪換,這兩個(gè)while(1)里的程序都可以得到執(zhí)行。

我們知道,在裸機(jī)編程里,如果出現(xiàn)while(1)這樣的語(yǔ)句,那么程序?qū)⒂肋h(yuǎn)在這個(gè)循環(huán)里執(zhí)行(當(dāng)然是要程序主動(dòng)調(diào)用break除外),他是不會(huì)放棄CPU的,那為什么加了操作系統(tǒng)后,程序能在這兩個(gè)while(1)之間輪換?

操作系統(tǒng)都需要使用時(shí)鐘節(jié)拍技術(shù)來(lái)實(shí)現(xiàn)對(duì)任務(wù)的監(jiān)控,并能主動(dòng)調(diào)度和切換任務(wù)的執(zhí)行。uCos也同樣是使用時(shí)鐘節(jié)拍來(lái)實(shí)現(xiàn)任務(wù)的監(jiān)管的,以STM32單片機(jī)為例,一般用SysTick這個(gè)系統(tǒng)時(shí)鐘定時(shí)器來(lái)實(shí)現(xiàn),比如我們?cè)O(shè)置這個(gè)定時(shí)器10ms的定時(shí)間隔,那么每隔10ms都會(huì)調(diào)用下面的中斷服務(wù)(所以移植的時(shí)候,需要實(shí)現(xiàn)這個(gè)函數(shù))

void SysTickHandler(void)

{

    OSIntEnter();

    OSTimeTick();    

    OSIntExit(); 

}

假設(shè)現(xiàn)在程序在Task_Awhile(1)里,然后SysTick中斷來(lái)了,SysTickHandler服務(wù)程序開(kāi)始執(zhí)行,先執(zhí)行OSIntEnter()

void  OSIntEnter (void)

{

    if (OSRunning == OS_TRUE) {

        if (OSIntNesting < 255u) {

            OSIntNesting++;                          

        }

    }

}

這個(gè)函數(shù)很簡(jiǎn)單,他是uCos的內(nèi)核函數(shù),主要是給中斷嵌套計(jì)數(shù)器OSIntNesting加一,因?yàn)?/span>uCos內(nèi)核需要實(shí)時(shí)的判斷程序的當(dāng)前執(zhí)行是不是在中斷里,要知道,大部分的處理器是可以中斷嵌套的,這里我們先不管判斷程序是不是在中斷里有什么用,后面馬上會(huì)說(shuō)到。

然后開(kāi)始執(zhí)行OSTimeTick(),這個(gè)函數(shù)我們只分析關(guān)鍵代碼,也就是跟任務(wù)管理有關(guān)的代碼:

void  OSTimeTick (void)

{

    OS_TCB    *ptcb;

#if OS_TICK_STEP_EN > 0

    BOOLEAN    step;

#endif

#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register     */

    OS_CPU_SR  cpu_sr = 0;

#endif

 

#if OS_TIME_TICK_HOOK_EN > 0

    OSTimeTickHook();                                      /* Call user definable hook                     */

#endif

 

#if OS_TIME_GET_SET_EN > 0

    OS_ENTER_CRITICAL();                                   /* Update the 32-bit tick counter               */

    OSTime++;

    OS_EXIT_CRITICAL();

#endif

 

    if (OSRunning == OS_TRUE) {

#if OS_TICK_STEP_EN > 0

        switch (OSTickStepState) {                         /* Determine whether we need to process a tick  */

            case OS_TICK_STEP_DIS:                         /* Yes, stepping is disabled                    */

                 step = OS_TRUE;

                 break;

 

            case OS_TICK_STEP_WAIT:                        /* No,  waiting for uC/OS-View to set ...       */

                 step = OS_FALSE;                          /*      .. OSTickStepState to OS_TICK_STEP_ONCE */

                 break;

 

            case OS_TICK_STEP_ONCE:                        /* Yes, process tick once and wait for next ... */

                 step            = OS_TRUE;                /*      ... step command from uC/OS-View        */

                 OSTickStepState = OS_TICK_STEP_WAIT;

                 break;

 

            default:                                       /* Invalid case, correct situation              */

                 step            = OS_TRUE;

                 OSTickStepState = OS_TICK_STEP_DIS;

                 break;

        }

        if (step == OS_FALSE) {                            /* Return if waiting for step command           */

            return;

        }

#endif

        ptcb = OSTCBList;                                  /* Point at first TCB in TCB list               */

        while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) {     /* Go through all TCBs in TCB list              */

            OS_ENTER_CRITICAL();

            if (ptcb->OSTCBDly != 0) {                     /* No, Delayed or waiting for event with TO     */

                if (--ptcb->OSTCBDly == 0) {               /* Decrement nbr of ticks to end of delay       */

                                                           /* Check for timeout                            */

                    if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {

                        ptcb->OSTCBStat   &= ~OS_STAT_PEND_ANY;                /* Yes, Clear status flag   */

                        ptcb->OSTCBPendTO  = OS_TRUE;                          /* Indicate PEND timeout    */

                    } else {

                        ptcb->OSTCBPendTO  = OS_FALSE;

                    }

 

                    if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {  /* Is task suspended?       */

                        OSRdyGrp               |= ptcb->OSTCBBitY;             /* No,  Make ready          */

                        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

                    }

                }

            }

            ptcb = ptcb->OSTCBNext;                        /* Point at next TCB in TCB list                */

            OS_EXIT_CRITICAL();

        }

    }

}

在這個(gè)函數(shù)里,出現(xiàn)了一個(gè)全局變量OSTCBList,這個(gè)變量是uCos內(nèi)核里任務(wù)控制塊(TCB)鏈表的表頭指針,問(wèn)題又來(lái)了,TCB是什么,TCB鏈表又是什么?

原來(lái)uCos的每個(gè)任務(wù)都需要有一個(gè)TCB來(lái)管理,這個(gè)TCB記錄了該任務(wù)的所有信息,同時(shí)uCos用鏈表來(lái)管理所有的這些TCB,TCB的結(jié)構(gòu)如下:

typedef struct os_tcb {

    OS_STK          *OSTCBStkPtr;      /* Pointer to current top of stack                              */

#if OS_TASK_CREATE_EXT_EN > 0

    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)                                           */

#endif

 

    struct os_tcb   *OSTCBNext;        /* Pointer to next     TCB in the TCB list                      */

    struct os_tcb   *OSTCBPrev;        /* Pointer to previous TCB in the TCB list                      */

 

#if OS_EVENT_EN

    OS_EVENT        *OSTCBEventPtr;    /* Pointer to event control block                               */

#endif

 

#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)

    void            *OSTCBMsg;         /* Message received from OSMboxPost() or OSQPost()              */

#endif

 

#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)

#if OS_TASK_DEL_EN > 0

    OS_FLAG_NODE    *OSTCBFlagNode;    /* Pointer to event flag node                                   */

#endif

    OS_FLAGS         OSTCBFlagsRdy;    /* Event flags that made task ready to run                      */

#endif

 

    INT16U           OSTCBDly;         /* Nbr ticks to delay task or, timeout waiting for event        */

    INT8U            OSTCBStat;        /* Task status                                                  */

    BOOLEAN          OSTCBPendTO;      /* Flag indicating PEND timed out (OS_TRUE == timed out)           */

    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        */

#if OS_LOWEST_PRIO <= 63

    INT8U            OSTCBBitX;        /* Bit mask to access bit position in ready table               */

    INT8U            OSTCBBitY;        /* Bit mask to access bit position in ready group               */

#else

    INT16U           OSTCBBitX;        /* Bit mask to access bit position in ready table               */

    INT16U           OSTCBBitY;        /* Bit mask to access bit position in ready group               */

#endif

 

#if OS_TASK_DEL_EN > 0

    INT8U            OSTCBDelReq;      /* Indicates whether a task needs to delete itself              */

#endif

 

#if OS_TASK_PROFILE_EN > 0

    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                          */

#endif

 

#if OS_TASK_NAME_SIZE > 1

    INT8U            OSTCBTaskName[OS_TASK_NAME_SIZE];

#endif

} OS_TCB;

 

這結(jié)構(gòu)體稍微有點(diǎn)大,這里我們先知需要關(guān)心OSTCBDly這個(gè)成員,這個(gè)成員記錄該任務(wù)的延時(shí)值,當(dāng)我們調(diào)用uCos的系統(tǒng)函數(shù)OSTimeDly(),OSQPend()等這些阻塞類(lèi)型的函數(shù)時(shí),該任務(wù)的TCB成員OSTCBDly就會(huì)被賦予相應(yīng)值。

我們繼續(xù)分析OSTimeTick(),這個(gè)函數(shù),在這個(gè)函數(shù)里,主要是給每個(gè)TCBOSTCBDly的計(jì)數(shù)值減一,如果OSTCBDly為零了,則在任務(wù)就緒表里標(biāo)記該任務(wù),這里又引出了一問(wèn)題,什么是就緒表,uCos的就緒表的實(shí)現(xiàn)用了一個(gè)比較巧妙的算法,這里先不仔細(xì)去分析,只有知道就緒表里記錄了當(dāng)前系統(tǒng)中,哪些任務(wù)是處于就緒態(tài),也就是可被CPU執(zhí)行的狀態(tài)。

好,到這里OSTimeTick()分析完了,他完成了對(duì)每個(gè)任務(wù)的延時(shí)值減一操作,并更新了就緒表。下面開(kāi)始執(zhí)行OSIntExit(),這個(gè)函數(shù)是個(gè)很關(guān)鍵的函數(shù),她將實(shí)現(xiàn)任務(wù)的上下文切換。

OSIntExit函數(shù)的源碼如下:

void  OSIntExit (void)

{

#if OS_CRITICAL_METHOD == 3                               

    OS_CPU_SR  cpu_sr = 0;

#endif

 

    if (OSRunning == OS_TRUE) {   /* 如果uCos還沒(méi)有啟動(dòng)運(yùn)行,則不進(jìn)行任務(wù)調(diào)度,因?yàn)檫@時(shí)候uCos的初始化還沒(méi)完成 */

        OS_ENTER_CRITICAL();

        if (OSIntNesting > 0) {    /* 將中斷嵌套計(jì)數(shù)器減一 */                      

            OSIntNesting--;

        }

        if (OSIntNesting == 0) {   /* 只有在中斷嵌套為0,也就是即將退出中斷時(shí)才進(jìn)行任務(wù)調(diào)度 */                      

            if (OSLockNesting == 0) {                     

                OS_SchedNew();/* 找出當(dāng)前就緒表中的最高優(yōu)先級(jí)任務(wù),并更新全局變量OSPrioHighRdy */

                if (OSPrioHighRdy != OSPrioCur) {/*如果發(fā)現(xiàn)當(dāng)前最高優(yōu)先級(jí)任務(wù)不是正在運(yùn)行的任務(wù),就要進(jìn)行任務(wù)切換*/        

                    OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];

#if OS_TASK_PROFILE_EN > 0

                    OSTCBHighRdy->OSTCBCtxSwCtr++;        

#endif

                    OSCtxSwCtr++;                         

                    OSIntCtxSw(); /*調(diào)用專(zhuān)門(mén)用于中斷服務(wù)里的上下文切換函數(shù),進(jìn)行山下文的切換 */                        

                }

            }

        }

        OS_EXIT_CRITICAL();

    }

}

        從上面的源碼和代碼注釋可以總結(jié)出該函數(shù)完成的功能為:找出就緒表里的最高優(yōu)先級(jí)任務(wù),并執(zhí)行OSIntCtxSw函數(shù)來(lái)進(jìn)行任務(wù)切換。這里要注意,雖然執(zhí)行了任務(wù)切換,但不會(huì)立刻進(jìn)行上下文的切換,這個(gè)實(shí)現(xiàn)過(guò)程跟CPU的硬件有關(guān),在STM32中,上下文的切換是用的懸起中斷,該中斷只有當(dāng)CPU的所有中斷完成了,也就是退出了所有的中斷嵌套后,才會(huì)執(zhí)行。OSIntCtxSw函數(shù)是移植uCos的一個(gè)非常關(guān)鍵的函數(shù),他負(fù)責(zé)恢復(fù)運(yùn)行任務(wù)的上次中斷現(xiàn)場(chǎng),這個(gè)函數(shù)跟CPU體系有緊密的聯(lián)系,這里先不仔細(xì)分析。

        到這里我們基本可以看到uCos利用系統(tǒng)時(shí)鐘滴答實(shí)現(xiàn)多任務(wù)的大概流程如下:

uCos的多任務(wù)如何實(shí)現(xiàn)

uCos的任務(wù)切換過(guò)程就分析跟蹤完了,這里要注意幾點(diǎn),任務(wù)的切換并不只會(huì)發(fā)生在系統(tǒng)時(shí)鐘滴答的中斷服務(wù)里,調(diào)用發(fā)送信號(hào)量,發(fā)送消息等這些系統(tǒng)函數(shù)時(shí)也會(huì)引起任務(wù)切換,但大致的原理都差不多,這里我們只對(duì)程序的原理和框架做了說(shuō)明,實(shí)際還是有些細(xì)節(jié)需要處理的。

 

原文來(lái)自:博客園

您還未登錄,請(qǐng)先登錄

熱門(mén)帖子

最新帖子

?