ucosIII 共享資源(訊號量、互斥訊號量)
共享資源:
變數(靜態或全域性變數)、資料結構體、RAM表格、I/O裝置等。OS在使用一些資源時候,例如IO裝置印表機,當任務1在使用印表機時候必須保證資源獨享,避免其他任務修改列印內容導致出錯,因此需要有資源共享機制。
一般推薦使用互斥訊號量對共享資源實現保護
獨佔資源和建立臨界區方法表
方法 | 優點 | 何時使用 |
---|---|---|
開關中斷 | 能很快地結束訪問共享資源,不推薦,會導致中斷延遲 | 訪問共享資源時間小於中斷關閉時間 |
鎖排程器 | 不推薦,有悖ucos任務切換 | 訪問共享資源時間大於中斷關閉時間,給排程器上鎖時間短 |
訊號量 | 訊號量可能會造出優先順序反轉。然而,訊號量方式的執行時間少於互斥訊號量 方式 | 所有的任務可以無限期等待對共享資源的訪問 |
互斥訊號量 | 推薦使用這種方法訪問共享資源,有內建的優先順序,時間較久 | 當任務要訪問的共享資源有截止時間 |
只有開關中斷才能任務和中斷程式共享資源。其它只能用於任務相關的資源共享。
關/開中斷
和CPU 相關的操作,其相關程式碼被放在與CPU相關的檔案中(見CPU.H)。uC/OS-III 中與CPU相關的模組叫做uC/CPU。每種架構的CPU 都需要設定相適應的uC/CPU 檔案。
API | 註釋 |
---|---|
CPU_SR_ALLOC() | 分配儲存空間儲存當前cpu的中斷狀態 |
CPU_CRITICAL_ENTER() | 關全域性中斷 |
CPU_CRITICAL_EXIT() | 開全域性中斷,恢復區域性變數中儲存的cpu中斷狀態 |
只有這種方法才能任務和中斷程式共享資源。只要關中斷的時間不比系統本身的關中斷時間長,就不會影響到系統的中斷延時。
鎖排程器
如果任務不需要和ISR 共享資源,就可以通過鎖排程器來訪問共享資源。
實際上就是禁止任務排程已達到資源的獨佔
API | 註釋 |
---|---|
OSSchedLock() | 鎖排程 |
OSSchedUnlock() | 開排程 |
注意,只有排程器被鎖,中斷是使能的,如果在處理臨界段時中斷髮生,ISR程式就會被執行。在ISR 的末尾,uC/OS-III會返回原任務(即使ISR中有高優先順序任務被就緒)。
支援巢狀250 級。當OSSchedUnlock()與OSSchedLock()被呼叫的次數相同時排程器才被鎖排程器影響了搶佔式核心的初衷,核心行為實際與不可剝奪的核心已經是一樣的了。
訊號量
一種上鎖機制,程式碼需要獲取對應的鑰匙才能訪問共享資源(繼續執行),我的理解是實際上只是對訪問共享資源區的程式碼段進行上鎖,如果沒有獲取到鑰匙(訊號量)則等待無法繼續執行,只有別的任務施放了訊號量之後才能繼續執行下去。
二進位制訊號量和計數型訊號量:
二進位制訊號量只能取0和1兩個值,計數型訊號量的訊號量值大於1,計數型訊號量的範圍由OS_SEM_CTR決定,OS_SEM_CTR可以為8位,16位和32位,取值範圍分別為:0~255,0~65535和0~4294967295。
二值訊號量用於那些一次只能一個任務使用的資源,比如I/O裝置,印表機計,數型訊號量用於某些資源可以同時被幾個任務所使用,比如一個快取池有10個快取塊,那麼同時最多可以支援10個任務來使用記憶體池。
應用中可以使用任意數量的訊號量,但是一般用於IO保護,很多情況下,訪問一個簡短的共享資源時不推薦使用訊號量,請求和釋放訊號量會消耗CPU時間。
API | 註釋 |
---|---|
OSSemCreate() | 建立一個訊號量 |
OSSemDel() | 刪除一個訊號量 |
OSSemPend() | 等待一個訊號量 |
OSSemPendAbort() | 取消等待 |
OSSemPoset() | 釋放一個訊號量 |
OSSemSet() | 強制設定訊號量值 |
OS_SEM | 訊號量型別 |
定義一個訊號量
OS_SEM XXX
建立一個訊號量
void OSSemCreate (OS_SEM *p_sem, //訊號量結構體,其中OS_SEM_CTR Ctr表示訊號量的值
CPU_CHAR *p_name,//名字
OS_SEM_CTR cnt, //訊號量初始值
OS_ERR *p_err)
刪除一個訊號量
OS_OBJ_QTY OSSemDel(OS_SEM *p_sem,
OS_OPT opt,
//1.NO_PEND 僅當沒有訊號請求的時候才刪除
//2.ALWAYS 直接刪除,不管有沒有訊號請求
OS_ERR *p_err)
請求/等待一個訊號量
OS_SEM_CTR OSSemPend(OS_SEM *p_sem,
OS_TICK timeout,//等待的時間,如果為0:一直等待下去
OS_OPT opt,
//1.暫時無效直接掛起 OS_OPT_PEND_BLOCKING
//2.無效直接返回OS_OPT_PEND_NON_BLOCKING
CPU_TS *p_ts,//時間戳:記錄接受訊號量的時刻,0或者null則不需要時間戳。
OS_ERR *p_err)
取消等待
OS_OBJ_QTY OSSemPendAbort(OS_SEM *p_sem,
OS_OPT opt,
//1. 僅終止等待該訊號量的最高優先順序任務
//2. 中止所有的等待該訊號量的任務
//3. 禁止任務排程
OS_ERR *p_err)
釋放一個訊號量
OS_SEM_CTR OSSemPost(OS_SEM *p_sem,
OS_OPT opt,
//1. 僅傳送給等待該訊號量的最高優先順序任務
//2. 傳送給所有的等待該訊號量的任務
//3. 禁止任務排程
OS_ERR *p_err)
步驟:
先定義OS_SEM
Create建立訊號量
OSSemPend等待訊號量
OSSemPost釋放訊號量
互斥訊號量
使用訊號量訪問共享資源會有優先順序反轉問題:高優先順序A任務執行訪問共享資源的時候被迫等待低優先順序任務D釋放訊號量,此時會發生任務切換到低優先順序。如果此時又有高一級的任務B中斷了D的任務,雖然B任務比A任務優先順序更低,也會發生任務排程。使得高優先順序任務A實際上被拉到佔用訊號量的低優先順序任務D同一優先級別,違背了實時RTOS。
互斥訊號量實際解決方法就是,將低優先順序任務D的優先順序臨時提升至任務A同一優先順序,等訊號量釋放後,再將任務D的優先順序恢復,這樣任務切換隻在這兩個任務之間執行(如果有比A更高的還是會切換)。
API | 註釋 |
---|---|
OSMutexCreate() | 建立一個互斥訊號量 |
OSMutexDel() | 刪除一個互斥訊號量 |
OSMutexPend() | 等待一個互斥訊號量 |
OSMutexPendAbort() | 取消等待訊號量 |
OSMutexPost() | 釋放一個互斥訊號量 |
建立互斥訊號量
void OSMutexCreate(OS_MUTEX *p_mutex,//指向互斥訊號量的控制塊
CPU_CHAR *p_name, //互斥訊號量名字
OS_ERR *p_err)//呼叫此函式後返回的錯誤碼
請求/等待互斥訊號量
void OSMutexPend(OS_MUTEX *p_mutex, //指向互斥訊號量的控制塊
OS_TICK timeout,//指定等待互斥訊號量的超時節拍,超時繼續執行,0位一直等待
OS_OPT opt,//是否阻塞
//OS_OPT_PEND_BLOCKING無效掛起
//OS_OPT_PEND_NON_BLOCKING無效直接返回
CPU_TS *p_ts,//時間戳
OS_ERR *p_err)//錯誤碼
釋放互斥訊號量
void OSMutexPost(OS_MUTEX *p_mutex,//指向互斥訊號量
OS_OPT opt,//指定是否進行任務排程操作
//OS_OPT_POST_NONE不指定特定的選項
//OS_OPT_POST_NO_SCHED 禁止在本函式內執行任務排程
OS_ERR *p_err)//錯誤碼
步驟:
先定義OS_MUTEX xxxx
Create建立訊號量
OSMutexPend等待訊號量
OSMutexPost釋放訊號量
死鎖:
任務1需要的資源要等到任務2釋放,任務2的資源需要等待任務1釋放,造成死鎖。
(1)假定任務T1 所等待的事情發生,任務1被執行。
(2)任務T1 申請M1。(mutex 1)
(3)任務T1 訪問資源R1。
(4)中斷髮生,中斷中使能了任務T2。由於任務T2 的優先順序高於任務T1,CPU 切換到任務T2。
(5)該中斷即是任務T2 所等待的事件。
(6)任務T2 申請M2 並佔用資源R2。
(7)任務T2 申請M1,但是M1 已被任務T1 佔用。任務T2
被掛起。
(8)uC/OS-III 切換到任務T1。
(9)此時任務T1 申請M2,但是M2 已經被任務T2 佔用。
此時,兩個任務互相等待,這就算死鎖。
用以下方式防止死鎖:
1)同一個時間不要申請多於一個mutex
2)不要直接地申請mutex(該申請放到器件驅動中和可重入函式中)
3)在處理之前先獲得全部所需要的mutex
4)任務間以同樣的順序申請資源
當申請訊號量或mutex 時允許設定時間期限,這樣能防止死鎖,但是同樣的死鎖可能稍後再次出現。