ucos實時操作系統學習筆記——任務間通信(信號量)


      ucos實時操作系統的任務間通信有好多種,本人主要學習了sem, mutex, queue, messagebox這四種。系統內核代碼中,這幾種任務間通信機制的實現機制相似,接下來記錄一下本人對核心代碼的學習心得,供以后回來看看,不過比較遺憾的是沒有仔細學習擴展代碼的功能實現部分。ucos操作系統的內核代碼實現相對簡單,但是對理解其他操作系統內核相同功能有幫助。

      ucos的任務間通信機制主要是基於event實現的,其實理解這個event不用翻譯成中文事件,就叫event感覺還更容易接收。下面是操作系統event的的數據結構:

typedef struct os_event {
    INT8U    OSEventType;                    /* Type of event control block (see OS_EVENT_TYPE_xxxx)    */
    void    *OSEventPtr;                     /* Pointer to message or queue structure                   */
    INT16U   OSEventCnt;                     /* Semaphore Count (not used if other EVENT type)          */
    OS_PRIO  OSEventGrp;                     /* Group corresponding to tasks waiting for event to occur */
    OS_PRIO  OSEventTbl[OS_EVENT_TBL_SIZE];  /* List of tasks waiting for event to occur                */
} OS_EVENT;

      上面的event是實現sem, mutex, queue, messagebox等必不可少的結構,其實該結構相對簡單,主要包括OSEventType用於記錄當前event是前面四種機制中的哪一種;OSEventPtr是一個指針用於指向messagebox和queue要傳遞的內容的地址;OSEventCnt對於sem來說,是一個計數值,而對於mutex來說則是記錄任務優先級的一個變量;OSEventGrp和OSEventTbl和前面講過的OSRdyGrp和OSRdyTbl的類似,主要記錄有哪些任務在等待當前event的。對於上面四種機制的實現,首先需要創建一個event才可以實現各自的功能,比如說OSSemCreate,OSMutexCreate等。

       ucos的sem主要的功能是對資源使用的一種限制,當信號量的值設置n就代表允許n個任務可以對當前資源進行使用,如果資源被n個任務占用,第n+1個任務要使用該資源時,需要等待前面n個任務中有1個或者多個釋放對資源的使用權。sem的具體功能就是OSSemCreate,OSSemPend,OSSemPost等這三個函數實現的,下面將具體分析一下這三個函數。

對sem來說,首先需要創建一個event結構,然后設置這個sem event允許幾個任務可以同時使用,具體函數OSSemCreate首先如下:

OS_EVENT  *OSSemCreate (INT16U cnt)
{
    OS_EVENT  *pevent;

    if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */
        return ((OS_EVENT *)0);                            /* ... can't CREATE from an ISR             */
    }
    OS_ENTER_CRITICAL();
    pevent = OSEventFreeList;                              /* Get next free event control block        */
    if (OSEventFreeList != (OS_EVENT *)0) {                /* See if pool of free ECB pool was empty   */
        OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
    }
    OS_EXIT_CRITICAL();
    if (pevent != (OS_EVENT *)0) {                         /* Get an event control block               */
        pevent->OSEventType    = OS_EVENT_TYPE_SEM;
        pevent->OSEventCnt     = cnt;                      /* Set semaphore value                      */
        pevent->OSEventPtr     = (void *)0;                /* Unlink from ECB free list                */
        OS_EventWaitListInit(pevent);                      /* Initialize to 'nobody waiting' on sem.   */
    }
    return (pevent);
}

      上面提到的四種任務間通信機制不允許在中斷中創建,所以OSSemCreate首先判斷當前創建過程是不是在中斷中,也就是判斷OSIntNesting是否大於0,有全局變量OSEventFreeList記錄當前操作系統通還有多少個free的event在列表中,OSSemCreate從free list中取一個event機構,然后OSEventFreeList指向下一個未被使用的event,然后設置OSEventType和OSEventFreeListCnt,cnt是有create函數帶入的參數,有使用該函數的程序員自己設定,sem不使用OSEventPtr變量,因為是指針,所以設置為0,之后初始化event的event group和 event table,表示當前沒有任務等待創建的sem,返回event結構的地址,給之后講的pend和post使用。

      其次,就要講到sem機制post和pend的兩個操作了,這兩個操作是成對使用的,簡單點講pend主要功能是檢查create的event中的OSEventCnt是否大於0,如果大於0,說明需要保護的資源還允許任務使用,這時候只需要將OSEventCnt減1操作,表示又有一個任務占用了資源的使用權;如果OSEventCnt不大於0,則說明任務可以使用的資源的權限已經達到上限,這時候就要把要使用的該資源的任務掛起(1),讓其處於等待狀態,並把任務放到event的event group和event table中,處於等待狀態的任務只能等其他任務釋放對資源的使用權之后才可以繼續運行。post的主要功能是要釋放被占用的資源的使用權,其操作首先會檢查當前event的group和table中有沒有在等待當前event的任務存在,如果有使用權直接轉給等待的任務(1),不需要對OSEventCnt加操作,如果沒有等待當前event的任務,則只需要將OSEventCnt加1操作,說明資源的可使用權又大了一個。接下來我們看一下具體pend和post的代碼具體流程。

void  OSSemPend (OS_EVENT  *pevent,
                 INT32U     timeout,
                 INT8U     *perr)
{
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   /* Validate event block type                     */
        *perr = OS_ERR_EVENT_TYPE;
        return;
    }
    if (OSIntNesting > 0u) {                          /* See if called from ISR ...                    */
        *perr = OS_ERR_PEND_ISR;                      /* ... can't PEND from an ISR                    */
        return;
    }
    OS_ENTER_CRITICAL();
(1)===================================================================================================
if (pevent->OSEventCnt > 0u) { /* If sem. is positive, resource available ... */ pevent->OSEventCnt--; /* ... decrement semaphore only if positive. */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return; } (2)===================================================================================================
/* Otherwise, must wait until event occurs */ OSTCBCur->OSTCBStat |= OS_STAT_SEM; /* Resource not available, pend on semaphore */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; OSTCBCur->OSTCBDly = timeout; /* Store pend timeout in TCB */ OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */ OS_EXIT_CRITICAL(); OS_Sched(); /* Find next highest priority task ready */ OS_ENTER_CRITICAL(); switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */ case OS_STAT_PEND_OK: *perr = OS_ERR_NONE; break; case OS_STAT_PEND_ABORT: *perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */ break; case OS_STAT_PEND_TO: default: OS_EventTaskRemove(OSTCBCur, pevent); *perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get event within TO */ break; } OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */ OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* Clear event pointers */ OS_EXIT_CRITICAL();
(3)===================================================================================================== }

      OSSemPend函數首先判斷當前的任務操作是否是一個sem event;然后判斷是否中斷中,如果是則返回錯誤;之后檢查該sem event的資源限號量是否大於0,如果是則進行減操作表示信號量被一個任務占用,如果不大於0,說明對改event來說,已經沒有信號量供使用需要的操作是掛起當前任務,並調用任務切換函數,在上面的代碼中在OS_Sched()上面的四步操作是掛起任務操作,直到有可以使用的信號量再次執行任務切換時,會切換到當前任務繼續從OS_Sched開始執行,可以看出下面的操作是從event的等待group和table中刪除該任務,並且改變任務的狀態為OS_STAT_RDY。當然任務的還有statpend狀態,在此不做詳細介紹。

INT8U  OSSemPost (OS_EVENT *pevent)
{
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   /* Validate event block type                     */
        return (OS_ERR_EVENT_TYPE);
    }
    OS_ENTER_CRITICAL();
(1)=====================================================================================================
if (pevent->OSEventGrp != 0u) { /* See if any task waiting for semaphore */ /* Ready HPT waiting on event */ (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK); OS_EXIT_CRITICAL(); OS_Sched(); /* Find HPT ready to run */ return (OS_ERR_NONE); }
(2)====================================================================================================
if (pevent->OSEventCnt < 65535u) { /* Make sure semaphore will not overflow */ pevent->OSEventCnt++; /* Increment semaphore count to register event */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); } OS_EXIT_CRITICAL(); /* Semaphore value has reached its maximum */ return (OS_ERR_SEM_OVF);
(3)=================================================================================================== }

      OSSemPost函數實現相對簡單,在判斷完當前操作是否是sem event之后,就判斷當前的event等待group中是否有等待該event的任務,如果有就設置等待該event的任務擁有該信號量的使用權限設置改任務處於RDY狀態然后執行任務調度,讓等待的任務得以執行,在此有一個巧妙地設計就是對於信號量的計量值OSEventCnt不做操作,因為如果event等待group中有等待任務的話,意味着在此釋放信號量,等待任務就獲得了信號量。如果在等待group中沒有等待任務的話,就會給OSEventCnt做加操作,表示有一個任務釋放了該信號量。在此說明的是該函數主要的操作在OS_EventTaskRdy中,其主要功能就是把event的等待group和table中的任務解析出來放到任務Rdy列表中,並把event中的等待任務從列表中刪除。

      如果信號量的值設置為1的話,就是一個特殊情況,表示只有1個任務擁有信號量,有點互斥的意味,但是與互斥鎖不同的是,互斥鎖為了防止低優先級占有資源,卻因為優先級的低不被執行,而高優先級得不到被低優先級占有的資源而不能執行,使用了優先級繼承機制。之后會介紹ucos互斥鎖的內核實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM