7.2 自旋鎖、 讀寫自旋鎖、順序鎖、讀-拷貝-更新
阿新 • • 發佈:2018-12-12
自旋鎖
作用:對臨界資源進行互斥手訪問
工作方式:為了獲得一個自旋鎖,在某CPU 上執行的程式碼需先執行一個原子操作(該操作完成之前其他執行單元不可能訪問這個記憶體變數),該操作測試並設定某個記憶體變數,如果測試結果表明鎖已經空閒,則程式獲得這個自旋鎖並繼續執行;如果測試結果表明鎖仍被佔用,程式將在一個小的迴圈內重複這個“測試並設定”操作(自旋),當自旋鎖的持有者通過重置該變數釋放這個自旋鎖後,某個等待的“測試並設定”操作向其呼叫者報告鎖已釋放。
自旋鎖相關的操作
spinlock_t spin;//定義 spin_lock_init(lock)//初始化 spin_lock(lock)//獲得自旋鎖,如果得到即返回該自旋鎖,否則,原地等待 spin_trylock(lock)//獲得自旋鎖,如果得到即返回真,否則,返回假 spin_unlock(lock)//釋放 //定義一個自旋鎖 spinlock_t lock; spin_lock_init(&lock); spin_lock (&lock) ; //獲取自旋鎖,保護臨界區 ...//臨界區 spin_unlock (&lock) ; //解鎖
使用自旋鎖注意事項
- 自旋鎖實際上是忙等鎖,當鎖不可用時,CPU一直迴圈執行“測試並設定”該鎖直到可用而取得該鎖,CPU在等待自旋鎖時不做任何有用的工作,僅僅是等待。因此,只有在佔用鎖的時間極短的情況下,使用自旋鎖才是合理的。當臨界區很大或有共享裝置的時候,需要較長時間佔用鎖,使用自旋鎖會降低系統的效能。
- 自旋鎖可能導致系統死鎖。引發這個問題最常見的情況是遞迴使用一個自旋鎖,即如果一個已經擁有某個自旋鎖的CPU 想第二次獲得這個自旋鎖,則該CPU 將死鎖。此外,如果程序獲得自旋鎖之後再阻塞,也有可能導致死鎖的發生。copy_from_user()、copy_to_user()和kmalloc()等函式都有可能引起阻塞,因此在自旋鎖的佔用期間不能呼叫這些函式。
讀寫自旋鎖
讀寫自旋鎖在寫操作方面,只能最多有一個寫程序,在讀操作方面,同時可以有多個讀執行單元(允許讀的併發)。當然,讀和寫也不能同時進行
讀寫自旋鎖涉及的操作
rwlock_t my_rwlock;/*定義*/ rwlock_t my_rwlock = RW_LOCK_UNLOCKED; /* 靜態初始化*/ rwlock_init(&my_rwlock); /* 動態初始化*/ //讀鎖定 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); int 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); rwlock_t lock; //定義rwlock rwlock_init(&lock); //初始化rwlock //讀時獲取鎖 read_lock(&lock); ... //臨界資源 read_unlock(&lock); //寫時獲取鎖 write_lock_irqsave(&lock, flags); ... //臨界資源 write_unlock_irqrestore(&lock, flags);
順序鎖
允許讀寫同時進行,因而更大地提高了併發性,但是,寫執行單元與寫執行單元之間仍然是互斥的,而且要求被保護的共享資源不含有指標。讀執行單元在讀操作期間,寫執行單元已經發生了寫操作,那麼,讀執行單元必須重新讀取資料,以便確保得到的資料是完整的。
順序鎖操作
//獲得寫順序鎖
void write_seqlock(seqlock_t *sl);
int write_tryseqlock(seqlock_t *sl);
write_seqlock_irqsave(lock, flags)
//write_seqlock_irqsave() = loal_irq_save() + write_seqlock()
write_seqlock_irq(lock)
//write_seqlock_irq() = local_irq_disable() + write_seqlock()
write_seqlock_bh(lock)
//write_seqlock_bh() = local_bh_disable() + write_seqlock()
//釋放寫順序鎖
void write_sequnlock(seqlock_t *sl);
write_sequnlock_irqrestore(lock, flags)
//write_sequnlock_irqrestore() = write_sequnlock() + local_irq_restore()
write_sequnlock_irq(lock)
//write_sequnlock_irq() = write_sequnlock() + local_irq_enable()
write_sequnlock_bh(lock)
//write_sequnlock_bh() = write_sequnlock() + local_bh_enable()
//使用寫順序鎖
write_seqlock(&seqlock_a);
...//寫操作程式碼塊
write_sequnlock(&seqlock_a);
//讀順序鎖開始
unsigned read_seqbegin(const seqlock_t *sl);
read_seqbegin_irqsave(lock, flags)
//read_seqbegin_irqsave() = local_irq_save() + read_seqbegin()
//重讀
int read_seqretry(const seqlock_t *sl, unsigned iv);
read_seqretry_irqrestore(lock, iv, flags)
read_seqretry_irqrestore() = read_seqretry() + local_irq_restore()
//使用讀順序鎖
do {
seqnum = read_seqbegin(&seqlock_a);
//讀操作程式碼塊
...
} while (read_seqretry(&seqlock_a, seqnum));
讀-拷貝-更新(RCU)
對於被RCU保護的共享資料結構,讀執行單元不需要獲得任何鎖就可以訪問它
使用RCU的寫執行單元在訪問它前需首先複製一個副本,然後對副本進行修改,最後使用一個回撥機制在適當的時機把指向原來資料的指標重新指向新的被修改的資料,這個時機就是所有引用該資料的CPU 都退出對共享資料的操作的時候。
讀執行單元沒有任何同步開銷,而寫執行單元的同步開銷則取決於使用的寫執行單元間的同步機制。