壹、實現原則
pthread mutex的實現非常輕量級,使用原子操作+futex系統調用。
在沒有競爭的情況下,也就是鎖空閑的時候,任務只需要通過原子操縱鎖的狀態值,將值設置為占有,然後記錄壹些其他的俄語信息(擁有者,計數,如果啟用了回收功能的話字符串到任務的信號量回收列表中等等。),然後返回。
如果在獲取鎖的時候發現被占用了,調用者需要等待睡眠,此時就會觸發futex系統調用,內核會繼續處理。內核將讓調用任務休眠,並在適當的時間喚醒(超時或鎖定狀態可用)。
當占用鎖的任務釋放鎖時,如果沒有等待鎖的任務,只需將鎖狀態設置為idle即可。如果發現其他任務正在等待這個鎖,就會觸發壹個futex系統調用,等待的任務就會被內核喚醒。
可以看出,在沒有競爭的情況下,互斥體只需要在用戶模式下操作鎖狀態值,效率非常高。
獲得鎖的任務並沒有被困在內核中,所以當鎖支持優先級翻轉時,高優先級任務等待鎖,正常處理必須提高占用鎖的任務的優先級。內核如何知道哪個任務占用了鎖?在實現中,鎖的狀態值被重用,空閑狀態下為0,非空閑狀態保存鎖持有者ID,即PID,內核狀態通過PID知道是任務。
第二,內核對鎖的管理
內核維護壹個哈希鏈表,每個鎖都被插入到哈希鏈表中。哈希值計算如下(參考GET _ FUTEX _ KEY): 1,如果是進程中的鎖,則傳遞。
鎖定的虛擬地址+任務mm指針值+頁面中的鎖定偏移量;2.如果是進程間鎖,則會獲取鎖的虛擬地址對應的物理地址的頁面描述符,並由頁面描述符構造。
哈希值。
進行這種計算的原因是,進程之間的鎖的虛擬地址在每個進程中可能不同,但它們都映射到同壹個物理地址,並對應於同壹個頁面描述符。所以,在裏面
內核用它來定位是否是同壹個鎖。
這裏計算進程間互斥鎖哈希值的方法為進程間鎖的使用設置了隱患條件。這個問題描述如下。
3.進程間互斥信號量的使用限制:互斥結構必須在系統管理的內存中定義,不能在用戶預留的* * *共享內存中定義。
為了實現進程之間的互斥,每個進程都必須能夠看到鎖。因此,鎖結構必須放在共享內存中。
對系統* * *共享內存的訪問是通過System V的API接口創建的:shmget、shmat、shmdt。但是shmget的參數需要壹個id值,每個進程需要相同的ID值來映射相同的內存。如果每個進程需要更多* * *共享內存,比如上千個,如何管理ID值?Shmget的man幫助和部分示例代碼顯示,通過ftok函數將壹個文件轉化為ID值(實際上是將文件對應的INODE轉化為ID值),但在實際應用中,如果需要大量的* * *內存,是否可以創建數千個文件來使用?以及如何確保文件在流程的生命周期內不會被刪除或重建?
當時開發的系統中還有另外壹種* * *共享內存,是通過remap_pfn_range實現的,我們自己管理這個內存的應用發布。應用程序接口參數是壹個字符串,相同的字符串代表相同的內存。因此,我傾向於使用自己的托管* * *共享內存來存儲互斥結構。但是在使用中發現這種方法並不能達到互斥的效果。為什麽?
原因是自主管理的* * *內存內核是通過remap_pfn_range實現的,內核會將這個內存設置為保留,表示非內核管理。獲取鎖的哈希值時,找不到頁面結構,返回失敗。最終的解決方案是通過shmget申請* * *享受內存,但不是通過ftok獲取ID,而是將字符串轉換成ID值,處理沖突。
第四,進程間互斥信號量的回收問題。
假設進程P1獲得了進程間信號量,異常退出,還沒有釋放信號量。這時候其他進程想獲取信號量,能得到嗎?
或者進程P1獲得信號量後,其他進程無法獲得。進入睡眠後,P1異常退出。誰負責喚醒沈睡的過程?
好在在制度設計上已經考慮到了這壹點。
只需在信號量初始化時調用pthread _ mutextettr _ setrobust _ NP來支持信號量回收機制,然後在獲取信號量時,如果原來占用信號量的進程退出,系統將返回EOWNERDEAD,調用pthread_mutex_consistent_np來完成信號量所有者的切換。
原理如下:
創建任務時,會在內核的任務控制塊TCB中間註冊壹個健壯列表(用戶態鏈表)。當信號量被獲取時,它將被鏈接到鏈表中。當進程復位時,內核會遍歷這個鏈表(內核壹定要非常小心,因為此時的鏈表信息可能不可靠,但不能影響內核),將ownerdead標誌置為lock狀態,喚醒等待這個信號量鏈表的進程。
動詞 (verb的縮寫)使用pthread接口的說明
Pthread_mutex_init:根據指定的屬性初始化壹個互斥體,狀態為idle。
刪除壹個互斥體。
pthread _ mutex _ lock/try lock/timed lock/unlock:獲取鎖和釋放鎖。在沒有競爭的情況下,在用戶模式下,返回鎖的狀態值,不會落入內核。但是timedlock的參數是timeout,壹般需要通過調用系統API來獲取,這樣會導致掉入內核,性能很差。在實現中,先嘗試trylock,失敗後再嘗試timedlock。
Pthread _ mutextettr _ init:配置初始化
Pthread _ mutextettr _ destroy:刪除配置初始化接口請求的資源。
Pthread _ mutextettr _ setpshared:設置互斥體是否在進程間共享。
Pthread _ mutextettr _ settype:設置類型,如遞歸調用、錯誤檢測等。
Pthread _ muteexattr _ setprotocol:設置是否支持優先級反轉。
Pthread _ mutextettr _ setprioceling:將獲取信號量的任務設置為以最高優先級運行。
每個set接口都有壹個對應的get接口。
不及物動詞pthread結構變量的描述
struct __pthread_mutex_s
{
int _ _ lock- 31bit:這個鎖有調用者嗎?30bit:這個鎖的主人是否已經掛了。其他位bit:0鎖狀態為空閑,非0為任務PID帶鎖;
無符號int _ _ count-獲取鎖的次數。支持嵌套調用。每獲取壹次鎖值,增加1,釋放1。
int _ _ owner-鎖的主人
無符號int _ _ nusers-使用鎖的任務數,通常為1(已占用)或0(空閑)。
int _ _ kind-鎖的屬性,如遞歸調用、優先級反轉等。
int _ _ spinsSMP下,嘗試獲取鎖的次數,盡量不要進入內核。
_ _ pthread _ list _ t _ _ list-將鎖插入回收清單。如果支持回收功能,則每次獲取鎖時,將鎖插入任務控制塊的回收列表中。
} _ _數據;