Task之互斥制訊號量
- 2019 年 10 月 31 日
- 筆記
多任務系統里經常面臨一個問題:資源競爭。哪些資源呢?很多,例如外設、文件、數據等。當多個任務同時訪問這些資源,就會發生衝突。例如下面這段程式碼

每調用一次函數myBufPut(),就會在數組myBuf[]中存入一個字元,這相當於一個入棧操作。在實際系統中,任務多了,任何情況都可能發生,假如有兩個任務(T1、T2)都調用這個函數,就可能出現以下執行序列

T1剛把myBufIndex加了1,就被高優先順序的T2搶佔了。然後必須等T2退出就緒隊列,T1才能繼續執行。最後的結果是myBuf[0]里啥也沒存,而myBuf[1]里先被存入'b',又被存入'a'。可見僅僅兩行程式碼,就會出亂子。那如果只有一行程式碼呢?

這個函數夠簡單了吧,就一條執行++的語句。反編譯看一下

可以看到,機器在執行時,是把它拆為三條指令的。也就是有兩個位置可以搶佔的,資源衝突的風險還是存在的。 更為嚴重的是,這種衝突發生的機率很小!例如T2早一點或晚一點進入就緒隊列,都不會干擾到T1的執行。所以這種資源衝突很難復現出來。這種不確定性,給軟體的調試帶來了很大麻煩。 那怎麼辦呢,也就是如何避免衝突呢?VxWorks給出的主要解決方案是互斥訊號量-Mutex Semaphore 多個任務競爭資源時,給共享的這個資源加上一個互斥訊號量,就相當於加上一把鎖。想要訪問資源的任務,需要先申請互斥鎖。如果申請成功,就可以訪問資源;否則就得阻塞。資源訪問完畢,需要釋放互斥鎖;然後其它任務才能夠訪問。 這個互斥訊號量就像《Task之二進位訊號量》一樣,使用之前,也需要創建

返回值就是訊號量的ID,參數options的取值通常有四個

SEM_Q_FIFO與SEM_Q_PRIORITY是互斥關係,創建訊號量時,必須二選一,決定了多個任務阻塞到同一個訊號量上的時候,要採用哪種排隊方式。 創建訊號量之後,就可以使用它了。用的是與二進位訊號量一樣的兩個函數:semTake()用來申請訊號量,semGive()用來釋放訊號量。

myBufPut()的例子就可以改成這樣了

這樣使用互斥訊號量保護共享資源後,就不可能有多個任務同時訪問它了
不過互斥訊號量的使用規則有些不同
- 互斥訊號量是誰申請、誰釋放
- 申請成功後,可以再次申請,不過要釋放同樣的次數
- 中斷里本來就不能申請任何訊號量,所以中斷里也不能釋放互斥訊號量
流程圖也就有所不同了


寫個例子,儘可能多的覆蓋這兩個流程的分支

跑一下,看看效果

可以看到
- task1可以多次申請同一訊號量
- task1釋放同樣次數後,其它阻塞任務才可能解除阻塞
- option用的0,即SEM_Q_FIFO,因此先創建的task2先解除阻塞
- task1釋放次數過多時,報錯
把option換成SEM_Q_PRIORITY試試

這次是task3先解除阻塞了,因為它的優先順序高一些
互斥訊號量在使用時,還有些需要注意的地方

上圖中,三個任務的優先順序都不一樣,tHigh在競爭tLow已佔用的同一資源時,被迫阻塞了。而這個時候,tMedium因為已經就緒就會搶在tLow之前執行。這種現象就叫做Priority Inversion,可以翻譯為優先順序倒置、反轉、逆轉、翻轉等。意思就是說優先順序高一些的tHigh需要等待優先順序略低的tMedium。 這顯然違背了實時系統的基本思想!
怎麼解決呢?非常簡單,在創建互斥訊號量時,指定選項SEM_INVERSION_SAFE。這樣當tHigh申請同一個訊號量時,系統就會將持有該訊號量的tLow的優先順序臨時提高到與tHigh相同,tLow就會先於tMedium執行,tMedium就不會干擾到tHigh了

有一點要注意的是:SEM_INVERSION_SAFE需要和SEM_Q_PRIORITY一起使用。 可能有人會疑惑,tHigh不是還要等tLow釋放訊號量才能執行嗎?tLow的優先順序本來更低呀。 tHigh不得不等,因為它倆要訪問同一資源,否則會出現衝突。不過這也提醒我們,一個任務持有互斥訊號量的時間要儘可能的短,不然可能會拖累高優先順序的任務。 怎樣才算短呢?在持有訊號量的時候,盡量不要調用可能阻塞或延時的操作!
持有互斥訊號量還有一個風險:任務被意外刪除。這樣訊號量將永遠不會被釋放了,其它任務也不可能申請成功了。為了避免這樣情況,要謹慎使用taskDelete(),另外,互斥訊號量也提供了一種選項:SEM_DELETE_SAFE。有了這個選項,任務在持有該訊號量時,嘗試刪除它的taskDelete()就會返回ERROR。 因此互斥訊號量創建時的選項經常寫作

最後,動態申請的訊號量如果不用了,需要使用semDelete()刪除。另外,在Shell里可以用show()命令來查看訊號量的屬性

最後的最後,留一個問題:聽說過死鎖嗎?
這正是: 資源共享有風險,多人同用易混亂。 互斥Semaphore來上鎖,謹慎使用保平安。