任務的同步與通信
任務間的同步
在多任務合作工作過程中,操作系統要解決兩個問題:
- 各任務間應該具有一種互斥關系,即對某些共享資源,如果一個任務正在使用,則其他任務只能等待,等到該任務釋放資源后,等待任務之一才能使用它;
- 相關的任務在執行上要有先后次序,一個任務要等其伙伴發來通知,或建立了某個條件后才能繼續執行,否則只能等待;
任務之間的這種制約性的合作運行機制叫任務間的同步;
任務中的事件
uCosII
使用信號量、消息郵箱和消息隊列這些中間環節來實現任務之間的通信,而這些中間環節都統一稱為事件;
事件控制塊及事件處理
事件控制塊
對於事件來說,當其被占用時,會導致其他請求該事件的任務因暫時得不到該事件的服務而處於等待狀態;
作為功能完善的事件,應該對這些等待任務有兩方面的管理能力:
- 要對等待事件的所有任務進行記錄並排序;
- 允許等待任務有一個等待時限,即當等待任務認為等不及時可以退出對事件的請求;
對於等待事件任務的記錄,系統定義了一個INT8U
類型的數組OSEventTbl[]
作為等待事件任務的記錄表,即等待任務表;
等待任務表以任務優先級別為順序為每個任務分配一個二進制位,來表示這一位對應的任務為事件的等待任務,否則不是等待任務;
為了加快該表的訪問速度,也定義了一個INT8U
類型的變量OSEventGrp
來表示等待任務中的任務組;
等待任務的等待時限記錄在等待任務的任務控制塊TCB
的成員OSTCBDly
中,並在每個時鍾節拍中斷服務程序中對該數據進行維護,每當有任務的等待時限到達時,便將該任務從等待任務表中刪除,並使它進入就緒狀態;
系統為了把描述事件的數據結構統一起來,使用叫做事件控制快ECB
的數據結構來描述諸如信號量、消息郵箱和消息隊列等事件;
事件控制塊的組織是通過事件控制塊鏈表的形式;
事件處理函數
系統中對事件控制塊進行基本操作的函數定義在文件os_core.c
中;
EventWaitListInit()
事件控制塊的初始化函數,作用是把變量OSEventGrp
及任務等待表中的每一位都清0,即令事件任務等待表中不含有任何等待任務;OS_EventTaskWait()
把一個任務置於等待狀態;OS_EventTaskRdy()
把調用這個函數的任務在任務等待表中的位置清0(解除等待狀態)后,再把任務在任務就緒表中對應的位置設置為1,然后引發一次任務調度;OS_EventTO()
使一個等待超時的任務進入就緒狀態;
信號量及其操作
當事件控制塊成員OSEventType
的值被設置為OS_EVENT_TYPE_SEM
時,這個事件控制塊描述的就是一個信號量;
信號量由計數器和等待任務表兩部分組成;
操作
OSSemCreate
應用程序在使用信號量之前必須先調用該函數來創建一個信號量;
OSSemPend
請求信號量,為防止任務因得不到信號量而處於長期的等待狀態,該函數允許用參數timeout設置一個等待時間的限制,當任務等待時間超過timeout時,可以結束等待狀態而進入就緒狀態,如果該參數設置為0,則表明任務的等待時間為無限長;
當任務需要訪問一個共享資源時,先要請求管理該資源的信號量,這樣就可以根據信號量當前是否有效(即信號量計數器的值是否大於0)來決定該任務是否可以繼續運行,如果信號量有效,則把信號量計數器減1,然后繼續運行任務,如果信號量無效(即信號量計數器的值等於0),則會在等待任務表中把該任務對應的位設置為1而讓任務處於等待狀態,並把等待時限timeout保存在任務控制塊對應的變量中;
如果希望任務在請求信號量,當信號量無效時不進入等待狀態而繼續運行,則不調用函數OSSemPend()
,而是調用函數OSSemAccept()
來請求信號量;
OSSemPost
任務獲得信號量,在訪問共享資源結束以后,必須調用該函數釋放信號量,也叫發送信號量;
該函數在對信號量的計數器操作之前,要檢查是否還有等待該信號量的任務,如果沒有,則將信號量計數器值加1,如果有,則調用調度器OS_Sched()
去運行等待任務中優先級最高的任務;
OSSemDel
如果應用程序不需要某個信號,那么可調用該函數來刪除該信號;
函數中的參數opt用來指明信號量的刪除條件,該參數有兩個參數值可以選擇:
- OS_DEL_NO_PEND當等待任務表中已沒有等待任務時才刪除信號量;
- OS_DEL_ALLWAYS在等待任務表中無論是否有等待任務都立即刪除信號量;
需要注意,只能在任務中刪除信號量,而不能在中斷服務程序中刪除;
OSSemQuery
任務可以調用該函數隨時查詢信號量的當前狀態;
互斥信號量
互斥信號量時一個二值信號,可以使任務以獨占方式使用共享資源;
使用互斥型信號量會出現任務優先級反轉的問題,即在可剝奪型內核中,當任務以獨占方式使用共享資源時,會出現低優先級任務先於高優先級任務而被運行的現象,這種現象在實時系統中是不允許出現的,因為它破壞了任務執行的預期順序,可能會導致嚴重的后果;
解決該現象的辦法之一是,使獲得信號量任務的優先級別在使用共享資源期間暫時提升到所有任務最高優先級的高一個級別上,以使該任務不被其他任務所打斷,從而能盡快地使用完共享資源並釋放信號量,然后在釋放信號量之后,再恢復該任務原來的優先級別;
OSMutexCreate
創建互斥型信號量,在描述互斥型信號量的事件控制塊中,除了成員OSEventType要賦予常數OS_EVENT_TYPE_MUTEX以表明是一個互斥型信號量和仍然沒有使用成員OSEventPtr之外,成員OSEventCnt被分成了低8位和高8位兩部分,高8位用來存放為了避免出現優先級反轉現象而要提升的優先級prio,低8位賦予常數OS_MUTEX_AVAILABLE,表明信號量尚未被任何任務所占用,處於有效狀態;
OSMutexPend
請求互斥型信號量;
也可以調用函數OSMutexAccept()無等待地請求一個互斥型信號量;
OSMutexPost
發送一個互斥型信號量;
OSMutexQuery
獲取互斥型信號量的當前狀態;
OSMutexDel
任務調用該函數可刪除一個互斥型信號量;
消息郵箱
OSMboxCreate
創建消息郵箱;
OSMboxPost
向消息郵箱發送消息;
OSMboxPostOpt
向消息郵箱發送消息,可以以廣播的方式向事件等待任務表中的所有任務發送消息;
OSMboxPend
請求消息郵箱;
OSMboxAccept
請求消息郵箱失敗時,不進行等待而是繼續運行;
OSMboxQuery
查詢消息郵箱當前狀態信息;
OSMboxDel
刪除一個消息郵箱;
消息隊列
使用消息隊列可在任務之間傳遞多條消息,消息隊列由三部分組成:時間控制塊、消息隊列和消息;
事件控制塊成員OSEventPtr指向一個叫做隊列控制塊(OS_Q)的結構,該結構管理着一個數組MsgTbl[],該數組中的元素都是指向消息的指針;
OSQCreate
創建消息隊列,首先需要定義一個指針數組,然后把各個消息數據緩沖區的首地址存入這個數組中,最后調用該函數來創建消息隊列;
OSQPend
請求消息隊列,即從消息隊列中獲取消息;
如果希望任務無等待地請求一個消息隊列,則可調用函數OSQAccept()函數;
OSQPost/OSQPostFront
向消息隊列發送消息;
函數OSQPost()是以FIFO的方式組織消息隊列;
函數OSQPostFront()以LIFO的方式組織消息隊列;
OSQPostOpt
該函數時任務將以廣播的方式通過消息隊列發送消息;
OSQFlush
清空消息隊列;
OSQDel
刪除一個已存在的消息隊列;
OSQQuery
查詢一個消息隊列的狀態;