linux設備驅動中的併發控制——自旋鎖

1 自旋鎖的使用
linux系統中與自旋鎖相關的操作主要有如下四種:
(1)定義自旋鎖
spinlock_t spin;
(2)初始化自旋鎖
spin_lock_init(lock)
//該宏用於動態初始化自旋鎖lock

(3)獲取自旋鎖
spin_lock(lock)//該宏用於獲取自旋鎖lock,如果立馬獲得鎖,就立刻返回,
//否則將自鎖在那裏,直到該自旋鎖的持有者釋放。
spin_trylock(lock)//該宏用於嘗試獲得自旋鎖。
(4)釋放自旋鎖
spin_unlock(lock)//該宏用於釋放自旋鎖,與spin_lock及spin_trylock配對使用
自旋鎖一般這樣使用:
//定義自旋鎖
spinlock_t lock;
spin_lock_init(&lock);
spin_lock(&lock);//獲取自旋鎖保護臨界區
...//臨界區
spin_unlock(&lock);//解鎖
說明:

    (1)自旋鎖主要應用於smp或單cpu但內存可搶佔的情況。
    (2)自旋鎖得到的代碼路徑在執行臨界區的時候可能受到中斷和底半部的影響,可以通過自旋鎖的下列衍生解決
   spin_lock_irq()=spin_lock()+local_irq_disable()
   spin_unlock_irq()=spin_unlock()+local_irq_enable()
   spin_lock_irqsave()=spin_lock()+local_irq_save()
   spin_unlock_irqrestore() = spin_unlock()+local_irq_restore()
   spin_lock_bh()=spin_lock()+local_bh_disable()
   spin_unlock_bh()=spin_unlock()+local_bh_enable()
    (3)自旋鎖是忙等鎖,只有在佔用鎖時間極短的情況下使用纔是合理的
    (4)自旋鎖可能導致系統崩潰,使用copy_to_user(),copy_from_user()和kmalloc()函數可能引起阻塞,因此在自旋鎖的佔用期間不能調用這些函數。
    代碼清單7.2 使用自旋鎖使設備只能被一個進程打開

  1. int xxx_count = 0;//定義文件打開次數計數  
  2.        static int xxx_open(struct inode *inode,struct file *filp)  
  3.        {  
  4.         ...  
  5.         spinlock(&xxx_lock);  
  6.         if(xxx_count)//已打開  
  7.         {  
  8.             spin_unlock(&xxx_lock);  
  9.             return - EBUSY;  
  10.         }  
  11.         xxx_count++;//增加使用計數  
  12.         spin_unlock(&xxx_lock);  
  13.         ...  
  14.         return 0;//成功  
  15.         }  
  16.         static int xxx_release(struct inode *inode,struct file *filp)  
  17.         {  
  18.             ...  
  19.             spinlock(&xxx_lock);  
  20.             xxx_count--;//減少使用計數  
  21.               
  22.             spin_unlock(&xxx_lock);  
  23.             return 0;//成功  
  24.               
  25.         } 
  2 讀寫自旋鎖
     讀寫自旋鎖是一種比自旋鎖力度更小的鎖機制,他保留了"自鎖"的概念,但是在寫方面,只能最多有一個寫進程,在讀方面,同時可以有多個讀執 行單元,當然讀寫不能同時進行。
  讀寫自旋鎖的操作如下:
  1)定義和初始化讀寫自旋鎖
  rwlock_t my_rwlock = RW_LOCK_UNLOCKED;//靜態初始化
  rwlock_t my_rwlock;
  rwlock_init(&my_rwlock);//動態初始化
  2)讀鎖定
  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);
  3)讀解鎖
  void read_unlock(rwlock_t *lock);
  void read_unlock_irqsave(rwlock_t *lock,unsigned long flags);
  void read_unlock_irq(rwlock_t *lock);
  void read_unlock_bh(rwlock_t *lock);
  說明:在對共享資源進行讀取之前,應該先調用讀鎖定函數,完成之後應調用解讀鎖定函數
  4)寫鎖定
  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);
  5)寫解鎖
  void write_unlock(rwlock_t *lock);
  void write_unlock_irqsave(rwlock_t *lock,unsigned long flags);
  void write_unlock_irq(rwlock_t *lock);
  void write_unlock_bh(rwlock_t *lock);
     說明:在對共享資源進行讀取之前,應該先調用寫鎖定函數,完成之後應調用解寫鎖定函數
   讀寫自旋鎖的使用方法如下:
   rwlock_t lock;//定義讀寫自旋鎖
   rwlock_init(&lock);//初始化
   //讀時獲取鎖
   read_lock(&lock);
   ...//臨界資源
   read_unlock(&lock);
   //寫時獲取鎖
   write_lock_irqsave(&lock,flags);
   ...//臨界資源
   write_unlock_irqrestore(&lock,flags);
3 順序鎖
    順序鎖是對讀寫鎖的一種優化,使用順序鎖,讀執行單元不會被寫執行單元堵塞。
    linux內核中,寫執行單元涉及如下順序鎖操作
    1)獲得順序鎖
    void write_seqlock(seqlock_t *s1);
    int write_tryseqlock(seqlock_t *s1);
    write_seqlock_irqsave(lock,flags);
    write_seqlock_irqe(lock);
    write_seqlock_bh(lock);
    2)釋放順序鎖
    void write_sequnlock(seqlock_t *s1);
    write_sequnlock_irqrestore(lock,flags);
    write_sequnlock_irqe(lock);
    write_sequnlock_bh(lock);
    //順序鎖的使用模式:
    write_seqlock(&seqlock_a);
    ...//寫操作代碼塊
    write_sequnlock(&seqlock_a);
    讀執行單元涉及如下操作:
    1)讀開始
    unsigned read_seqbegin(const seqlock_t *s1);
    read_seqbegin_irqsave(lock,flags)
    2)重讀
    int read_seqretry(const seqlock_t *s1,unsigned iv);
    read_seqretry_irqrestore(lock,iv,flags);
    讀執行單元訪問順序鎖的模式如下:
    do{
           seqnum = read_seqbegin(&seqlock_a)
            //讀操作代碼塊
                ...   
    }while(read_seqretry(&seqlock_a,seqnum));
 4 讀-拷貝-更新(RCU)
    RCU可以看做讀寫鎖的高性能版本,相比讀寫鎖,RCU的優點在於既允許多個讀執行單元同時訪問被保護的數據,又允許多個讀執行單元和多個寫執行單元同時訪問被保護的數據。
    但是,RCU不能代替讀寫鎖,因爲如果寫比較多時,對讀執行單元的性能提高不能彌補寫執行單元導致的損失。因爲使用RCU時,寫執行單元之間的同步開銷會比較大,它需要延遲數據結構的釋放,複製被修改的數據結構,它也必須使用某種鎖機制同步並行的其他寫執行單元的修改操作。
 linux系統提供如下4中RCU的操作
  1)讀鎖定
  rcu_read_lock()
  rcu_read_lock_bh()
  2)讀解鎖
  rcu_read_unlock()
  rcu_read_unlock_bh()
 使用模式如下:
  rcu_read_lock()
  ...//讀臨界區
  rcu_read_unlock()
 實際上,rcu_read_lock(),rcu_read_unlock()實質上只是禁止和使能內核的搶佔進度
  3)同步RCU
  synchronize_rcu()
  該函數由RCU寫執行單元調用,將阻塞寫執行單元,直到所有讀執行單元已經完成度執行單元臨界區 ,寫執行單元纔可以繼續下一步。
  4)掛接回調
  void fastcall call_rcu(struct rcu_head *head,void (*func)(struct rcu_head *rcu));
     該函數也由RCU的寫執行單元調用,他不會使寫執行單元阻塞,因而可以在中斷上下文或軟中斷中使用。該函數 將由函數func掛接到RCU回調函數鏈上,然後立即返回。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章