linux內核中有多種內核鎖,內核鎖的作用是: 多核處理器下,會存在多個進程處於內核態的情況,而在內核態下,進程是可以訪問所有內核數據的,因此要對共用數據進行保護,即互斥處理; linux內核鎖機制有信號量、互斥鎖、自旋鎖還有原子操作。 一、信號量(struct semaphore): 是用來解決進 ...
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):
是為實現保護共用資源而提出一種鎖機制。其實,自旋鎖與互斥鎖比較類似,它們都是為瞭解決對某項資源的互斥使用。無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執行單元獲得鎖。但是兩者在調度機制上略有不同。對於互斥鎖,如果資源已經被占用,資源申請者只能進入睡眠狀態。但是自旋鎖不會引起調用者睡眠,如果自旋鎖已經被別的執行單元保持,調用者就一直迴圈在那裡看是否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是因此而得名。
四、原子操作:
Reference:
http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html
http://blog.csdn.net/vividonly/article/details/6599502
http://blog.csdn.net/williamwang2013/article/details/8517380
http://blog.csdn.net/yikai2009/article/details/8650221
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.2 內核中的位原子操作函數
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、互斥量的加鎖和解鎖必須由同一線程分別對應使用,信號量可以由一個線程釋放,另一個線程得到;
自旋鎖與互斥鎖的區別:
1、因為自旋鎖不會引起調用者睡眠,所以效率比較高
2、自旋鎖比較適用於鎖使用者保持鎖時間比較短的情況。
3、自旋鎖容易造成死鎖,所以需要安全使用它;