zoukankan      html  css  js  c++  java
  • 内核必看: 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. 使用情景对比
    =============================================
    需求                     建议的加锁方法 
    低开销加锁               优先使用自旋锁
    短期锁定                 优先使用自旋锁
    长期加锁                 优先使用信号量
    中断上下文中加锁         使用自旋锁
    持有锁是需要睡眠、调度    使用信号量
    =============================================

  • 相关阅读:
    SQL考点例题解析
    数据交换技术
    网络安全的攻击手段
    实词和虚词
    VBA代码
    宏代码
    常用模块
    常用模块
    模块和包
    模块介绍
  • 原文地址:https://www.cnblogs.com/mylinux/p/5497609.html
Copyright © 2011-2022 走看看