下面討論完成量的內容,首先需明確完成量表示為乙個執行單元需要等待另乙個執行單元完成某事後方可執行,它是一種輕量級機制。事實上,它即是為了完成程序間的同步而設計的,故而僅僅提供了代替同步訊號量的一種解決方法,初值被初始化為0。它在include\linux\completion.h定義。
如圖8.1所示,對於執行單元a而言,如果執行單元b不執行complete函式,執行單元a就會因為申請不到程序而睡眠,直至complete函式在執行單元b中被呼叫,所以執行**b前必須等到執行單元b執行完**c。這一點同訊號量同步的機制類似,只是呼叫的函式不同而已。
圖8.1 完成量的使用示例
下面筆者將討論下它的實現機制。筆者從核心中找出了相關的原始碼,依次如下圖8.2至圖8.5所示。由於圖8.5中的do_wait_for_common函式實現較多,故給出的是刪節後的函式大概框架圖。
圖8.2 完成量的結構體定義
圖8.2展示了完成量的結構體定義,done變數是完成量要保護的物件,wait則是申請完成量的程序的等待佇列。從圖8.3中我們可以看出當初始化完成量變數時,done變數被初始化成0,對比利用訊號量實現同步的內容,可以發現它們是相一致的。
圖8.3 完成量的核心原始碼
配合圖8.1中的完成量原始碼,我們發現現在對於執行單元a,它現在執行完**a後呼叫wait_for_common函式,這個函式的原始碼實現如圖8.5所示。可以看出,它其實即是對done變數作判斷,若done變數沒有大於0,則它一直處於while迴圈中。此時,若是執行單元b執行完**c後,執行complete函式,此時,觀察圖8.4中的complete函式實現,可以發現它對done變數值增1。此時,wait_for_common函式便會退出while迴圈,同時將done值減1,以表示申請完成量成功。
圖8.4 完成量的核心原始碼
圖8.5 完成量的核心原始碼
至此,關於完成量的內容即討論到此,總體來說,完成量的內容還是較為簡單的。後續筆者將會討論有關互斥量的使用和實現。
首先討論關於經典互斥量的內容,它的結構體定義如圖9.1所示。結構體中我們重點關注count、wait_lock、wait_list等變數。和讀寫訊號量的定義相類似。
圖9.1 經典互斥量的結構體定義
另外,關於它的用法和先前的一些鎖機制其實都差不多,具體函式即把加解鎖的函式換成mutex_lock和mutex_unlock等,而它的內部的原始碼實現是建立上原子鎖和自旋鎖的相關機制之上,同時還有一些關於佇列維護的一些內容,這些內容,我們可從它的結構體定義即可簡單看出,這裡不太深入討論了,感興趣的話可以看上面提示的兩個檔案。
下面我們來介紹實時互斥鎖的內容,在介紹這個內容之前,我們先來看乙個有趣的問題,就是無限制優先順序反轉問題,這個問題曾經引起了美國的火星探測器的故障(後續介紹)。
下面具體討論到底什麼是無限制優先順序反轉問題,總體來說,圖9.2至圖9.7展示了整個問題發生的過程。首先當前有乙個執行單元c獲取了乙個互斥量,正在所保護的臨界區中執行,且不打算在短時間內推出。此時,系統中「跑來」執行單元a申請這個互斥量,由於執行單元c正在使用這個互斥量,故a只能等待c執行完,儘管a的優先順序遠高於c(多麼無奈)。這時候,又冒出乙個執行單元b,它的優先順序介於a和c之間。由於系統的乙個中斷的發生,導致執行單元b直接搶占c程序而開始執行互斥量所在的臨界區。但這種情況實際上也搶占了執行單元a,儘管執行單元a的優先順序高於執行單元b(a就是個悲劇)。如果執行單元b繼續執行,那麼執行單元a將等待更長的時間,因為執行單元c被執行單元b搶占,所以它也只能等待執行單元b完成其操作。因此看起來就像執行單元b的優先順序高於執行單元a一樣。這種情況就是我們所說的無限制優先順序反轉問題。
圖9.2 執行單元c訪問臨界區圖9.3 執行單元a等待c完成其操作
圖9.4 b搶占c而執行臨界區圖9.5 b在臨界區中執行
圖9.6 b執行完後c繼續執行臨界區圖9.7 a最終執行臨界區
下面簡單提及下美國火星探測器的故障問題:2023年的美國的火星探測器(探測器使用的是vxworks系統)遇到乙個優先順序反轉問題引起的故障。首先火星探測器有乙個資訊匯流排,有乙個高優先順序的匯流排任務負責匯流排資料的訪問,訪問匯流排都需要通過乙個互斥鎖(共享資源出現了);還有乙個低優先順序的,執行不是很頻繁的氣象蒐集任務,它需要對匯流排寫資料,也就同樣需要訪問互斥鎖;最後還有乙個中優先順序的通訊任務,它的執行時間比較長。平常這個系統執行毫無問題,但是有一天,在氣象任務獲得互斥鎖往匯流排寫資料的時候,乙個中斷發生導致通訊任務被排程就緒,通訊任務搶占了低優先順序的氣象任務,而無巧不成書的是,此時高優先順序的匯流排任務正在等待氣象任務寫完資料歸還互斥鎖,但是由於通訊任務搶占了cpu並且執行時間比較長,導致氣象任務得不到cpu時間也無法釋放互斥鎖,從而使本來是高優先順序的匯流排任務也無法執行,匯流排任務無法及時執行的後果被探路者認為是乙個嚴重錯誤,最後就是整個系統被重啟。事實上,vxworks系統允許優先順序繼承,然而遺憾的是工程師們將這個功能給停止了,從而使火星探測器就此悲劇了。
由於無限制優先順序反轉問題無法使用經典互斥量來解決,理由是經典互斥量中的佇列並沒有實現將等待程序按優先順序排隊。實時互斥量就此因運而生,我們們可以簡單看下它的結構體定義,如圖9.8所示。
圖9.8 實時互斥量的結構體定義
既然經典互斥量不能解決將等待程序按優先順序排隊問題,因此對於實時互斥量,它的實現關鍵即是wait_list的實現,看此變數的定義型別為plist_head,便可一目了然。在plist_head鍊錶運算元,它即採用了優先順序繼承關係將等待程序佇列按程序優先順序方式排隊。這裡優先順序繼承表示為如果高優先順序的程序阻塞在互斥量上,該互斥量當前由低互斥量擁有,那麼將低優先順序的臨時提公升到高優先順序。
linux核心鎖機制
核心鎖的討論 為什麼需要核心鎖?多核處理器下,會存在多個程序處於核心態的情況,而在核心態下,程序是可以訪問所有核心資料的,因此要對共享資料進行保護,即互斥處理 有哪些核心鎖機制?1 原子操作 atomic t資料型別,atomic inc atomic t v 將v加1 原子操作比普通操作效率要低,...
Linux核心中的同步機制
與其解釋什麼是同步,倒不如告訴讀者同步的由來。在linux核心中,同步技術是為了解決問題而產生的。說起這個問題,不得不提起可重入核心。可重入核心執行多個程序交替執行,而程序的切換就發生在核心態下。程序的切換就意味著a程序還未執行結束,就要換b程序執行,如果存在全域性變數g,一旦程序切換,這意味著a程...
linux核心同步機制之自旋鎖
定義 最多只能被乙個可執行執行緒持有。如果乙個執行執行緒試圖獲得乙個被爭用的自旋鎖,那麼該執行緒就會一直進行忙迴圈 旋轉 等待鎖重新可用。自旋鎖有 加鎖 和 解鎖 兩種狀態。加鎖 一直在尋求 解鎖 解鎖 馬上會尋求 加鎖 並原地打轉,所以加鎖位置的 進入臨界區執行,直到解鎖。注意 1.占用臨界區的時...