內核必看: spinlock、 mutex 以及 semaphore


linux 內核的幾種鎖介紹 http://wenku.baidu.com/link?url=RdvuOpN3RPiC5aY0fKi2Xqw2MyTnpZwZbE07JriN7raJ_L6Ss8Ru1f6C3Gaxl1klYrX8sWGjWV0FJigMFo96Umisnf8cdnccboyczsikpye


一、 以2.6.38以前的內核為例, 講spinlock、 mutex 以及 semaphore
1. spinlock更原始,效率高,但講究更多,不能隨便用。
2. 個人覺得初級階段不要去深挖mutex 以及 semaphore的不同,用法類似。在內核代碼里面搜索,感覺 DEFINE_MUTEX + mutex_lock_xx + mutex_unlock 用的更多。
3. 在內核里面這三個符號發揮的作用就是: 自旋鎖與互斥體。
semaphore:內核中的信號量通常用作mutex互斥體(信號量初值初始化為1,即binary semaphore的方式,就達到了互斥的效果)。
mutex:顧名思義, 互斥體。
所以在內核里面,mutex_lock()和down()的使用情景基本上相同。

//spinlock.h
/******
*API
*spin_lock
*spin_lock_bh
*spin_lock_irq
*spin_trylock
*spin_trylock_bh
*spin_trylock_irq
*spin_unlock
*spin_unlock_bh
*spin_unlock_irq
*spin_unlock_irqrestore
*spin_unlock_wait
******/

//semaphore.h
用 DECLARE_MUTEX 定義了一個count==1 的信號量(binary semaphore)。

#define DECLARE_MUTEX(name)    \
    struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
    
struct semaphore {
    spinlock_t        lock;
    unsigned int        count;
    struct list_head    wait_list;
};    

#define __SEMAPHORE_INITIALIZER(name, n)                \
{                                    \
    .lock        = __SPIN_LOCK_UNLOCKED((name).lock),        \
    .count        = n,                        \
    .wait_list    = LIST_HEAD_INIT((name).wait_list),        \
}
    
#define init_MUTEX(sem)        sema_init(sem, 1)
#define init_MUTEX_LOCKED(sem)    sema_init(sem, 0)

/*****
*API:
*#define init_MUTEX(sem)        sema_init(sem, 1)
*#define init_MUTEX_LOCKED(sem)    sema_init(sem, 0)
*extern void down(struct semaphore *sem);
*extern int __must_check down_interruptible(struct semaphore *sem);
*extern int __must_check down_killable(struct semaphore *sem);
*extern int __must_check down_trylock(struct semaphore *sem);
*extern int __must_check down_timeout(struct semaphore *sem, long jiffies);
*extern void up(struct semaphore *sem);
****/

//mutex.h
#define DEFINE_MUTEX(mutexname) \
    struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
    
    #define __MUTEX_INITIALIZER(lockname) \
        { .count = ATOMIC_INIT(1) \
        , .wait_lock = __SPIN_LOCK_UNLOCKED(lockname.wait_lock) \
        , .wait_list = LIST_HEAD_INIT(lockname.wait_list) \
        __DEBUG_MUTEX_INITIALIZER(lockname) \
        __DEP_MAP_MUTEX_INITIALIZER(lockname) }

/*
 * Simple, straightforward mutexes with strict semantics:
 *
 * - only one task can hold the mutex at a time
 * - only the owner can unlock the mutex
 * - multiple unlocks are not permitted
 * - recursive locking is not permitted
 * - a mutex object must be initialized via the API
 * - a mutex object must not be initialized via memset or copying
 * - task may not exit with mutex held
 * - memory areas where held locks reside must not be freed
 * - held mutexes must not be reinitialized
 * - mutexes may not be used in hardware or software interrupt
 *   contexts such as tasklets and timers
 *
 * These semantics are fully enforced when DEBUG_MUTEXES is
 * enabled. Furthermore, besides enforcing the above rules, the mutex
 * debugging code also implements a number of additional features
 * that make lock debugging easier and faster:
 *
 * - uses symbolic names of mutexes, whenever they are printed in debug output
 * - point-of-acquire tracking, symbolic lookup of function names
 * - list of all locks held in the system, printout of them
 * - owner tracking
 * - detects self-recursing locks and prints out all relevant info
 * - detects multi-task circular deadlocks and prints out all affected
 *   locks and tasks (and only those tasks)
 */
struct mutex {
    /* 1: unlocked, 0: locked, negative: locked, possible waiters */
    atomic_t        count;
    spinlock_t        wait_lock;
    struct list_head    wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
    struct thread_info    *owner;
#endif
#ifdef CONFIG_DEBUG_MUTEXES
    const char         *name;
    void            *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lockdep_map    dep_map;
#endif
};

/*******
*API:
*extern void mutex_lock(struct mutex *lock);
*extern int __must_check mutex_lock_interruptible(struct mutex *lock);
*extern int __must_check mutex_lock_killable(struct mutex *lock);
*extern void mutex_unlock(struct mutex *lock);
********/

EG1-1:
    spinlock_t rtc_lock;
    spin_lock_init(&rtc_lock);//每個驅動都會事先初始化,只需要這一次初始化
    
    spin_lock_irq(&rtc_lock);
    //臨界區
    spin_unlock_irq(&rtc_lock);

EG1-2:
    unsigned long flags;
    static spinlock_t i2o_drivers_lock;
    spin_lock_init(&i2o_drivers_lock);//每個驅動都會事先初始化,只需要這一次初始化

    spin_lock_irqsave(&i2o_drivers_lock, flags);
    //臨界區
    spin_unlock_irqrestore(&i2o_drivers_lock, flags);

EG2:
    static DECLARE_MUTEX(start_stop_sem);
    down(&start_stop_sem);
    //臨界區
    up(&start_stop_sem);

EG3:
    static DEFINE_MUTEX(adutux_mutex);
    mutex_lock_interruptible(&adutux_mutex);
    //臨界區
    mutex_unlock(&adutux_mutex);

 



二、 2.6.38以后DECLARE_MUTEX替換成DEFINE_SEMAPHORE(命名改變), DEFINE_MUTEX用法不變

    static DEFINE_SEMAPHORE(msm_fb_pan_sem);// DECLARE_MUTEX
    down(&adb_probe_mutex);
    //臨界區
    up(&adb_probe_mutex);
    
    static DEFINE_SEMAPHORE(bnx2x_prev_sem);
    down_interruptible(&bnx2x_prev_sem);
    //臨界區
    up(&bnx2x_prev_sem); 

Linux 2.6.36以后file_operations和DECLARE_MUTEX 的變化  http://blog.csdn.net/heanyu/article/details/6757917

在include/linux/semaphore.h 中將#define DECLARE_MUTEX(name)   改成了 #define DEFINE_SEMAPHORE(name)   【命名】

      
三、自旋鎖與信號量
1. 自旋鎖
簡單的說,自旋鎖在內核中主要用來防止多處理器中並發訪問臨界區,防止內核搶占造成的競爭。【適用於多處理器】【自旋鎖會影響內核調度】
另外自旋鎖不允許任務睡眠(持有自旋鎖的任務睡眠會造成自死鎖——因為睡眠有可能造成持有鎖的內核任務被重新調度,而再次申請自己已持有的鎖),它能夠在中斷上下文中使用。【不允許任務睡眠】

鎖定一個自旋鎖的函數有四個:
void spin_lock(spinlock_t *lock); //最基本得自旋鎖函數,它不失效本地中斷。
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);//在獲得自旋鎖之前禁用硬中斷(只在本地處理器上),而先前的中斷狀態保存在flags中
void spin_lockirq(spinlock_t *lock);//在獲得自旋鎖之前禁用硬中斷(只在本地處理器上),不保存中斷狀態
void spin_lock_bh(spinlock_t *lock);//在獲得鎖前禁用軟中斷,保持硬中斷打開狀態

2. 信號量
內核中的信號量通常用作mutex互斥體(信號量初值初始化為1就達到了互斥的效果)。

如果代碼需要睡眠——這往往是發生在和用戶空間同步時——使用信號量是唯一的選擇。由於不受睡眠的限制,使用信號量通常來說更加簡單一些。【信號量使用簡單】
如果需要在自旋鎖和信號量中作選擇,應該取決於鎖被持有的時間長短。理想情況是所有的鎖都應該盡可能短的被持有,但是如果鎖的持有時間較長的話,使用信號量是更好的選擇。【如果鎖占用的時間較長,信號量更好】
另外,信號量不同於自旋鎖,它不會關閉內核搶占,所以持有信號量的代碼可以被搶占。這意味者信號量不會對影響調度反應時間帶來負面影響。【信號量不會影響內核調度】

3. 使用情景對比
=============================================
需求                     建議的加鎖方法 
低開銷加鎖               優先使用自旋鎖
短期鎖定                 優先使用自旋鎖
長期加鎖                 優先使用信號量
中斷上下文中加鎖         使用自旋鎖
持有鎖是需要睡眠、調度    使用信號量
=============================================

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM