從零開始學習UCOSII操作系統8--互斥型信號量

從零開始學習UCOSII操作系統8--互斥型信號量

1、互斥型信號量的定義

(1)任務可以用互斥型信號量實現對共享資源的獨佔式處理,互斥型信號量也稱爲mutex,mutex是二值信號量,不但具有UCOSII普通信號量的機制外,還具有其他的一些特性。

(2)最重要的一點是,可以解除優先級反轉的問題。當高優先級的任務需要使用某個共享資源的時候,而該資源已被一個低優先級反轉的問題,就會發生優先級反轉的問題。對於這個問題:內核將低優先級提升到高於那個高優先級的任務,直到低優先級的任務使用完佔用的共享資源。

2、互斥型信號量的使用原理

(1)建立一個互斥型信號量OSMutexCreate()
在使用mutex之前,必須建立它,建立mutex是通過調用函數OSMutexCreate()來完成的。

OS_EVENT  *OSMutexCreate (INT8U   prio,
                          INT8U  *perr)
{
    OS_EVENT  *pevent;

#if OS_ARG_CHK_EN > 0u
    if (prio >= OS_LOWEST_PRIO) {                          /* 如果優先級大於最低的優先級                           */
        *perr = OS_ERR_PRIO_INVALID;
        return ((OS_EVENT *)0);
    }
#endif
    if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */
        *perr = OS_ERR_CREATE_ISR;                         /*不能在一箇中斷中設置互斥型信號量*/
        return ((OS_EVENT *)0);
    }
    OS_ENTER_CRITICAL();
    if (OSTCBPrioTbl[prio] != (OS_TCB *)0) {               /* 互斥型信號量優先級存在  */
        OS_EXIT_CRITICAL();                                /* Task already exist at priority ...       */
        *perr = OS_ERR_PRIO_EXIST;                         /* ... inheritance priority                 */
        return ((OS_EVENT *)0);
    }
    OSTCBPrioTbl[prio] = OS_TCB_RESERVED;                  /* Reserve the table entry                  */
    pevent             = OSEventFreeList;                  /* 得到一個空閒的鏈表   */
    if (pevent == (OS_EVENT *)0) {                         /* See if an ECB was available              */
        OSTCBPrioTbl[prio] = (OS_TCB *)0;                  /* No, Release the table entry              */
        OS_EXIT_CRITICAL();
        *perr              = OS_ERR_PEVENT_NULL;           /* No more event control blocks             */
        return (pevent);
    }
    OSEventFreeList        = (OS_EVENT *)OSEventFreeList->OSEventPtr;   /* Adjust the free list        */
    OS_EXIT_CRITICAL();
    pevent->OSEventType    = OS_EVENT_TYPE_MUTEX;
    pevent->OSEventCnt     = (INT16U)((INT16U)prio << 8u) | OS_MUTEX_AVAILABLE; /* Resource is avail.  */
    pevent->OSEventPtr     = (void *)0;                                 /*初始化爲沒有任何的任務擁有這個任務   */

    OS_EventWaitListInit(pevent);
    *perr                  = OS_ERR_NONE;
    return (pevent);
}

(2)等待一個互斥型信號量(掛起),OSMutexPend()

使用前面的計數信號量。
1、不支持嵌套操作。

2、不支持所有者,任意的任務都能發送釋放。

3、無法解決優先級反轉的問題。

對於請求信號量的操作有以下的幾種操作:

(1)當信號量已經被自己佔有的時候,再次請求,就+1
(2)當有一個高優先級的任務已經佔有這個信號量的時候,
現在有一個低優先級的任務進來了,顯然這個任務是需要進行等待的。
(3)當有一個低優先級的任務已經佔有這個信號量的時候,
現在有一個高優先級的任務進來了,顯然這個時候,低優先級的任務需要暫停的提高到高優先級的任務中來。

互斥型信號量的請求:
參數定義:
(1)參數1:OS_EVENT pevent :定義一個指針,類型是指向這個事件控制塊的指針。
(2)參數2:timeout:超時等待
(3)參數3:
perr :返回的輸出型參數:代表出錯的類型

void  OSMutexPend (OS_EVENT  *pevent,
                   INT32U     timeout,
                   INT8U     *perr)
{
    INT8U      pip;                                        /* Priority Inheritance Priority (PIP)      */
    INT8U      mprio;                                      /* Mutex owner priority                     */
    BOOLEAN    rdy;                                        /* Flag indicating task was ready           */
    OS_TCB    *ptcb;
    OS_EVENT  *pevent2;
    INT8U      y;

/*各種創建的時候的異常的代碼*/

    OS_ENTER_CRITICAL();
    pip = (INT8U)(pevent->OSEventCnt >> 8u);              
         /* 該信號量繼承的優先級*/

     /* 當互斥信號量還沒有被佔用的時候 */
    if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) 
    {
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;       
        /* 直接以當前的優先級佔用它 */

        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;   

         /*  保存擁有者的優先級    */
        pevent->OSEventPtr  = (void *)OSTCBCur;            

        /*    事件控制塊的指針,指向任務控制塊,把任務掛起到事件鏈表中執行   */

        if (OSTCBCur->OSTCBPrio <= pip) 
        {                  
        /*  PIP的優先級必須更小,不然的話,就會發生優先級反轉 */
            OS_EXIT_CRITICAL();                            /*      ... than current task!              */
            *perr = OS_ERR_PIP_LOWER;
        } 
        else 
        {
            OS_EXIT_CRITICAL();
            *perr = OS_ERR_NONE;
        }
        return;
    }
    mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); 
     /* 佔用該信號量的擁有者的任務優先級   */

    //事件控制塊的指針
    ptcb  = (OS_TCB *)(pevent->OSEventPtr);        

     /*  佔用該信號量的任務的TCB  */
    if (ptcb->OSTCBPrio > pip)
     {                                 
         /*  如果當前所有者優先級比繼承的優先級大*/
        if (mprio > OSTCBCur->OSTCBPrio)
        {
            y = ptcb->OSTCBY;

            /*並且互斥型信號量擁有者處於就緒狀態的話,那麼就需要將其從就緒態中移除*/

            if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0u) {          /*     See if mutex owner is ready   */
                OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;   

                /* 把優先級進行反轉之後移除現在佔有該信號量的任務 */
                if (OSRdyTbl[y] == 0u) 
                {                          
                    /* ... list at current prio */
                    OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
                }
                rdy = OS_TRUE;
            } 
            else  /*佔有該信號量的任務現在在事件的等待列表中*/
            {
                pevent2 = ptcb->OSTCBEventPtr;
                if (pevent2 != (OS_EVENT *)0)
                 {                  
                     /* 從事件列表中移除此任務       */
                    y = ptcb->OSTCBY;
                    pevent2->OSEventTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;
                    if (pevent2->OSEventTbl[y] == 0u) {
                        pevent2->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
                    }
                }
                rdy = OS_FALSE;                          
            }

            ptcb->OSTCBPrio = pip;   

 /* Change owner task prio to PIP            */

#if OS_LOWEST_PRIO <= 63u
            ptcb->OSTCBY    = (INT8U)( ptcb->OSTCBPrio >> 3u);
            ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x07u);
#else
            ptcb->OSTCBY    = (INT8U)((INT8U)(ptcb->OSTCBPrio >> 4u) & 0xFFu);
            ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x0Fu);
#endif
            ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);
            ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);

            if (rdy == OS_TRUE) 
            {                          
            /*如果之前佔有該信號量的任務已經接近於就緒態了
                就將提升之後的優先級加入到就緒表中*/
                OSRdyGrp               |= ptcb->OSTCBBitY; /* ... make it ready at new priority.       */
                OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
            } 
            else 
            {
                pevent2 = ptcb->OSTCBEventPtr;
                if (pevent2 != (OS_EVENT *)0) {           
             /* Add to event wait list                   */
                    pevent2->OSEventGrp               |= ptcb->OSTCBBitY;
                    pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                }
            }
            OSTCBPrioTbl[pip] = ptcb;
        }
    }
    OSTCBCur->OSTCBStat     |= OS_STAT_MUTEX;        
     /* Mutex not available, pend current task        */
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;
    OSTCBCur->OSTCBDly       = timeout;               
     /* Store timeout in current task's TCB           */
    OS_EventTaskWait(pevent);                         
    /* Suspend task until event or timeout occurs    */
    OS_EXIT_CRITICAL();
    OS_Sched();                                       
    /*  因爲已經切換任務了,查找最高優先級的任務    */
    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 getting mutex        */
             break;

        case OS_STAT_PEND_TO:
        default:
             OS_EventTaskRemove(OSTCBCur, pevent);
             *perr = OS_ERR_TIMEOUT;                  /* Indicate that we didn't get mutex 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                          */
#if (OS_EVENT_MULTI_EN > 0u)
    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
    OS_EXIT_CRITICAL();
}

互斥型信號量釋放的操作函數:

只有在同一個任務中請求互斥型函數,才能同一個任務中釋放互斥型函數。

INT8U  OSMutexPost (OS_EVENT *pevent)
{
    INT8U      pip;                                   
    /* Priority inheritance priority                 */
    INT8U      prio;

    /* 異常處理 */

    OS_ENTER_CRITICAL();
    pip  = (INT8U)(pevent->OSEventCnt >> 8u);        
     /*得到互斥型信號量的優先級 */
    prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); 
     /* 得到擁有者原來的優先級   */
    if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr)
     {   
        /* 進入到這個函數中的話,說明請求互斥型信號量的操作,被其他的任務釋放,這是不符合互斥型信號量的,返回一個錯誤值 */
        OS_EXIT_CRITICAL();
        return (OS_ERR_NOT_MUTEX_OWNER);
    }
    if (OSTCBCur->OSTCBPrio == pip) 
    {                 
        /* 查看佔用mutex的任務的優先級是否已經升到了PIP,因爲有一個更高的優先級的任務也需要整個mutex,在這種情況下,佔用mutex的任務被將回到原來的優先級 */
        OSMutex_RdyAtPrio(OSTCBCur, prio);            

    }
    OSTCBPrioTbl[pip] = OS_TCB_RESERVED;             
         /* Reserve table entry                           */
    /*如果互斥性信號量存在的話*/
    if (pevent->OSEventGrp != 0u) 
    {                   
        /* 有沒有其他的任務在等待這個信號量 */

         /* Yes, Make HPT waiting for mutex ready         */
        prio                = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;  
        /* 保存新的擁有者的優先級  */

        pevent->OSEventCnt |= prio;
        pevent->OSEventPtr  = OSTCBPrioTbl[prio];  

        /* Link to new mutex owner's OS_TCB         */
        if (prio <= pip) 
        {                           
         /*      PIP 'must' have a SMALLER prio ...       */
            OS_EXIT_CRITICAL();                       
        /*      ... than current task!                   */
            OS_Sched();                               
        /*      Find highest priority task ready to run  */
            return (OS_ERR_PIP_LOWER);
        } 
        else
         {
            OS_EXIT_CRITICAL();
            OS_Sched();                              
     /*      Find highest priority task ready to run  */
            return (OS_ERR_NONE);
        }
    }
    pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;         /* No,  Mutex is now available                   */
    pevent->OSEventPtr  = (void *)0;
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}

(3)無等待的獲取互斥型信號量(任務不掛起)OSMutexAccept();

相當於查詢的功能:
參數1:事件控制塊的指針:
參數2:範回的錯誤值:

BOOLEAN  OSMutexAccept (OS_EVENT  *pevent,
                        INT8U     *perr)
{
    INT8U      pip;                                   

    /*  異常處理  */

    OS_ENTER_CRITICAL();                             
    /* Get value (0 or 1) of Mutex                  */

    pip = (INT8U)(pevent->OSEventCnt >> 8u);         
      /*   得到互斥型信號量的優先級     */

    if ((pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) 
    {
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;   

/*      Mask off LSByte (Acquire Mutex)         */
        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;     

/* 如果互斥型信號量有效,那麼OSMutex把調用該函數任務的優先級寫到
OSEventCnt的低8位,並將mutex的事件控制塊ECB鏈接到該任務的任務控制塊。  */
        pevent->OSEventPtr  = (void *)OSTCBCur;        

/*      Link TCB of task owning Mutex           */
        if (OSTCBCur->OSTCBPrio <= pip) 
        {              

/*      PIP 'must' have a SMALLER prio ...      */
            OS_EXIT_CRITICAL();                       

     /*      ... than current task!                  */
            *perr = OS_ERR_PIP_LOWER;
        } 
        else 
        {
            OS_EXIT_CRITICAL();
            *perr = OS_ERR_NONE;
        }
        return (OS_TRUE);
    }
    OS_EXIT_CRITICAL();
    *perr = OS_ERR_NONE;
    return (OS_FALSE);
}
發佈了89 篇原創文章 · 獲贊 86 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章