Linux内核中的互斥操作(3)——读写锁

来源:互联网 发布:致远a6协同办公软件 编辑:程序博客网 时间:2024/06/16 18:06

*对内核中的信号量和自旋锁经过源码剖析后,再来看最后一个内核中的互斥操作——读写锁。。。→_→*

  1. 初始化读写锁

    • 读写锁的类型定义
    typedef struct {    //不调试时,读写锁实际上就是一个无符号整形。通过下面的代码还可以看出其实质就是一个计数器    volatile unsigned int lock;#if SPINLOCK_DEBUG    unsigned magic;#endif} rwlock_t;
    • rwlock_init()源代码
    #define RW_LOCK_BIAS         0x01000000#define RW_LOCK_BIAS_STR    "0x01000000"#define RWLOCK_MAGIC    0xdeaf1eed#if SPINLOCK_DEBUG#define RWLOCK_MAGIC_INIT   , RWLOCK_MAGIC#else#define RWLOCK_MAGIC_INIT   /* */#endif#define RW_LOCK_UNLOCKED (rwlock_t) { RW_LOCK_BIAS RWLOCK_MAGIC_INIT }//初始化读写锁时,将读写锁的lock初始化为0x01000000#define rwlock_init(x)  do { *(x) = RW_LOCK_UNLOCKED; } while(0)
  2. 内核中的read_lock()

    • read_lock()源代码
    static inline void read_lock(rwlock_t *rw){#if SPINLOCK_DEBUG    if (rw->magic != RWLOCK_MAGIC)        BUG();#endif    //第一个参数为读写锁指针,第二个为获取读锁失败时的处理函数的函数指针    __build_read_lock(rw, "__read_lock_failed");}
    • __build_read_lock()源代码
    #define __build_read_lock(rw, helper)   do { \                        //是编译器gcc的内置函数,用于判断一个值是否为编译时常量,如果是常数,函数返回1,否则返回0                        if (__builtin_constant_p(rw)) \                             __build_read_lock_const(rw, helper); \                        else \                            __build_read_lock_ptr(rw, helper); \                    } while (0)
    • __build_read_lock_const()与__build_read_lock_ptr()源代码
    #define __build_read_lock_ptr(rw, helper)   \    //锁总线,将rw减1,判断结果的符号位是否为1,若符号位为0则获取读锁成功并返回       asm volatile(LOCK "subl $1,(%0)\n\t" \              "js 2f\n" \             "1:\n" \             ".section .text.lock,\"ax\"\n" \             //若符号位为1则获取读锁失败,调用失败处理函数helper             "2:\tcall " helper "\n\t" \             "jmp 1b\n" \             ".previous" \             ::"a" (rw) : "memory")#define __build_read_lock_const(rw, helper)   \    asm volatile(LOCK "subl $1,%0\n\t" \             "js 2f\n" \             "1:\n" \             ".section .text.lock,\"ax\"\n" \             "2:\tpushl %%eax\n\t" \             "leal %0,%%eax\n\t" \             "call " helper "\n\t" \             "popl %%eax\n\t" \             "jmp 1b\n" \             ".previous" \             :"=m" (*(volatile int *)rw) : : "memory")
    • __read_lock_failed()源代码
    #if defined(CONFIG_SMP)asm(".align  4.globl  __read_lock_failed__read_lock_failed:    lock ; incl (%eax) //锁总线,将rw加11:  cmpl    $1,(%eax) //判断结果是否小于1    js  1b //若符号位为1,则继续循环测试rw的值    lock ; decl (%eax) //若结果大于等于1,锁总线,将rw减1    js  __read_lock_failed//判断结果的符号位是否为0,若为1,继续循环测试rw的值    ret//否则返回");#endif
  3. 内核中的read_unlock()

    • read_unlock()源代码
    //锁总线,将读写锁的计数加1#define read_unlock(rw)     asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory") 
  4. 内核中的write_lock()

    • write_lock()源代码
    static inline void write_lock(rwlock_t *rw){#if SPINLOCK_DEBUG    if (rw->magic != RWLOCK_MAGIC)        BUG();#endif    __build_write_lock(rw, "__write_lock_failed");//第一个参数为写锁,第二参数为获取写锁失败时的处理函数的函数指针}
    • __build_write_lock()源代码
    //格式同__build_read_lock()#define __build_write_lock(rw, helper)  do { \                        if (__builtin_constant_p(rw)) \                            __build_write_lock_const(rw, helper); \                        else \                            __build_write_lock_ptr(rw, helper); \                    } while (0)
    • __build_write_lock_const()和__build_write_lock_ptr()源代码
    #define __build_write_lock_ptr(rw, helper) \    //锁总线,将rw减RW_LOCK_BIAS_STR,即rw减0x01000000,判断结果是否为0,若为0则获取写锁成功并返回    asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \             "jnz 2f\n" \             "1:\n" \             ".section .text.lock,\"ax\"\n" \             //若结果不为0则获取写锁失败,调用失败处理函数helper             "2:\tcall " helper "\n\t" \             "jmp 1b\n" \             ".previous" \             ::"a" (rw) : "memory")#define __build_write_lock_const(rw, helper) \    asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \             "jnz 2f\n" \             "1:\n" \             ".section .text.lock,\"ax\"\n" \             "2:\tpushl %%eax\n\t" \             "leal %0,%%eax\n\t" \             "call " helper "\n\t" \             "popl %%eax\n\t" \             "jmp 1b\n" \             ".previous" \             :"=m" (*(volatile int *)rw) : : "memory")
    • __write_lock_failed源代码
    #if defined(CONFIG_SMP)asm(".align  4.globl  __write_lock_failed__write_lock_failed:    " LOCK "addl    $" RW_LOCK_BIAS_STR ",(%eax) //锁总线,将rw加RW_LOCK_BIAS_STR,即rw加0x010000001:  cmpl    $" RW_LOCK_BIAS_STR ",(%eax) //判断结果是否小于RW_LOCK_BIAS_STR     jne 1b //若结果不等于RW_LOCK_BIAS_STR ,则继续循环测试rw的值    " LOCK "subl    $" RW_LOCK_BIAS_STR ",(%eax) //若结果等于RW_LOCK_BIAS_STR,锁总线,将rw减RW_LOCK_BIAS_STR     jnz __write_lock_failed //判断结果是否等于0,若不等于0,则继续循环测试rw的值    ret //否则返回#endif
  5. 内核中的write_unlock()

    • write_unlock()源代码
    //锁总线,将读写锁的计数加RW_LOCK_BIAS_STR#define write_unlock(rw)    asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory")
  6. 内核中的读写锁具体应用的类型

    不同类型的异同见自旋锁中的分析——传送门请戳:自旋锁

    • 获取读写锁的操作
    #define read_lock_irqsave(lock, flags)      do { local_irq_save(flags);       read_lock(lock); } while (0)#define read_lock_irq(lock)         do { local_irq_disable();         read_lock(lock); } while (0)#define read_lock_bh(lock)          do { local_bh_disable();          read_lock(lock); } while (0)#define write_lock_irqsave(lock, flags)     do { local_irq_save(flags);      write_lock(lock); } while (0)#define write_lock_irq(lock)            do { local_irq_disable();        write_lock(lock); } while (0)#define write_lock_bh(lock)         do { local_bh_disable();         write_lock(lock); } while (0)
    • 释放读写锁的操作
    #define read_unlock_irqrestore(lock, flags) do { read_unlock(lock);  local_irq_restore(flags); } while (0)#define read_unlock_irq(lock)           do { read_unlock(lock);  local_irq_enable();       } while (0)#define read_unlock_bh(lock)            do { read_unlock(lock);  local_bh_enable();        } while (0)#define write_unlock_irqrestore(lock, flags)    do { write_unlock(lock); local_irq_restore(flags); } while (0)#define write_unlock_irq(lock)          do { write_unlock(lock); local_irq_enable();       } while (0)#define write_unlock_bh(lock)           do { write_unlock(lock); local_bh_enable();        } while (0)
  7. 总结

    • 读写锁本质上就是一个计数器,初始化值为0x01000000,表示最多可以有0x01000000个读者同时获取读锁

    • 获取读锁时,rw计数减1,判断结果的符号位是否为1。若结果符号位为0时,获取读锁成功

    • 获取读锁时,rw计数减1,判断结果的符号位是否为1。若结果符号位为1时,获取读锁失败,表示此时读写锁被写者占有,此时调用__read_lock_failed失败处理函数,循环测试rw+1的值,直到结果的值大于等于1

    • 获取写锁时,rw计数减RW_LOCK_BIAS_STR,即rw-0x01000000,判断结果是否为0。若结果为0时,获取写锁成功

    • 获取写锁时,rw计数减RW_LOCK_BIAS_STR,即rw-0x01000000,判断结果是否为0。若结果不为0时,获取写锁失败,表示此时有读者占有读写锁或有写着占有读写锁,此时调用__write_lock_failed失败处理函数,循环测试rw+0x01000000,直到结果的值等于0x01000000

    • 通过对读写锁的源代码分析,可以看出读写锁其实是带计数的特殊自旋锁,能同时被多个读者占有或一个写者占有,但不能同时被读者和写者占有

    • 操作系统为了避免一直有读者占有读写锁而导致写者饥饿的情况,让写者等待时排在读者前面,使写者的优先级更高

*一个下午就这样过去了。。。→_→*

原创粉丝点击