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

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

  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加1
    1:  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加0x01000000
    1:  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

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

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

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

猜你喜欢

转载自blog.csdn.net/kongkongkkk/article/details/76615838
今日推荐