1. 程式人生 > >ucosIII 共享資源(訊號量、互斥訊號量)

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 時允許設定時間期限,這樣能防止死鎖,但是同樣的死鎖可能稍後再次出現。