uC/OS-II的任務同步與通訊
在多工合作過程中的,作業系統應解決兩個問題:一是各任務之間應具有一種互斥關係,即對於某個共享資源的共享,如果一個任務正在使用,則其他任務只能等待,等到該任務釋放該資源以後,等待的任務之一才能使用它;二是相關的任務在執行上要有先後次序,一個任務要等其夥伴發來通知或建立了某個條件後才能繼續執行,否則只能等待。
任務之間的這種制約性的合作執行機制叫做任務間的同步。
事件
通訊就要依賴中間媒介。在uC/OS-II中,使用訊號量,郵箱(訊息郵箱)和訊息佇列這些資料結構來作為中間媒介。由於這些資料結構將要影響到任務的程式流程,所以它們也被稱為事件。
把資訊傳送到事件上的操作叫做傳送事件,讀取事件的操作叫做請求事件
訊號量
訊號量是一類用來進行任務間通訊的最基本事件。由於二值事件可以實現共享資源的獨佔式佔用,所以叫做互斥型訊號量。計數式的訊號叫做訊號量。
給等待訊號量的任務設定一個等待時限。若等待訊號量的任務因等待時間已超過這個時限卻還未等到這個訊號,則令該任務脫離等待狀態而繼續執行,這樣就不會出現宕機現象。
在嚴格按照優先級別進行排程的可剝奪核心中,優先級別決定了任務能否獲得處理器的使用權,而能否獲得訊號量則決定它能否被執行。也就是說,在使用了訊號量進行同步的任務中,制約任務能否執行的條件有兩個:一個是它的優先級別;另一個是它是否獲得了它正在等待的訊號量。正是這個事實,產生了不得不想辦法解決的優先順序反轉問題
訊息郵箱
在多工作業系統中,常常需要通過傳遞一個數據(這種資料叫做訊息)的方式來進行任務之間的通訊。為了達到這個目的,可以在記憶體中建立一個儲存空間作為該資料的緩衝區。如果把這個緩衝區叫做訊息緩衝區,那麼在任務間傳遞資料(訊息)的一個最簡單的方法就是傳遞訊息緩衝區。於是,用來傳遞訊息緩衝區指標的資料結構(事件)就叫做訊息郵箱。
訊息佇列
上面的訊息郵箱不僅可用來傳遞一個訊息,而且也可以定義一個指標陣列。讓陣列的每個元素都存放一個訊息緩衝區指標,那麼任務就可以通過傳遞這個指標陣列指標的方法來傳遞多個訊息了。這種可以傳遞多個訊息的資料結構叫做訊息佇列。
事件的等待任務列表
作為功能完善的事件,應有對這些等待任務具有兩方面的管理功能:一是要對等待事件的所有任務進行記錄並排序;二是允許任務有一定的等待時限。
對於等待事件任務的記錄和排序,uC/OS-II採用了與任務就緒表類似的方法,定義一個INT8U型別的陣列OSEventTbl[]作為記錄等待事件任務的記錄表,這個表叫做等待任務表。也定義一個INT8U的變數OSEventGrp來表示等待任務表中的任務組。
至於等待任務的等待時限,則記錄在等待任務的任務控制塊TCB的成員OSTCBDly中,並在每個時鐘節拍中斷服務程式中對該資料進行維護。每當有任務的等待時限已到時,則將該任務從事件等待任務表中刪除,並使它進入就緒狀態。
事件控制塊
uC/OS-II把事件等待任務表和與事件相關的其它資訊組合起來定義一個叫做事件控制塊ECB的資料結構。這樣在uC/OS-II中統一採用ECB來描述注入訊號量,郵箱(訊息郵箱)和訊息佇列這些事件。
在uC/OS-II.H中,使勁按控制塊的定義如下:
#if (OS_MAX_EVENTS >= 2)
typedef struct {
void*OSEventPtr;/* Pointer to message or queue structure*/
INT8UOSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur*/
INT16UOSEventCnt;/* Count of used when event is a semaphore*/
INT8UOSEventType;/* OS_EVENT_TYPE_MBOX, OS_EVENT_TYPE_Q or OS_EVENT_TYPE_SEM */
INT8UOSEventGrp;/* Group corresponding to tasks waiting for event to occur*/
} OS_EVENT;
#endif
成員OSEventTbl[OS_EVENT_TBL_SIZE]是一個數組,與任務就緒表的格式一樣。應用程式中的所有任務按照優先級別各自在表中佔據一個二進位制位,並用該位的值是1還是0來表示該位對應的任務是否為正在等待事件的任務,這個表被叫做任務等待表。
事件控制塊的基本函式操作
uC/OS-II有4個對事件控制塊進行基本操作的函式(定義在檔案OS_CORE.C中),以供操作訊號量,訊息郵箱,訊息佇列等事件的函式來呼叫。
1)事件控制塊的初始化函式
#if(OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN
voidOSEventWaitListInit (OS_EVENT *pevent)//指向事件控制塊的指標
該函式將在任務呼叫OS***Create()函式建立事件時,被OS***Create()函式所呼叫。這裡***代表:Sem,Mutex,Mbox,Q。
2)使一個任務進入等待狀態的函式
把一個任務置於等待狀態要呼叫OSEventTaskWait()函式。
#if(OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN
voidOSEventTaskWait (OS_EVENT *pevent)
該函式將在任務呼叫函式OS***Pend()請求一個事件時,被OS***Pend()所呼叫。
3)使一個正在等待任務進入就緒狀態的函式
呼叫函式OSEventTaskRdy(),把呼叫這個函式的任務在任務等待表中的位置清0(解除等待狀態)後,再把任務在任務就緒表中的對應位置1,然後引發一次任務排程。
#if(OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN
voidOSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
該函式將在任務呼叫函式OS***Post()傳送一個事件時,被函式OS***Post()呼叫。
4)使一個等待超時的任務進入就緒狀態的函式
如果一個正在等待事件的任務已經超過了等待的時間,卻仍由於麼有獲取事件等原因而具備可以執行的條件,卻又要使它進入就緒狀態,這時要呼叫函式OSEventTO()。
#if(OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN
voidOSEventTO (OS_EVENT *pevent)
該函式將在任務呼叫OS***Pend()請求一個事件時,被OS***Pend()所呼叫。
空事件控制塊連結串列
與管理任務控制塊的方法類似,uC/OS-II把事件控制塊也組織成為兩條連結串列來管理。
在uC/OS-II初始化時,系統會在初始化函式OSInit()中按照應用程式使用事件的總數OS_MAX_EVENTS(OS_CFG.H),建立OS_MAX_EVENTS個空事件塊,並借用成員OSEventPtr作為連結指標,組成單向連結串列。