1. 資源訪問的線程安全問題
在多任務系統中,當多個任務或中斷擁有同一資源訪問權限時,可能發生資源訪問的錯誤。當一個任務未完整訪問完一個資源就退出運行態,其他任務或中斷再訪問同一資源時,可能導致數據損壞或其他錯誤。
1.1 哪些資源訪問存在線程安全問題
外設資源
讀-改-寫操作
非原子性訪問變量
函數重入
1.2 如何確保資源訪問的線程安全
保證任務或中斷在對資源訪問時的獨占性,也就是一旦開始訪問改資源就獨占該資源,直到完整訪問完后再釋放改資源。也就是稱之為“互斥”的技術。
2 互斥技術
2.1 臨界段
代碼的臨界段也稱為臨界區,指處理時不可分割的代碼區域,一旦這部分代碼開始執行,則不允許任何中斷打斷。為確保臨界段代碼的執行不被中斷,在進入臨界段之前須關中斷,而臨界段代碼執行完畢后,要立即打開中斷。
臨界段必須短小,否則影響中斷響應時間。
支持嵌套,在嵌套深度為0是才退出對資源的獨占
禁止任務及部分/全部中斷的搶占
2.2 掛起內核調度
只任務禁止搶占,中斷可搶占
恢復內核調度時間較長
2.3 互斥信號量
互斥信號量,可以被看成是一個令牌。當要請求訪問一個共享資源時,必須先取得令牌的所有權,成為令牌所有者并完成資源的訪問后,必須返還令牌,這樣其他任務才可繼續申請令牌完成資源的訪問。
2.3.1 互斥信號量的使用風險
互斥信號量保證了資源的互斥訪問,同時也帶來了一些風險和使用隱患,比如優先級反轉、死鎖、同任務優先級時序影響等。
2.3.1.1 優先級反轉
假設圖1信號量實現資源互斥中的Task A優先級較低,Task B優先級較高,但是Task B確被Task A阻塞。高優先級被低優先級阻塞的現象,我們稱之為“優先級反轉”。
假設此時還有一個任務C,優先級在Task A(低優先級)和Task B(高優先級)之間,在Task B等待資源時,Task C卻能搶占Task A而執行,這樣不僅Task A阻塞了Task B,而且Task C也阻塞了Task B,如果有更多的中等優先級位于Task A 和 Task B之間,會發生更多的優先級反轉。能否減少優先級反轉的情況發生呢?
優先級繼承
優先級繼承就是一種讓優先級反轉影響最小化的一種機制。它的原理是:暫時提升令牌持有者的優先級到正試圖持有令牌的最高優先級任務的優先級,并在令牌持有者返還令牌時恢復其原來的優先級。
互斥信號量與二值信號量的不同就是有優先級繼承機制
優先級繼承會影響任務的優先級,所以中斷服務程序中不能使用互斥信號量。
2.3.1.2 死鎖
互鎖
當兩個任務都在等待另一個任務持有的資源而無法繼續執行時就發生了死鎖。假設有兩個任務Task A和Task B都需要獲取資源互斥信號量M1 和 M2,并按如下情形執行:
- TASK A執行并成功獲取M1。
- Task B搶占Task A并成功獲取M2, 然后試圖獲取M1,但M1已被TASK A持有,TASK B只能掛起等待M1的釋放。
- TASK A繼續執行,并試圖獲取M2,但M2已被TASK B持有,TASK A只能掛起等待M1的釋放。
至此,TASK A在等待TASK B釋放M2, 而TASK B在等待TASK A釋放M1,兩個任務都無法繼續執行而形成死鎖。
- TASK A繼續執行,并試圖獲取M2,但M2已被TASK B持有,TASK A只能掛起等待M1的釋放。
如何解除死鎖
- 設計層面考慮避免死鎖發生的風險
- 在獲取資源互斥信號量時不要無限期等待,設置合理的超時等待時間,該時間比預期等待的最大時間長一些就可以。如果獲取互斥信號量超時事件發生,可能發生了死鎖,這也有利于幫助我們重新審視資源管理的設計是否存在合理。
自鎖
當一個任務已經是互斥資源的持有者,并再次嘗試獲取該資源互斥信號量,就陷入了自我等待的死鎖狀態。這種死鎖狀態通常發生在任務持有互斥資源后,其調用的庫又試圖獲取該互斥資源,可以通過遞歸互斥技術來避免。
遞歸互斥
遞歸互斥可以在同一任務中多次‘獲取’,直到之前每一個‘獲取’互斥都‘釋放’了才恢復有效。第一次獲取,之后的每次‘獲取’只增加遞歸深度;每次'釋放'遞減遞歸深度,直到遞歸深度為0,互斥信號量才恢復有效。
是否為了避免自鎖,我們就都使用遞歸互斥呢?實際應用中應盡量避免使用互斥遞歸,從設計上杜絕自鎖的發生。可以將資源對資源的訪問進行封裝,每一次訪問都是先獲取資源互斥,使用完釋放資源互斥,使資源的管理盡量簡單可靠。
2.3.1.3 同任務優先級調度時序
同優先級的任務,系統在調度時使用基于時間片的輪轉調度,以保證各任務能獲得大致均等的執行時間。假如同一優先級的多任務使用同一資源信號量,未持有資源互斥信號量的任務在自己的時間片到來時申請資源互斥量進入阻塞態而放棄其余下時間片,資源信號量的持有者任務繼續其時間片。如果資源持有者的任務是一個緊密的循環執行體,其時間片內發生資源的釋放和再獲取,這樣其他同優先級也使用該資源的任務較少甚至沒有機會獲得該資源,這樣同一優先級的多任務的執行時間將有較大差異,甚至發生死鎖。針對這種情況,可以在其資源釋放并距資源獲取發生了tick值改變時,讓任務放棄當前的時間片占用,當然更好的方法是避免這樣的設計。
2.4 守護任務
守護任務是一種資源把關任務,對資源擁有唯一所有權的任務。只有守護任務能直接訪問資源,其他任務只能間接通過守護任務來實現資源的訪問。
可見守護任務是一種干凈利落實現互斥訪問的方式,而且不存在優先級反轉和死鎖問題。