博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ARM下的读写锁rwlock实现
阅读量:4056 次
发布时间:2019-05-25

本文共 3145 字,大约阅读时间需要 10 分钟。

内核同步之读写锁rwlock_t

本文讲述的是基于linux 4.7.1版本的。

我们先来看读写锁的结构体定义:

include/linux/rwlock_types.h

这里只考虑raw_lock一个成员。

arch/arm/include/asm/spinlock_types.h

arch_rwlock_t只有一个成员lock.

下面,我们来介绍相关的API。

1. rwlock_init()

这个用来初始化rwlock.

include/linux/rwlock.h

include/linux/rwlock_types.h

arch/arm/include/asm/spinlock_types.h

从上述代码实现来看,rwlock_init(lock)就相当于lock->raw_lock.lock = 0;

 

2. read_lock()

这个API是占用读锁的。

include/linux/rwlock.h

include/linux/rwlock_api_smp.h

148行也是先禁内核抢占,这样,当前cpu就不会切换到其他进程了,只有当前进程和中断程序。

150行先调用do_raw_read_trylock(),如果失败(返回0),再调用do_raw_read_lock()。

include/linux/lockdep.h

我们先来看do_raw_read_trylock().

include/linux/rwlock.h

arch/arm/include/asm/spinlock.h

259-262行,将rw->lock + 1,看是否>=0(strexpl表示261行的操作结果大于等于0时才执行strex操作);如果rw->lock + 1>=0; 则将rw->lock +=1,并写入rw->lock,表示读锁占有成功,strexpl失败会使得while(1)来重新执行259-262的过程。成功占有读锁后,返回0.

如果rw->lock + 1 < 0,则认为写锁被占有(当写锁被占有时,rw->lock被设置为最小的负数0x80000000), 则返回1.

0x80000000是最小的负数,值为-2,147,483,648。

当do_raw_read_trylock()失败时,说明写锁被占用了,则调用do_raw_read_lock()

include/linux/rwlock.h

arch/arm/include/asm/spinlock.h

218-221行,判断rw->lock+1是否>=0,若>=0,表示当前没有写锁占有,则将rw->lock +=1.如果,当前有写锁占有,则219行adds的结果<0,rsbpls不执行(pl表示>=0)。rsbpls用于判断220行的strex是否成功。成功时,%0 = 0-%1=0,失败时,%0=0-%1=-1.

WFE(“mi”)会执行wfemi(wfemi表示负数执行wfe,即写锁存在的情况下会wfe), WFE等待写锁占有者释放锁。

222行, %0 = 0 - %1, 即当strex失败时,%1为1,%0为-1,223行就跳转到1处重新来一遍。

故当没有写锁strex失败时, 223行会执行跳转,重新来一次。当有写锁时WFE, 在被SEV唤醒后,由于之前219行adds的结果小于0,故222行的rsbpls不执行,直接执行223行跳转,重新来一遍。

 

2. read_unlock()

include/linux/rwlock.h

include/linux/rwlock_api_smp.h

__raw_read_unlock()调用do_raw_read_unlock()之后使能了内核抢占。

include/linux/rwlock.h

arch/arm/include/asm/spinlock.h

arch_read_unlock()比较简单了,就是将rw->lock -=1, 如果当前是最后一把读锁,则SEV,即发送event,用于唤醒可能在wfe的读写锁等待。SEV可以唤醒所有的WFE。

 

3. write_lock()

include/linux/rwlock.h

include/linux/rwlock_api_smp.h

__raw_write_lock()也是先禁内核抢占,然后调用do_raw_write_trylock(),如果失败(返回0), 表示当前有读锁或则写锁占用,则调用do_raw_write_lock()。

先来看do_raw_write_trylock()。

include/linux/rwlock.h

arch/arm/include/asm/spinlock.h

arch_write_trylock()判断rw->lock是否为0,为0则表示没有读锁或写锁,可以上写锁,就将rw->lock设置为最小的负数0x80000000, 表示写锁占有;如果rw->lock不为0,则表示有读锁或写锁存在,返回0,表示trylock()失败,后面就去调用do_raw_write_lock()了。

trylock()成功时,返回1.

当trylock()失败时,我们接着看do_raw_write_lock().

include/linux/rwlock.h

arch/arm/include/asm/spinlock.h

147-149行,判断rw->lock是否为0,如果不为0,则表示有读锁或者写锁存在,则WFE等待锁释放。当锁被释放后(读锁在最后一把锁unlock才会发SEV),由于之前eq不成立,strexeq不执行,故152行会跳转到147行重新来一遍。假如此时rw->lock是0,即不存在有读锁和写锁,则将rw->lock设置为最小的负数0x80000000.

 

4. write_unlock()

include/linux/rwlock.h

include/linux/rwlock_api_smp.h

__raw_write_unlock()在调用do_raw_write_unlock()释放锁之后,调用preempt_enable()使能了内核抢占。

我们接着看do_raw_write_unlock().

include/linux/rwlock.h

arch/arm/include/asm/spinlock.h

arch_write_unlock()比较简单,就是将rw->lock设置为0,然后SEV,唤醒等待WFE的锁操作。

 

至此,我们讲完了所有的API: rwlock_init(), read_lock(), read_unlock(), write_lock(), write_unlock()。

这里,我先提出一个问题:当已有写锁被占有时,其他的尝试读锁和写锁的操作都在WFE等待,如果此时写锁占有者释放了锁,即发送了SEV,那么其他锁操作请求者谁先占用呢?

我的理解是: 当SEV发出后,唤醒了所有的WFE等待的锁操作请求者,至于后面谁先占用锁,就看谁”手快”了,读写锁没有自旋锁spinlock的FIFO。

 

和spinlock类似地,这几个读写API并不能用于中断或中断和进程共享中。当使用上述lock的API函数后,在unlock之前,如果发生了中断,且中断中也尝试去获取这个锁,那么这就死锁了。要在中断中使用,则需要使用中断版本的API:read_lock_irq()/read_unlock_irq(), write_lock_irq()/write_unlock_irq()

转载地址:http://ltlci.baihongyu.com/

你可能感兴趣的文章
出现( linker command failed with exit code 1)错误总结
查看>>
iOS开发中一些常见的并行处理
查看>>
iOS获取手机的Mac地址
查看>>
ios7.1发布企业证书测试包的问题
查看>>
如何自定义iOS中的控件
查看>>
iOS 开发百问
查看>>
Mac环境下svn的使用
查看>>
github简单使用教程
查看>>
如何高效利用GitHub
查看>>
环境分支-git版本管理
查看>>
uni-app 全局变量
查看>>
js判断空对象的几种方法
查看>>
java 不用递归写tree
查看>>
springboot2 集成Hibernate JPA 用 声明式事物
查看>>
fhs-framework jetcache 缓存维护之自动清除缓存
查看>>
SpringBoot 动态编译 JAVA class 解决 jar in jar 的依赖问题
查看>>
fhs-framework springboot mybatis 解决表关联查询问题的关键方案-翻译服务
查看>>
ZUUL2 使用场景
查看>>
Spring AOP + Redis + 注解实现redis 分布式锁
查看>>
elastic-job 和springboot 集成干货
查看>>