ucos II 任務間 通訊之四:郵箱

來源:互聯網
上載者:User

 

                   ucos II 任務間 通訊之四:郵箱

   學過訊號量之後再來看郵箱,發現他們是非常相似的,甚至有時候郵箱可以當做訊號量來使用,郵箱相對訊號量而言,只是多傳遞了一個指標變數。

和訊號量很相似,ucos II提供了5個對郵箱進行操作的函數。它們是:

1. 建立一個郵箱,OSMboxCreate()

2. 等待一個郵箱中的訊息,OSMboxPend()

3. 發送一個訊息到郵箱中,OSMboxPost()

4. 無等待地從郵箱中得到一個訊息, OSMboxAccept()

5. 查詢一個郵箱的狀態, OSMboxQuery()

使用郵箱之前,必須先建立該郵箱。該操作可以通過調用OSMboxCreate()函數來完成,並且要指定指標的初始值。一般情況下,這個初始值是NULL,但也可以初始化一個郵箱,使其在最開始就包含一條訊息。如果使用郵箱的目的是用來通知任務某一個事件已經發生(發送一條訊息),那麼就要初始化該郵箱為NULL。如果使用者用郵箱來共用某些資源,那麼就要初始化該郵箱為一個非NULL的指標。在這種情況下,郵箱被當成一個二值訊號量使用。

 下面來看看建立一個郵箱函數的實現代碼:

OS_EVENT *OSMboxCreate (void *msg)

{

    OS_EVENT *pevent;

 

 

    OS_ENTER_CRITICAL();

    pevent = OSEventFreeList;

    if (OSEventFreeList != (OS_EVENT *)0) {

        OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;

    }

    OS_EXIT_CRITICAL();

    if (pevent != (OS_EVENT *)0) {

        pevent->OSEventType = OS_EVENT_TYPE_MBOX;                      (1)

        pevent->OSEventPtr  = msg;                                     (2)

        OSEventWaitListInit(pevent);

    }

    return (pevent);                                                   (3)

}

仔細看看,其實和建立一個訊號量的過程幾乎是一樣的,先申請一個空事件控制塊,接著初始化這個事件控制塊。最後返回一個指向這個事件控制塊的指標。不同之處在於事件控制塊的類型被設定成OS_EVENT_TYPE_MBOX[ (1)],以及使用.OSEventPtr域來容納訊息指標。

接著來看看等待郵箱函數實現代碼:

void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)

{

    void  *msg;

 

    OS_ENTER_CRITICAL();

    if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {                   (1)

        OS_EXIT_CRITICAL();

        *err = OS_ERR_EVENT_TYPE;

        return ((void *)0);

    }

    msg = pevent->OSEventPtr;

    if (msg != (void *)0) {                                            (2)

        pevent->OSEventPtr = (void *)0;                                (3)

        OS_EXIT_CRITICAL();

        *err = OS_NO_ERR;

    } else if (OSIntNesting > 0) {                                     (4)

        OS_EXIT_CRITICAL();

        *err = OS_ERR_PEND_ISR;

    } else {

        OSTCBCur->OSTCBStat |= OS_STAT_MBOX;                           (5)

        OSTCBCur->OSTCBDly   = timeout;

        OSEventTaskWait(pevent);

        OS_EXIT_CRITICAL();

        OSSched();

        OS_ENTER_CRITICAL();

        if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) {                 (6)

            OSTCBCur->OSTCBMsg      = (void *)0;

            OSTCBCur->OSTCBStat     = OS_STAT_RDY;

            OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;

            OS_EXIT_CRITICAL();

            *err                    = OS_NO_ERR;

        } else if (OSTCBCur->OSTCBStat & OS_STAT_MBOX) {               (7)

            OSEventTO(pevent);                                         (8)

            OS_EXIT_CRITICAL();

            msg                     = (void *)0;                       (9)

            *err                    = OS_TIMEOUT;

        } else {

            msg                     = pevent->OSEventPtr;             (10)

            pevent->OSEventPtr      = (void *)0;                            (11)

            OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;                  (12)

            OS_EXIT_CRITICAL();

            *err                    = OS_NO_ERR;

        }

    }

    return (msg);

}

同樣,它和OSSemPend()也很相似,說白了就是先看有沒有有用的訊息,要是沒有,就把該任務掛起來。

 OSMboxPend()首先檢查該事件控制塊是由OSMboxCreate()函數建立的[ (1)]。當.OSEventPtr域是一個非NULL的指標時,說明該郵箱中有可用的訊息[ (2)]。這種情況下,OSMboxPend()函數將該域的值複製到局部變數msg中,然後將.OSEventPtr置為NULL[ (3)]。這正是我們所期望的,也是執行OSMboxPend()函數最快的路徑。

如果此時郵箱中沒有訊息是可用的(OSEventPtr域是NULL指標),OSMboxPend()函數檢查它的調用者是否是中斷服務子程式[ (4)]。象OSSemPend()函數一樣,不能在中斷服務子程式中調用OSMboxPend(),因為中斷服務子程式是不能等待的。這裡的代碼同樣是為了以防萬一。但是,如果郵箱中有可用的訊息,即使從中斷服務子程式中調用OSMboxPend()函數,也一樣是成功的。

如果郵箱中沒有可用的訊息,OSMboxPend()的調用任務就被掛起,直到郵箱中有了訊息或者等待逾時[ (5)]。當有其它的任務向該郵箱發送了訊息後(或者等待時間逾時),這時,該任務再一次成為最高優先順序任務,OSSched()返回。這時,OSMboxPend()函數要檢查是否有訊息被放到該任務的任務控制塊中[ (6)]。如果有,那麼該次函數調用成功,對應的訊息被返回到調用函數。

發送一個訊息到郵箱中OSMboxPost()的代碼如下:

INT8U OSMboxPost (OS_EVENT *pevent, void *msg)

{

    OS_ENTER_CRITICAL();

    if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {                   (1)

        OS_EXIT_CRITICAL();

        return (OS_ERR_EVENT_TYPE);

    }

    if (pevent->OSEventGrp) {                                          (2)

        OSEventTaskRdy(pevent, msg, OS_STAT_MBOX);                     (3)

        OS_EXIT_CRITICAL();

        OSSched();                                                     (4)

        return (OS_NO_ERR);

    } else {

        if (pevent->OSEventPtr != (void *)0) {                         (5)

            OS_EXIT_CRITICAL();

            return (OS_MBOX_FULL);

        } else {

            pevent->OSEventPtr = msg;                                  (6)

            OS_EXIT_CRITICAL();

            return (OS_NO_ERR);

        }

    }

}

發送一個訊息到郵箱和發送一個訊號量也很相似,就是查看有沒有任務在等待這個訊息,如果有就把那個任務從睡眠態拉回就緒態。

代碼的詳細解釋如下:

檢查了事件控制塊是否是一個郵箱後[ (1)],OSMboxPost()函數還要檢查是否有任務在等待該郵箱中的訊息[ (2)]。如果事件控制塊中的OSEventGrp域包含非零值,就暗示著有任務在等待該訊息。這時,調用OSEventTaskRdy()將其中的最高優先順序任務從等待列表中刪除[ (3)],加入系統的就緒工作清單中,準備運行。然後,調用OSSched()函數[ (4)],檢查該任務是否是系統中最高優先順序的就緒任務。如果是,執行任務切換[僅當OSMboxPost()函數是由任務調用時],該任務得以執行。如果該任務不是最高優先順序的任務,OSSched()返回,OSMboxPost()的調用函數繼續執行。如果沒有任何任務等待該訊息,指向訊息的指標就被儲存到郵箱中[
(6)](假設此時郵箱中的指標不是非NULL的[ (5)])。這樣,下一個調用OSMboxPend()函數的任務就可以立刻得到該訊息了。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.