自旋鎖:和訊號量不同,自旋鎖可以在不能休眠的代碼中使用,比如中斷處理常式。自旋鎖通常實現為某個整數值中的某個位。希望獲得某特定鎖的代碼測試相關的位。如果鎖可用,則鎖定位被設定,而代碼進入臨界區。相反,如果鎖定被其他人獲得,則代碼進入忙迴圈並重複檢查這個鎖。以上測試並設定的操作必須以原子方式進行。
自旋鎖API
使用自旋鎖必須包含標頭檔<linux/spinlock.h>,鎖的類型為spinlock_t類型
定義同時完成初始化:
spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
或在運行前初始化:
void spin_lock_init(spinlock_t *lock);
在進入臨界區之前,通過如下代碼獲得需要的鎖:
void spin_lock(spinlock_t *lock);
上面兩條語句一般同時運用:
void spin_lock_init(spinlock_t *lock);
void spin_lock(spinlock_t *lock);
注意所有自旋鎖在本質都是不可中斷的。一旦調用spin_lock,在獲得鎖之前一直為自旋狀態。
釋放鎖void spin_unlock(spinlock_t *lock);
在驅動擁有自旋鎖工作在臨界區的時候下可能會出現如下情況,使驅動程式丟掉處理器從而影響系統效能或導致系統死結。例如調用一個函數(copy_from_user),這個函數導致進程進入休眠狀態。或者,發生了核心搶佔,更高優先順序的進程搶佔了處理器,這樣在這段時間內我們的驅動程式將始終持有這個自旋鎖,這是如果有其他進程試圖來獲得該鎖,那麼該進程要等待很長時間。
使用於自旋鎖的規則:
1.任何自旋鎖的代碼必須是原子的,它不能休眠,事實上它不能因為任何原因放棄處理器,除了中斷處理常式外(某些情況此時也不可以放棄處理器)。
2.核心搶佔的情況有自旋鎖代碼本身進行處理——應該理解為對於核心搶佔的處理在自旋鎖所保護的臨界區內進行,所以任何時候,只要核心代碼擁有自旋鎖,在相關處理器上的搶佔就被禁止.
3.訊號量可以用於休眠的情況,而自旋鎖基本上不可以用於休眠。核心中有許多用於休眠的函數,如在於使用者空間發生資料互動時,等待I/O口完成任務時,kmallo分配記憶體時。所以在編寫需要在自旋鎖下執行的代碼時,必須注意每一個調用函數。
4.在中斷處理常式中:當我們的驅動程式在擁有自旋鎖的情況下對裝置進行訪問時,裝置出現一個中斷,它導致中斷處理常式開始執行,在中斷處理常式訪問裝置前也需要獲得該鎖,這時中斷常式自旋時,非終端代碼將沒有任何機會來釋放這個鎖,處理器將永遠自旋下去。所以我們我們需要在擁有該鎖時,禁止所有中斷(僅在本地CPU上)。
5.自旋鎖必須在可能的最短時間內擁有。
自旋鎖函數
void spin_lock(spinlock_t *lock);
void spin_lock_irqsave(spinlock_t *lock,int flags); //獲得鎖,禁止中斷,儲存當前中斷狀態
void spin_lock_irq(spinlock_t *spin);//獲得鎖,禁止中斷,在確保沒有任何其他代碼禁止本地處理器中斷時運用
void spin_lock_bh(spinlock_t *lock);//禁止非強制中斷,但是會讓硬中斷保持開啟。
如果我們有一個自旋鎖,它可以被運行在(硬體或軟體中斷)上下文中的代碼獲得,則必須使用某個禁止中斷的spin_lock形式。如果我們不會在硬體中斷處理常式中訪問自旋鎖,但可能在軟體中斷中訪問,則應該使用spin_lock_bh(spinlock_t *lock)以避免死結同時還能服務硬中斷。
釋放鎖spin_unlock_irqrestore(spinlock_t *lock,int flags);
spin_unlock_irq(spinlock_t *lock);
int spin_trylock(spinlock_t *lock);
int spin_trylock_bh(spinlock_t *lock);
不會阻塞,在沒獲得鎖的情況下,直接返回。
讀寫鎖(略)
關於鎖的一些重要規則,這些規則只有到實踐中才能夠真正掌握,這裡只是瞭解一下大致的概念。
1.如果獲得鎖的進程要調用其他同樣試圖獲得該鎖的進程,代碼就會死結。不論訊號還自旋鎖都不允許擁有者第二次獲得這個鎖。
2.對於裝置內部的靜態函數在編寫時,都假定調用者已經擷取了相關的鎖,而提供給外部調用的函數則必須顯示的處理鎖定,在編寫那些假定調用者已處理了鎖定的內建函式時,我們應該顯示地說明這種假定。
在scull的例子中,所採用的規則是:由系統調用直接調用的那些函數均要獲得訊號量,以便保護要訪問的資料結構。而其他內建函式只會由其他的scull函數調用,且假定訊號量已經獲得。
3.保持對鎖的順序獲得,千萬避免交叉申請,否則會導致申請的進程全部死結。
4.先獲得局部鎖,比如裝置鎖,然後再獲得深度更深的鎖。
5.如果我們擁有訊號量和自旋鎖的組合,則必須首先獲得訊號量,因為如果先獲得自旋鎖,在保護區內down掉訊號量(可能導致休眠)則對於鎖來說是嚴重錯誤的。