1. 程式人生 > 其它 >Linux核心鎖機制——訊號量、互斥鎖、自旋鎖、原子操作詳解

Linux核心鎖機制——訊號量、互斥鎖、自旋鎖、原子操作詳解

技術標籤:1000道程式設計師常見問題解析多執行緒linux互斥鎖自旋鎖原子操作

閱讀目錄

一、訊號量(struct semaphore):
二、互斥鎖:(mutex_lock)
三、自旋鎖(spin_lock):
四、原子操作:
4.1、Linux原子概念:
4.2、Linux核心兩組原子操作介面

參考資料

90分鐘搞懂執行緒鎖、程序鎖以及分散式鎖
還有人弄不懂執行緒鎖、程序鎖和分散式鎖?
聊點通俗的自旋鎖,互斥鎖,原子操作,CAS

正文

linux核心中有多種核心鎖,核心鎖的作用是:

多核處理器下,會存在多個程序處於核心態的情況,而在核心態下,程序是可以訪問所有核心資料的,因此要對共享資料進行保護,即互斥處理;

linux核心鎖機制有訊號量、互斥鎖、自旋鎖還有原子操作。

一、訊號量(struct semaphore):

是用來解決程序/執行緒之間的同步和互斥問題的一種通訊機制,是用來保證兩個或多個關鍵程式碼不被併發呼叫。

訊號量(Saphore)由一個值和一個指標組成,指標指向等待該訊號量的程序。訊號量的值表示相應資源的使用情況。訊號量S>=0時,S表示可用資源的數量。執行一次P操作意味著請求分配一個資源,因此S的值減1;當S<0時,表示已經沒有可用資源,S的絕對值表示當前等待該資源的程序數。請求者必須等待其他程序釋放該類資源,才能繼續執行。而執行一個V操作意味著釋放一個資源,因此S的值加1;若S<0,表示有某些程序正在等待該資源,因此要喚醒一個等待狀態的程序,使之執行下去。

訊號量是選擇睡眠的方式來對共享工作停止訪問的。

也就是說訊號量通過PV操作同步解決了程序/執行緒對臨界資源利用的衝突問題;

二、互斥鎖:(mutex_lock)

互斥鎖同樣也是對執行緒間(不能對程序)同步和互斥的一種另一種機制。

互斥鎖更多的是強調對共享資源的鎖定作用,當一個執行緒佔用了當前共享資源,使用互斥鎖將其lock住之後,其他執行緒就無法訪問,必須等到unlock之後,其他執行緒才能利用共享資源裡面的內容;

互斥鎖是選擇睡眠的方式來對共享工作停止訪問的。

也就是說互斥鎖通過對共享資源的鎖定和互斥解決利用資源衝突問題;

三、自旋鎖(spin_lock):

是為實現保護共享資源而提出一種鎖機制。其實,自旋鎖與互斥鎖比較類似,它們都是為了解決對某項資源的互斥使用。無論是互斥鎖,還是自旋鎖,在任何時刻,最多隻能有一個保持者,也就說,在任何時刻最多隻能有一個執行單元獲得鎖。但是兩者在排程機制上略有不同。對於互斥鎖,如果資源已經被佔用,資源申請者只能進入睡眠狀態。但是自旋鎖不會引起呼叫者睡眠,如果自旋鎖已經被別的執行單元保持,呼叫者就一直迴圈在那裡看是否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是因此而得名。

四、原子操作:

4.1、Linux原子概念:

所謂原子操作,就是“不可中斷的一個或一系列操作”。

原子操作,就是不能被更高等級中斷搶奪優先的操作。你既然提這個問題,我就說深一點。由於作業系統大部分時間處於開中斷狀態,所以,一個程式在執行的時候可能被優先順序更高的執行緒中斷。而有些操作是不能被中斷的,不然會出現無法還原的後果,這時候,這些操作就需要原子操作。就是不能被中斷的操作。

硬體級的原子操作:在單處理器系統(UniProcessor)中,能夠在單條指令中完成的操作都可以認為是“原子操作”,因為中斷只發生在指令邊緣。在多處理器結構中(Symmetric Multi-Processor)就不同了,由於系統中有多個處理器獨立執行,即使能在單條指令中完成的操作也有可能受到干擾。在X86平臺生,CPU提供了在指令執行期間對匯流排加鎖的手段。CPU上有一根引線#HLOCK pin連到北橋,如果組合語言的程式中在一條指令前面加上字首"LOCK",經過彙編以後的機器程式碼就使CPU在執行這條指令的時候把#HLOCK pin的電位拉低,持續到這條指令結束時放開,從而把匯流排鎖住,這樣同一總線上別的CPU就暫時不能通過匯流排訪問記憶體了,保證了這條指令在多處理器環境中的原子性。對於其他平臺的CPU,實現各不相同,有的是通過關中斷來實現原子操作(sparc),有的通過CMPXCHG系列的指令來實現原子操作(IA64)。本文主要探討X86平臺下原子操作的實現。

4.2、Linux核心兩組原子操作介面:

1、原子整數操作
原子操作通常針對int或bit型別的資料,但是Linux並不能直接對int進行原子操作,而只能通過atomic_t的資料結構來進行。

定義於#include<asm/atomic.h>

在這裡插入圖片描述

圖1.1 核心中的整數原子操作函式

2、核心中提供的一些主要位原子操作函式。同時核心還提供了一組與上述操作對應的非原子位操作函式,名字前多兩下劃線。由於不保證原子性,因此速度可能執行更快。

定義於#include<asm/bitops.h>

 1 void atomic_set(atomic_t *v,int i);    //設定原子變數v的值為i
 2 atomic_t v = ATOMIC_INIT(0);     //定義原子變數v,並初始化為0;
 3 
 4 atomic_read(atomic_t* v);     //返回原子變數v的值;
 5 
 6 void atomic_add(int i, atomic_t* v);     //原子變數v增加i;
 7 void atomic_sub(int i, atomic_t* v);    
 8 
 9 void atomic_inc(atomic_t* v);     //原子變數增加1;
10 void atomic_dec(atomic_t* v);     
11 
12 int atomic_inc_and_test(atomic_t* v);        //先自增1,然後測試其值是否為0,若為0,則返回true,否則返回false;
13 int atomic_dec_and_test(atomic_t* v);       //先自減1,然後測試其值是否為0,若為0,則返回true,否則返回false 
14 int atomic_sub_and_test(int i, atomic_t* v);     //先減i,然後測試其值是否為0,若為0,則返回true,否則返回false;
15 //注意:只有自加,沒有加操作
16 
17 int atomic_add_return(int i, atomic_t* v);   //v的值加i後返回新的值;
18 int atomic_sub_return(int i, atomic_t* v);  
19 int atomic_inc_return(atomic_t* v);     //v的值自增1後返回新的值;
20 int atomic_dec_return(atomic_t* v);    

例項程式碼:

在scull_open 函式和scull_close函式中:

如果沒有程序在使用該驅動 ,原子變數值 為 1 ,將原子變數減 一 為 0 ,函式返回 true ,再 !true 為 假 ,if 裡面的程式碼不執行;這樣打開了、並使用該驅動, 原子變數變為 0;

如果再有程序來開啟驅動程式,0-1 = 負1,返回 false ,if 條件成立,執行裡面的程式碼,將原子變數加一恢復到 0,程式返回;

最後, 在應用程式退出時 close 函式, 自增 恢復原子變數值為 1:

1 static atomic_t scull_available = ATOMIC_INIT(1);      //init atomic
 2 
 3 int scull_open(struct inode *inode, struct file *filp)
 4 {
 5     struct scull_dev *dev;         // device information
 6 
 7     dev = container_of(inode->i_cdev, struct scull_dev, cdev);
 8     filp->private_data = dev;         // for other methods 
 9     if(!atomic_dec_and_test(&scull_available)){
10         atomic_inc(&scull_available);
11         return -EBUSY;
12     }
13     return 0;         // success 
14 }
15 
16 int scull_release(struct inode *inode, struct file *filp)
17 {
18     atomic_inc(&scull_available);
19     return 0;
20 }

複製程式碼

以上總結幾點:

互斥鎖與訊號量的區別:

1、訊號量一般以同步的方式對共享資源進行控制,而互斥鎖通過互斥的方式對共享資源對其進行控制;

2、訊號量可以對程序的共享資源進行控制,而互斥鎖不行;

3、訊號量的值為非負整數,而互斥鎖的值只能為0或1;

4、互斥量的加鎖和解鎖必須由同一執行緒分別對應使用,訊號量可以由一個執行緒釋放,另一個執行緒得到;mutex和二值訊號量的區別在於mutex必須是同一個程序來釋放

自旋鎖與互斥鎖的區別:

1、因為自旋鎖不會引起呼叫者睡眠,所以效率比較高

2、自旋鎖比較適用於鎖使用者保持鎖時間比較短的情況。

3、自旋鎖容易造成死鎖,所以需要安全使用它;