几句话说明Linux中读写锁的实现逻辑

基本概念(可以跳过不看)

为什么需要读写锁

..在操作系统中,有很多共享数据,进程对这些共享数据要进行修改的情况很少,而读取的情况却是非常多的,这些共享数据的操作基本都是在读取。如果每次读取这些共享数据都加锁的话,那就太浪费时间了,会降低进程的运行效率。因为读操作不会导致修改数据,所以在读取数据的时候不用加锁了,而是可以共享的访问,只有涉及到对共享数据修改的时候,才需要加锁互斥访问。
  想像一下 100 个进程同时读取一个共享数据,而每个进程都要加锁解锁,剩下的进程只能等待,这会大大降低整个系统性能,这时候就需要使用一种新的锁了——读写锁。

读写锁的功能

img
那读写锁是怎么工作的呢?读写之间是互斥的,读取的时候不能写入,写入的时候不能读取,而且读取和写入操作在竞争锁的时候,写会优先得到锁,步骤如下。
1. 当共享数据没有锁的时候,读取的加锁操作和写入的加锁操作都可以满足。
2. 当共享数据有读锁的时候,所有的读取加锁操作都可以满足,写入的加锁操作不能满足,读写是互斥的。
3. 当共享数据有写锁的时候,所有的读取的加锁操作都不能满足,所有的写入的加锁操作也不能满足,读与写之间是互斥的,写与写之间也是互斥的。

读写锁的实现逻辑(重点在这里!!!)

先看一下这个锁的数据结构

//读写锁初始化锁值#define RW_LOCK_BIAS     0x01000000
//读写锁的底层数据结构
typedef struct{  
     unsigned int lock;
     }arch_rwlock_t;

简单来说,读锁,写锁的是否能够成功,都是通过判断lock值来进行,lock值初始化为0x1000000

每进行一次读锁操作,lock值减1 ,只要lock大于0 ,则可以成功获取读锁,这里可以看出可以同时满足0x1000000个进程的读锁操作。(读锁的释放,lock值加1

而写锁是否成功,则取决lock值减去0x1000000是否等于0,等于0,则获取写锁成功,这里我们可以看出,在没有任何进程进行读锁操作时,lock正好等于0x1000000,这也意味着可以获取写锁成功,当写锁没有释放时,此时lock的值为0,那么此时是无法获取读锁。当读锁有一个没有释放时,lock值是小于0x1000000的,那么也就意味着,无法获取写锁 (写锁释放,lock值恢复为0x1000000

具体代码(有能力的可以看看,只保留的重要部分的代码)


//读写锁初始化锁值
#define RW_LOCK_BIAS     0x01000000
//读写锁的底层数据结构
typedef struct{
    unsigned int lock;
}arch_rwlock_t;
//释放读锁 
static inline void arch_read_unlock(arch_rwlock_t*rw){ 
    asm volatile(
        LOCK_PREFIX"incl %0" //原子对lock加1
        :"+m"(rw->lock)::"memory");
}
//释放写锁
static inline void arch_write_unlock(arch_rwlock_t*rw){
    asm volatile(
        LOCK_PREFIX"addl %1, %0"//原子对lock加上RW_LOCK_BIAS
        :"+m"(rw->lock):"i"(RW_LOCK_BIAS):"memory");
}
//获取写锁失败时调用
ENTRY(__write_lock_failed)
    //(%eax)表示由eax指向的内存空间是调用者传进来的 
    2:LOCK_PREFIX addl  $ RW_LOCK_BIAS,(%eax)
    1:rep;nop//空指令
    cmpl $RW_LOCK_BIAS,(%eax)
    //不等于初始值则循环比较,相等则表示有进程释放了写锁
    jne   1b
    //执行加写锁
    LOCK_PREFIX subl  $ RW_LOCK_BIAS,(%eax)
    jnz 2b //不为0则继续测试,为0则表示加写锁成功
    ret //返回
ENDPROC(__write_lock_failed)
//获取读锁失败时调用
ENTRY(__read_lock_failed)
    //(%eax)表示由eax指向的内存空间是调用者传进来的 
    2:LOCK_PREFIX incl(%eax)//原子加1
    1:  rep; nop//空指令
    cmpl  $1,(%eax) //和1比较 小于0则
    js 1b //为负则继续循环比较
    LOCK_PREFIX decl(%eax) //加读锁
    js  2b  //为负则继续加1并比较,否则返回
    ret //返回
ENDPROC(__read_lock_failed)
//获取读锁
static inline void arch_read_lock(arch_rwlock_t*rw){
    asm volatile(
        LOCK_PREFIX" subl $1,(%0)\n\t"//原子对lock减1
        "jns 1f\n"//不为小于0则跳转标号1处,表示获取读锁成功
        "call __read_lock_failed\n\t"//调用__read_lock_failed
        "1:\n"
        ::LOCK_PTR_REG(rw):"memory");
}
//获取写锁
static inline void arch_write_lock(arch_rwlock_t*rw){
    asm volatile(
        LOCK_PREFIX"subl %1,(%0)\n\t"//原子对lock减去RW_LOCK_BIAS
        "jz 1f\n"//为0则跳转标号1处
        "call __write_lock_failed\n\t"//调用__write_lock_failed
        "1:\n"
        ::LOCK_PTR_REG(rw),"i"(RW_LOCK_BIAS):"memory");
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值