ucos另一種任務間通信的機制是消息(mbox),個人感覺是它是queue中只有一個信息的特殊情況,從代碼中可以很清楚的看到,因為之前有關於queue的學習筆記,所以一並講一下mbox。為什么有了queue機制還要用mbox呢,只要設置queue的msg只有一個不就行了?其實很簡單,就是為了節約資源,因為使用queue的話需要專門描述queue的機構體os_q,同時需要分配一段內存用來存放msg,而如果直接使用mbox機制的話,就好多了,節約。。。。。
首先從mbox的創建開始,mbox創建的函數是OSMboxCreate ,簡化代碼如下:
OS_EVENT *OSMboxCreate (void *pmsg) { OS_EVENT *pevent; if (OSIntNesting > 0) { /* 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) { pevent->OSEventType = OS_EVENT_TYPE_MBOX; pevent->OSEventCnt = 0; pevent->OSEventPtr = pmsg; /* Deposit message in event control block */ OS_EventWaitListInit(pevent); } return (pevent); /* Return pointer to event control block */ }
mbox使用同樣使用event機制,它與sem的不同之處在於sem使用event中OSEventCnt變量存放信號量,而mbox使用OSEventPtr存放創建時候的msg地址,可以對比sem的create代碼,兩者的create代碼如此相似。
void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr) { void *pmsg; if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* Validate event block type */ *perr = OS_ERR_EVENT_TYPE; return ((void *)0); } if (OSIntNesting > 0) { /* See if called from ISR ... */ *perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */ return ((void *)0); } if (OSLockNesting > 0) { /* See if called with scheduler locked ... */ *perr = OS_ERR_PEND_LOCKED; /* ... can't PEND when locked */ return ((void *)0); } OS_ENTER_CRITICAL(); pmsg = pevent->OSEventPtr; if (pmsg != (void *)0) { /* See if there is already a message */ pevent->OSEventPtr = (void *)0; /* Clear the mailbox */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (pmsg); /* Return the message received (or NULL) */ } OSTCBCur->OSTCBStat |= OS_STAT_MBOX; /* Message not available, task will pend */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; OSTCBCur->OSTCBDly = timeout; /* Load timeout in TCB */ OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */ OS_EXIT_CRITICAL(); OS_Sched(); /* Find next highest priority task ready to run */ OS_ENTER_CRITICAL(); switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */ case OS_STAT_PEND_OK: pmsg = OSTCBCur->OSTCBMsg; *perr = OS_ERR_NONE; break; case OS_STAT_PEND_ABORT: pmsg = (void *)0; *perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */ break; case OS_STAT_PEND_TO: default: OS_EventTaskRemove(OSTCBCur, pevent); pmsg = (void *)0; *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 */ OSTCBCur->OSTCBMsg = (void *)0; /* Clear received message */ OS_EXIT_CRITICAL(); return (pmsg); /* Return received message */ }
因為好於其他的機制代碼對比,所以將整個代碼貼出來,其實主要的差別在黃色部分,因為在創建msg的時候,或者在post msg的時候,會將msg放到OSEventPtr,所以直接從中取出msg,判斷當前的msg是否存在,如果存在的話就會將msg返回並且對msgbox即OSEventPtr清0操作;如果msg沒有則直接將當前任務掛起處於event的等待列表中,進行任務調度也就是黃色部分下面的處理過程,不做過多的贅述。
INT8U OSMboxPost (OS_EVENT *pevent, void *pmsg) { if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* Validate event block type */ return (OS_ERR_EVENT_TYPE); } OS_ENTER_CRITICAL();
(1)==================================================================================================== if (pevent->OSEventGrp != 0) { /* See if any task pending on mailbox */ /* Ready HPT waiting on event */ (void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK); OS_EXIT_CRITICAL(); OS_Sched(); /* Find highest priority task ready to run */ return (OS_ERR_NONE); }
(2)==================================================================================================== if (pevent->OSEventPtr != (void *)0) { /* Make sure mailbox doesn't already have a msg */ OS_EXIT_CRITICAL(); return (OS_ERR_MBOX_FULL); } pevent->OSEventPtr = pmsg; /* Place message in mailbox */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE);
(3)==================================================================================================== }
mbox的post函數代碼如上所示,其實處理過程和sem等類似,post其實就是填充的功能,首先判斷有沒有在等待當前event的任務存在,如果有的話直接將msg等交給等待任務,如果沒有則進入到第三部分,判斷當前的OSEventPtr中是否有msg,如果有表示沒有任務從中取出msg,這時會返回錯誤,如果OSEventPtr中沒有msg,則將msg放到OSEventPtr中,等待任務從中取出。
通過學習sem,mutex,queue以及mbox的內核實現可以大體的了解操作系統任務間的通信機制是如何實現的,也可以和其他的操作系統作對別,通過簡單的內核實現取理解復雜的操作系統該部分的實現機制,比如所Linux操作系統,這是一個很好的學習方法。下面簡單總結一下這四中通信機制的功能:
1. 信號量:信號量其實就是一種通過數字大小來實現限制資源使用的一種機制,設置信號量其實就是設置資源最大可以允許多少個任務同時訪問同一個資源,通過信號量pend和post操作即信號量變量的加減實現任務控制,當特殊情況,只有一個信號量的時候就有點互斥的意思。
2. 互斥鎖:互斥鎖就是同一時間只有一個任務可以占有資源,當有其他任務要訪問資源的時候就會將這個任務掛起,放到event的等待列表中,當占有資源的任務釋放掉鎖的時候,等待任務才可以占有資源並且上鎖,為了防止優先級的翻轉,使用了優先級繼承的機制,就是把占有資源的任務的優先級提升一下比要使用資源的任務的優先級高。
3. 隊列:隊列就是取一段內存用於存放消息,這個消息是一個地址,真正的消息內容是存放在這個地址中,這樣的話可以就可以實現真正的任務間通信,將數據從一個任務傳到另一個任務,而不像信號量和互斥鎖一下僅僅是一個限制作用。隊列使用要注意,如果多個任務在等待不同的消息的話,有可能會出現不同任務獲得了不是自己想要的信息並且將消息從隊列中去除掉了,所以使用的時候需要注意。
4. 消息:消息其實是隊列的一種特殊情況,為了節省資源,之前也有講到,如果消息數量一定的話同一時間只有一個消息使用,那么采用消息機制更簡單,同樣實現了數據的傳輸功能。消息的使用也同樣要注意,pend和post的使用,因為如果有多個任務同時使用的話,就會存在是否是當前任務想要的信息,如果不是的話有可能把別的任務的消息給去處並釋放掉了,所以使用時需要注意。
