鎖的種類與特點

2021-08-02 21:01:40 字數 4976 閱讀 5202

在linux核心中,同步機制是一大特性。比較經典的有原子操作、spin_lock(自旋鎖)、mutex(互斥鎖)、semaphore(訊號量)等。

在linux核心中,有很多同步機制。比較經典的有原子操作、spin_lock(忙等待的鎖)、mutex(互斥鎖)、semaphore(訊號量)等。並且它們幾乎都有對應的rw_***(讀寫鎖),以便在能夠區分讀與寫的情況下,讓讀操作相互不互斥(讀寫、寫寫依然互斥)。

atomic(原子操作):

所謂原子操作,就是該操作絕不會在執行完畢前被任何其他任務或事件打斷,也就說,它的最小的執行單位,不可能有比它更小的執行單位,因此這裡的原子實際是使用了物理學裡的物質微粒的概念。

原子操作需要硬體的支援,因此是架構相關的,其api和原子型別的定義都定義在核心原始碼樹的include/asm/atomic.h檔案中,它們都使用組合語言實現,因為c語言並不能實現這樣的操作。

原子操作主要用於實現資源計數,很多引用計數(refcnt)就是通過原子操作實現的。

mutex(互斥鎖)

互斥鎖主要用於實現核心中的互斥訪問功能。核心互斥鎖是在原子api之上實現的,但這對於核心使用者是不可見的。對它的訪問必須遵循一些規則:同一時間只能有乙個任務持有互斥鎖,而且只有這個任務可以對互斥鎖進行解鎖。*互斥鎖不能進行遞迴鎖定或解鎖。乙個互斥鎖物件必須通過其api初始化,而不能使用memset或複製初始化。乙個任務在持有互斥鎖的時候是不能結束的。互斥鎖所使用的記憶體區域是不能被釋放的。使用中的互斥鎖是不能被重新初始化的。並且互斥鎖不能用於中斷上下文。但是互斥鎖比當前的核心訊號量選項更快,並且更加緊湊,因此如果它們滿足您的需求,那麼它們將是您明智的選擇。但是,對於互斥鎖而言,如果資源已經被占用,其它的資源申請程序只能進入sleep狀態。*

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);

int pthread_mutex_lock(pthread_mutex *mutex);

int pthread_mutex_destroy(pthread_mutex *mutex);

int pthread_mutex_unlock(pthread_mutex *

spinlock(自旋鎖)

自旋鎖與互斥鎖有點類似,只是自旋鎖不會引起呼叫者睡眠,如果自旋鎖已經被別的執行單元保持,呼叫者就一直迴圈在那裡看是否該自旋鎖的保持者已經釋放了鎖,「自旋」一詞就是因此而得名由於自旋鎖使用者一般保持鎖時間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠高於互斥鎖。

訊號量和讀寫訊號量適合於保持時間較長的情況,它們會導致呼叫者睡眠,因此只能在程序上下文使用(_trylock的變種能夠在中斷上下文使用),而自旋鎖適合於保持時間非常短的情況,它可以在任何上下文使用。如果被保護的共享資源只在程序上下文訪問,使用訊號量保護該共享資源非常合適,如果對共巷資源的訪問時間非常短,自旋鎖也可以。但是如果被保護的共享資源需要在中斷上下文訪問(包括底半部即中斷處理控制代碼和頂半部即軟中斷),就必須使用自旋鎖。

自旋鎖保持期間是搶占失效的,而訊號量和讀寫訊號量保持期間是可以被搶占的。自旋鎖只有在核心可搶占或smp的情況下才真正需要,在單cpu且不可搶占的核心下,自旋鎖的所有操作都是空操作。

跟互斥鎖一樣,乙個執行單元要想訪問被自旋鎖保護的共享資源,必須先得到鎖,在訪問完共享資源後,必須釋放鎖。:自旋鎖不能遞迴使用。如果在獲取自旋鎖時,沒有任何執行單元保持該鎖,那麼將立即得到鎖;如果在獲取自旋鎖時鎖已經有保持者,那麼獲取鎖操作將自旋在那裡,直到該自旋鎖的保持者釋放了鎖。

無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有乙個保持者,也就說,在任何時刻最多只能有乙個執行單元獲得鎖。對於互斥鎖而言,如果資源已經被占用,其它的資源申請程序只能進入sleep狀態。但是自旋鎖不會引起呼叫者sleep。

自旋鎖是一種比較低階的保護資料結構或**片段的原始方式,這種鎖可能存在兩個問題:死鎖和過多占用cpu資源。

semaphore(訊號量)

linux核心的訊號量在概念和原理上與使用者態的systemv的ipc機制訊號量是一樣的,但是它絕不可能在核心之外使用,因此它與systemv的ipc機制訊號量毫不相干。

linux提供兩種訊號量:

(1) 核心訊號量,由核心控制路徑使用

(2)使用者態程序使用的訊號量,這種訊號量又分為posix訊號量和system v訊號量。

posix訊號量又分為有名訊號量和無名訊號量。

有名訊號量,其值儲存在檔案中, 所以它可以用於執行緒也可以用於程序間的同步。無名訊號量,其值儲存在記憶體中。

倘若對訊號量沒有以上的全面認識的話,你就會很快發現自己在訊號量的森林裡迷失了方向。

下面說本文要說的核心訊號量。至於使用者態訊號量是用於ipc的

訊號量在建立時需要設定乙個初始值,表示同時可以有幾個任務可以訪問該訊號量保護的共享資源,初始值為1就變成互斥鎖(mutex),即同時只能有乙個任務可以訪問訊號量保護的共享資源。乙個任務要想訪問共享資源,首先必須得到訊號量,獲取訊號量的操作將把訊號量的值減1,若當前訊號量的值為負數,表明無法獲得訊號量,該任務必須掛起在該訊號量的等待佇列等待該訊號量可用;若當前訊號量的值為非負數,表示可以獲得訊號量,因而可以立刻訪問被該訊號量保護的共享資源。當任務訪問完被訊號量保護的共享資源後,必須釋放訊號量,釋放訊號量通過把訊號量的值加1實現,如果訊號量的值為非正數,表明有任務等待當前訊號量,因此它也喚醒所有等待該訊號量的任務。

rw_semaphore (讀寫訊號量)

讀寫訊號量對訪問者進行了細分,或者為讀者,或者為寫者,讀者在保持讀寫訊號量期間只能對該讀寫訊號量保護的共享資源進行讀訪問,如果乙個任務除了需要讀,可能還需要寫,那麼它必須被歸類為寫者,它在對共享資源訪問之前必須先獲得寫者身份,寫者在發現自己不需要寫訪問的情況下可以降級為讀者。讀寫訊號量同時擁有的讀者數不受限制,也就說可以有任意多個讀者同時擁有乙個讀寫訊號量。如果乙個讀寫訊號量當前沒有被寫者擁有並且也沒有寫者等待讀者釋放訊號量,那麼任何讀者都可以成功獲得該讀寫訊號量;否則,讀者必須被掛起直到寫者釋放該訊號量。如果乙個讀寫訊號量當前沒有被讀者或寫者擁有並且也沒有寫者等待該訊號量,那麼乙個寫者可以成功獲得該讀寫訊號量,否則寫者將被掛起,直到沒有任何訪問者。因此,寫者是排他性的,獨占性的。

讀寫訊號量有兩種實現,一種是通用的,不依賴於硬體架構,因此,增加新的架構不需要重新實現它,但缺點是效能低,獲得和釋放讀寫訊號量的開銷大;另一種是架構相關的,因此效能高,獲取和釋放讀寫訊號量的開銷小,但增加新的架構需要重新實現。在核心配置時,可以通過選項去控制使用哪一種實現。

seqlock(順序鎖)

用於能夠區分讀與寫的場合,並且是讀操作很多、寫操作很少,寫操作的優先權大於讀操作。

seqlock的實現思路是,用乙個遞增的整型數表示sequence。寫操作進入臨界區時,sequence++;退出臨界區時,sequence再++。寫操作還需要獲得乙個鎖(比如mutex),這個鎖僅用於寫寫互斥,以保證同一時間最多只有乙個正在進行的寫操作。

當sequence為奇數時,表示有寫操作正在進行,這時讀操作要進入臨界區需要等待,直到sequence變為偶數。讀操作進入臨界區時,需要記錄下當前sequence的值,等它退出臨界區的時候用記錄的sequence與當前sequence做比較,不相等則表示在讀操作進入臨界區期間發生了寫操作,這時候讀操作讀到的東西是無效的,需要返回重試。

seqlock寫寫是必須要互斥的。但是seqlock的應用場景本身就是讀多寫少的情況,寫衝突的概率是很低的。所以這裡的寫寫互斥基本上不會有什麼效能損失。

而讀寫操作是不需要互斥的。seqlock的應用場景是寫操作優先於讀操作,對於寫操作來說,幾乎是沒有阻塞的(除非發生寫寫衝突這一小概率事件),只需要做sequence++這一附加動作。而讀操作也不需要阻塞,只是當發現讀寫衝突時需要retry。

seqlock的乙個典型應用是時鐘的更新,系統中每1毫秒會有乙個時鐘中斷,相應的中斷處理程式會更新時鐘(見《linux時鐘**》)(寫操作)。而使用者程式可以呼叫gettimeofday之類的系統呼叫來獲取當前時間(讀操作)。在這種情況下,使用seqlock可以避免過多的gettimeofday系統呼叫把中斷處理程式給阻塞了(如果使用讀寫鎖,而不用seqlock的話就會這樣)。中斷處理程式總是優先的,而如果gettimeofday系統呼叫與之衝突了,那使用者程式多等等也無妨。

rwlock(讀寫鎖)

讀寫鎖實際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作。這種鎖相對於自旋鎖而言,能提高併發性,因為在多處理器系統中,它允許同時有多個讀者來訪問共享資源,最大可能的讀者數為實際的邏輯cpu數。寫者是排他性的,乙個讀寫鎖同時只能有乙個寫者或多個讀者(與cpu數相關),但不能同時既有讀者又有寫者。

在讀寫鎖保持期間也是搶占失效的。

如果讀寫鎖當前沒有讀者,也沒有寫者,那麼寫者可以立刻獲得讀寫鎖,否則它必須自旋在那裡,直到沒有任何寫者或讀者。如果讀寫鎖沒有寫者,那麼讀者可以立即獲得該讀寫鎖,否則讀者必須自旋在那裡,直到寫者釋放該讀寫鎖。

比較:訊號量和讀寫訊號量適合於保持時間較長的情況,它們會導致呼叫者睡眠,因而自旋鎖適合於保持時間非常短的情況;

自旋鎖可以用於中斷,不能用於程序上下文(會引起死鎖),而訊號量不允許使用在中斷中,而可以用於程序上下文;

自旋鎖保持期間是搶占失效的,自旋鎖被持有時,核心不能被搶占,而訊號量和讀寫訊號量保持期間是可以被搶占的。

訊號量主要適用於程序間通訊,當然,也可用於執行緒間通訊。而互斥鎖只能用於執行緒間通訊。(程序間互斥鎖可以用其他方法實現,但那就不是核心提供的支援了)

MySQL行鎖 表鎖 悲觀鎖 樂觀鎖的特點與應用

轉至 我們在運算元據庫的時候,可能會由於併發問題而引起的資料的不一致性 資料衝突 如何保證資料併發訪問的一致性 有效性,是所有資料庫必須解決的乙個問題,鎖的衝突也是影響資料庫併發訪問效能的乙個重要因素,從這一角度來說,鎖對於資料庫而言就顯得尤為重要。mysql鎖概述 相對其他資料庫而言,mysql的...

java鎖的種類以及辨析(一) 自旋鎖

自旋鎖是採用讓當前執行緒不停地的在迴圈體內執行實現的,當迴圈的條件被其他執行緒改變時 才能進入臨界區。如下 public class spinlock public void unlock 使用了cas原子操作,lock函式將owner設定為當前執行緒,並且 原來的值為空。unlock函式將owne...

一維碼的種類 特點及限制

在實際工作中我們跟據需求來挑選合適我們的條碼型別。1 code128,code39 extended,code93 extended 支援全ascii碼,code128有a b c三種字符集,每種字符集支援一部分,大致是這樣的,a字符集支援支援a z 26個大寫字母 0 9 9個數字及一些特殊字元 ...