Linux內核中的互斥操作(2)——自旋鎖

*本篇來看看多次在內核中出現的spin_lock——自旋鎖,到底是個什麼東西。。。→_→*

  1. 內核中的spin_lock()

    • spin_lock()源代碼
    
    static inline void spin_lock(spinlock_t *lock)//自旋鎖的類型定義見下方
        {
        #if SPINLOCK_DEBUG
            __label__ here;
        here:
            if (lock->magic != SPINLOCK_MAGIC) {
        printk("eip: %p\n", &&here);
                BUG();
            }
        #endif
            __asm__ __volatile__(
                spin_lock_string//進入宏函數spin_lock_string,傳參數lock->lock
                :"=m" (lock->lock) : : "memory");
        }
    • 自旋鎖的類型定義
    typedef struct {
            volatile unsigned int lock;//不考慮調試時,自旋鎖就是一個無符號整形,volatile保證編譯器不進行過度優化
        #if SPINLOCK_DEBUG
            unsigned magic;
        #endif
        } spinlock_t;
    • spin_lock_string宏函數
    
    #define spin_lock_string \
    
        "\n1:\t" \
        "lock ; decb %0\n\t" \ //decb指令涉及讀-改-寫操作,所以lock總線保證該條指令的原子性,%0就是傳入的參數lock->lock,decb指令將lock->lock減1,結果非負表示加鎖成功,直接返回
        "js 2f\n" \ 
        ".section .text.lock,\"ax\"\n" \
        "2:\t" \ //結果爲負,循環測試lock->lock的值
        "cmpb $0,%0\n\t" \ //將lock->lock的值與0比較
        "rep;nop\n\t" \
        "jle 2b\n\t" \ //當lock->lock小於等於0時,繼續循環測試
        "jmp 1b\n" \ //當lock->lock大於0時,跳轉到標號1,獲取自旋鎖
        ".previous"

    從代碼中得知,如果lock->lock小於等於0,那麼就一直循環測試其值,直到lock->lock大於0。這就相當於讓CPU一直空轉,做無用功,因此自旋鎖應用的地方不能加鎖時間太長,否則就會浪費資源。

  2. 內核中的spin_unlock()

    • spin_unlock()源代碼
    static inline void spin_unlock(spinlock_t *lock)
    {
    
    #if SPINLOCK_DEBUG
    
        if (lock->magic != SPINLOCK_MAGIC)
            BUG();
        if (!spin_is_locked(lock))
            BUG();
    
    #endif
    
        __asm__ __volatile__(
            spin_unlock_string//調用spin_unlock_string宏函數,傳參數lock->lock
            :"=m" (lock->lock) : : "memory");
    }
    • spin_unlock_string宏函數
    
    #define spin_unlock_string \
    
        "movb $1,%0" //%0就是傳入的參數lock->lock,movb指令將lock->lock置爲1,movb指令本身就是原子操作,所以不需要lock總線
  3. 內核中的自旋鎖具體應用的類型

    • 加鎖操作
    
    
    #define spin_lock_irqsave(lock, flags)      do { local_irq_save(flags);       spin_lock(lock); } while (0)
    
    
    #define spin_lock_irq(lock)         do { local_irq_disable();         spin_lock(lock); } while (0)
    
    
    #define spin_lock_bh(lock)          do { local_bh_disable();          spin_lock(lock); } while (0)
    
    
    • 去鎖操作
    
    
    #define spin_unlock_irqrestore(lock, flags) do { spin_unlock(lock);  local_irq_restore(flags); } while (0)
    
    
    #define spin_unlock_irq(lock)           do { spin_unlock(lock);  local_irq_enable();       } while (0)
    
    
    #define spin_unlock_bh(lock)            do { spin_unlock(lock);  local_bh_enable();        } while (0)
    
    
    • 不同操作之間的異同

    同:加鎖和去鎖操作中都是分爲兩部分,即先執行local_操作——關閉或開啓本處理器上的中斷響應,再執行_lock操作——防止來自其他處理器的干擾。

    異:主要區別就在於如何關閉本處理器上的中斷響應

    /* For spinlocks etc */
    
    #define local_irq_save(x)   __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory") //通過cli指令關閉中斷,且將本處理器的狀態標識寄存器通過push和pop操作,保存到參數x中,以便去鎖時恢復。狀態標誌寄存器中的IF標誌位反映當前中斷的開關狀態
    
    
    #define local_irq_restore(x)    __restore_flags(x) //去鎖時恢復標識寄存器
    
    
    #define local_irq_disable() __cli() //直接將標識寄存器的IF標誌位清0
    
    
    #define local_irq_enable()  __sti()
    

*碼完喫飯。。。下午繼續最後的read_lock和write_lock。。。→_→*

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章