ARM下的读写锁rwlock实现

内核同步之读写锁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()

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值