----一年前寫的東西,重新抄錄以防遺忘。
glibc提供的pthread互斥訊號量可以用在程序內部,也可以用在程序間,可以在初始化時通過pthread_mutexattr_setpshared介面設定該訊號
量屬性,表示是程序內還是程序間。程序內的使用較為簡單,本文的總結主要是針對程序間的,程序內的也可以參考,其**實現原理是類似
的。一、實現原理
pthread mutex的實現是非常輕量級的,採用原子操作+futex系統呼叫。
在沒有競爭的情況下,即鎖空閒時,任務獲取訊號量只需要通過原子操作鎖的狀態值,把值置為占有,再記錄其他一些俄資訊(owner
,計數,如果使能**功能則串入任務的訊號量**鍊錶等),然後就返回了。
如果在獲取鎖時發現被占用了,如果呼叫者需要睡眠等待,這時候會觸發futex系統呼叫,由核心繼續處理,核心會讓呼叫任務睡眠,
並在適當時候喚醒(超時或者鎖狀態為可用)。
占用鎖的任務釋放鎖時,如果沒有任務等待這把鎖,只需要把鎖狀態置為空閒即可。如果發現有其他任務在等待此鎖,則觸發futex系
統呼叫,由核心喚醒等待任務。
由此可見,在沒有競爭的情況下,mutex只需要在使用者態操作鎖狀態值,無須陷入核心,是非常高效的。
獲取到鎖的任務沒有陷入核心,那麼當鎖支援優先順序翻轉時,高優先順序任務等待這把鎖,正常處理必須提公升占用鎖的任務優先順序。內
核又是怎麼知道是哪個任務占用了鎖呢?實現上,復用了鎖的狀態值,該值在空閒態時為0,非空閒態則儲存了鎖的持有者id,即pid,核心態通
過pid就知道是那個任務了。
二、核心對鎖的管理
核心維護了乙個hash鍊錶,每把鎖都被插入到hash鍊錶中去,hash值的計算如下(參考get_futex_key):1,如果是程序內的鎖,則通
過鎖的虛擬位址+任務mm指標值+鎖在頁內偏移;2,如果是程序間的鎖,則會獲取鎖虛擬位址對應實體地址的page描述符,由page描述符構造
hash值。
這樣計算的原因是程序間的鎖在各個程序內虛擬位址可能是不同的,但都對映到同乙個實體地址,對應同乙個page描述符。所以,內
核使用它來定位是否同乙個鎖。
這裡對程序間互斥鎖計算hash值的方法,給程序間共享鎖的使用設定了乙個隱患條件。下面描述這個問題。
三、程序間互斥訊號量的使用限制:必須在系統管理的記憶體上定義mutex結構,而不能在使用者reserved的共享記憶體上定義mutex結構。
鎖要實現程序間互斥,必須各個程序都能看到這個鎖,因此,鎖結構必須放在共享記憶體上。
獲取系統的共享記憶體通過system v的api介面建立:shmget, shmat,shmdt。但是shmget的引數需要乙個id值,各程序對映同一塊共享
記憶體需要同樣的id值。如果各個程序需要共享的共享記憶體比較多,如幾千上萬個,id值如果管理?shmget的man幫助和一些示例**給出的是通
過ftok函式把乙個檔案轉為id值(實際就是把檔案對應的inode轉為id值),但實際應用中,如果需要的共享記憶體個數較多,難道建立成千上萬
個檔案來使用?而且怎麼保證檔案在程序的生命週期內不會被刪除或者重建?
當時開發的系統還存在另外一種共享記憶體,就是我們通過remap_pfn_range實現的,自己管理了這塊記憶體的申請釋放。申請介面引數為
字串,相同的字串表示同一塊記憶體。因此,傾向於使用自己管理的共享記憶體存放mutex結構。但在使用中,發現這種方法達不到互斥的效果
。為什麼?
原因是自己管理的共享記憶體在核心是通過remap_pfn_range實現的,核心會把這塊記憶體置為reserved,表示非核心管理,獲取鎖的hash
值時,查詢不到page結構,返回失敗了。最後的解決方法還是通過shmget申請共享記憶體,但不是通過ftok獲取id,而是通過字串轉為id值並
處理衝突。
四、程序間互斥訊號量**問題。
假設程序p1獲取了程序間訊號量,異常退出了,還沒有釋放訊號量,這時候其他程序想來獲取訊號量,能獲取的到嗎?
或者程序p1獲取了訊號量後,其他程序獲取不到進入了睡眠後,p1異常退出了,誰來負責喚醒睡眠的程序?
好在系統設計上已經考慮了這一點。
只要在訊號量初始化時呼叫pthread_mutexattr_setrobust_np設定支援訊號量**機制,然後,在獲取訊號量時,如果原來占有訊號
量的程序退出了,系統將會返回eownerdead,判斷是這個返回值後,呼叫pthread_mutex_consistent_np完成訊號量owner的切換工作即可。
其原理如下:
任務建立時,會註冊乙個robust list(使用者態鍊錶)到核心的任務控制塊tcb中期,獲取了訊號量時,會把訊號量掛入鍊錶。程序復
位時,核心會遍歷此鍊錶(核心必須非常小心,因為此時的鍊錶資訊可能不可靠了,可不能影響到核心),置上ownerdead的標誌到鎖狀態,並
喚醒等待在此訊號量鍊錶上的程序。
五、pthread介面使用說明
pthread_mutex_init: 根據指定的屬性初始化乙個mutex,狀態為空閒。
pthread_mutex_destroy: 刪除乙個mutex
pthread_mutex_lock/trylock/timedlock/unlock: 獲取鎖、釋放鎖。沒有競爭關係的情況下在使用者態只需要置下鎖的狀態值即返回了
,無須陷入核心。但是timedlock的入參為超時時間,一般需要呼叫系統api獲取,會導致陷入核心,效能較差,實現上,可先trylock,失敗了
再timedlock。
pthread_mutexattr_init:配置初始化
pthread_mutexattr_destroy:刪除配置初始化介面申請的資源
pthread_mutexattr_setpshared:設定mutex是否程序間共享
pthread_mutexattr_settype:設定型別,如遞迴呼叫,錯誤檢測等。
pthread_mutexattr_setprotocol:設定是否支援優先順序翻轉
pthread_mutexattr_setprioceiling:設定獲取訊號量的任務執行在最高優先順序。
每個set介面都有對應的get介面。
六、pthread結構變數說明
struct __pthread_mutex_s
__data;
訊號量 互斥量
lonelycatcher if only as first.來自 訊號量用在多執行緒多工同步的,乙個執行緒完成了某乙個動作就通過訊號量告訴別的執行緒,別的執行緒再進行某些動作 大家都在semtake的時候,就阻塞在 而互斥鎖是用在多執行緒多工互斥的,乙個執行緒占用了某乙個資源,那麼別的執行緒就無法...
訊號量和互斥量
1.互斥量用於執行緒的互斥,訊號線用於執行緒的同步。這是互斥量和訊號量的根本區別,也就是互斥和同步之間的區別。互斥 是指某一資源同時只允許乙個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。同步 是指在互斥的基礎上 大多數情況 通過其它機制實現訪問者對資...
訊號量,互斥鎖
注 摘自 程式設計師的自我修養 相關章節。關鍵字 執行緒同步 原子操作 鎖 二元訊號量 訊號量 互斥量 臨界區 讀寫鎖 條件變數 原子操作 共享資料 全域性變數或堆變數 的自增 操作在多執行緒環境下會出現錯誤是因為這個操作 一條c語句 被編譯為彙編 後不止一條指令,因此在執行的時候可能執行了一半就被...