Release log:
2021-05-07 五: 完成初版
2021-05-10 一: 添加自旋鎖和信号量的對比
原文位址
Linux 核心中自旋鎖的使用
在 Linux 核心中,要使用自旋鎖需要包含的檔案
<linux/spinlock.h>
,鎖的資料類型為
spinlock_t
。
自旋鎖的聲明以及初始化
void spin_lock_init(spinlock_t *lock);
自旋鎖的擷取以及釋放
void spin_lock(spinlock_t *lock);
void spin_unlock(spinlock_t *lock);
注意: 所有自旋鎖等待在本質上是不可中斷的
使用自旋鎖的核心規則
- 任何擁有自旋鎖的代碼都必須是原子的。它不能休眠,事實上,它不能因為任何原因放棄處理器,除了服務中斷以外(某些情況下此時也不能放棄處理器)
- 核心搶占的情況由自旋鎖代碼本身處理。任何時候,隻要核心代碼擁有自旋鎖,在相關處理器上的搶占就會被禁止
- 在擁有鎖的時候避免休眠有時候很難做到,當我們編寫需要自旋鎖下執行的代碼時,必須注意每一個所調用的函數
- 擁有鎖的時間越短越好
自旋鎖函數
鎖定一個自旋鎖的函數
void spin_lock(spinlock_t *lock);
/* 這是一個宏,獲得自旋鎖之前禁止中斷(隻在本地處理器上),而先前的中斷狀态儲存在 flags 中 */
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
void spin_lock_irq(spinlock_t *lock);
/* 在獲得鎖之前禁止軟體中斷,但是會讓硬體中斷保持打開 */
void spin_lock_bh(spinlock_t *lock);
如果我們有一個自旋鎖,它可以被運作在(硬體或軟體)中斷上下文中的代碼獲得,則必須使用某個禁止中斷的 spin_lock 形式
釋放自旋鎖的方法需要嚴格對應
void spin_unlock(spinlock_t *lock);
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);
另外,還有如下非阻塞的自旋鎖操作
int spin_trylock(spinlock_t *lock);
int spin_trylock_bh(spinlock_t *lock);
讀取者/寫入者自旋鎖
讀取者/寫入者鎖的類型為 rwlock_t,在
<linux/spinlock.h>
中定義
自旋鎖的初始化
void rwlock_init(rwlock_t *lock);
讀取者對應的函數(這裡沒有 read_trylock 可用)
void read_lock(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_lock_irq(rwlock_t *lock);
void read_lock_bh(rwlock_t *lock);
void read_unlock(rwlock_t *lock);
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_bh(rwlock_t *lock);
寫入者對應的函數
void write_lock(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_lock_irq(rwlock_t *lock);
void write_lock_bh(rwlock_t *lock);
void write_trylock(rwlock_t *lock);
void write_unlock(rwlock_t *lock);
void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
和 rwsem 類似,讀取者/寫入者鎖可能造成讀取者饑餓
對比信号量與自旋鎖
自旋鎖可以在不能休眠的代碼中使用,比如中斷處理例程。自旋鎖如果被其他人獲得,則代碼進入忙循環并重複檢查這個鎖,直到該鎖可用為止。信号量是一種睡眠鎖,如果一個任務試圖獲得一個已被持有的信号量時,信号量會将其推入等待隊列,然後讓其睡眠。信号量不能在中斷上下文中被排程
自旋鎖會關閉搶占,而信号量不會
在正确使用的情況下,自旋鎖通常可以提供比信号量更高的性能
需求 建議的加鎖方式
低開銷加鎖 優先使用自旋鎖
短期鎖定 優先使用自旋鎖
中斷上下文中加鎖 使用自旋鎖
長期加鎖 優先使用信号量
持有鎖是需要睡眠、排程 使用信号量