2015年6月2日 星期二

Linux spinlock

參考自 : http://blog.csdn.net/wh_19910525/article/details/11536279

在Linux內核中何時使用spin_lock,何時使用spin_lock_irqsave很容易混淆。首先看一下代碼是如何實現的。
spin_lock的調用關係
     spin_lock 
            |
           + -----> raw_spin_lock 

  1. static  inline  void  __raw_spin_lock(raw_spinlock_t *lock)  
  2. {  
  3.         preempt_disable();  
  4.         spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);  
  5.         LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);  
  6. }  
spin_lock_irq的調用關係
    spin_lock_irq
                |
               +-------> raw_spin_lock_irq

  1. static  inline  void  __raw_spin_lock_irq(raw_spinlock_t *lock)  
  2. {  
  3.         local_irq_disable( );  
  4.         preempt_disable();  
  5.         spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);  
  6.         LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);  
  7. }  
可以看出來他們兩者只有一個差別:是否調用local_irq_disable()函數, 即是否禁止本地中斷。
在任何情況下使用spin_lock_irq都是安全的。因為它既禁止本地中斷,又禁止內核搶占。
spin_lock比spin_lock_irq速度快,但是它並不是任何情況下都是安全的。
舉個例子:進程A中調用了spin_lock(&lock)然後進入臨界區,此時來了一個中斷(interrupt),
該中斷也運行在和進程A相同的CPU上,並且在該中斷處理程序中恰巧也會spin_lock(&lock)
試圖獲取同一個鎖。由於是在同一個CPU上被中斷,進程A會被設置為TASK_INTERRUPT狀態,
中斷處理程序無法獲得鎖,會不停的忙等,由於進程A被設置為中斷狀態,schedule()進程調度就
無法再調度進程A運行,這樣就導致了死鎖!
但是如果該中斷處理程序運行在不同的CPU上就不會觸發死鎖。因為在不同的CPU上出現中斷不會導致
進程A的狀態被設為TASK_INTERRUPT,只是換出。當中斷處理程序忙等被換出後,進程A還是有機會
獲得CPU,執行並退出臨界區。
所以在使用spin_lock時要明確知道該鎖不會在中斷處理程序中使用。
====================
使用spin_lock_irqsave在於你不期望在離開臨界區後,改變中斷的開啟,關閉狀態!進入臨界區是關閉的,離開後它同樣應該是關閉的!
如果自旋鎖在中斷處理函數中被用到,那麼在獲取該鎖之前需要關閉本地中斷,spin_lock_irqsave只是下列動作的一個便利接口:
1保存本地中斷狀態
2關閉本地中斷
3獲取自旋鎖 解鎖時通過spin_unlock_irqrestore完成釋放鎖、恢復本地中斷到之前的狀態等工作

--------------------
還有一對spin_lock_irq和spin_unlock_irq
如果你確定在獲取鎖之前本地中斷是開啟的,那麼就不需要保存中斷狀態,解鎖的時候直接將本地中斷啟用就可以啦

沒有留言: