rw_semaphoree
设计思想
基本
- 读与读不互斥;
- 读与写互斥;
- 写与写互斥;
等待机制
- 读只有睡眠等待
- 写有忙等待,与睡眠等待;
唤醒
- 读:唤醒链表上的读进程,直到遇到一个写进程为止;
- 写:一次只能唤醒一个写者
count计数:(不保证完全正确)
概述
通过count标识锁的状态;
- 空闲状态:(0,0)
- 等待队列存在waiter:
#define RWSEM_WAITING_BIAS (-RWSEM_ACTIVE_MASK-1) - 只通过
RWSEM_WAITING_BIAS标识存在waiter,不标识有多少个waiter; - 标识存在写或读的:
#define RWSEM_ACTIVE_BIAS 0x00000001L- 标识存在读:(0, 1)
- 标识存在写:(-1, 1)
设计思想
- 可以看作一个二元组(x,y),(0,0)标识空闲;
- 1(
RWSEM_ACTIVE_BIAS)标识存在读或写,为了区分读或者写通过二元组的x标识;
注释中的count:
- 0x00000000:锁空闲
- 0x0000000X:只有x个读
- 0xffff000X:(-1,x)模型,-1表示有写,x表示有读;
- 所以可能是有x-1个读获取锁,有1个写;
- 1个写获取锁,x-1个读在等待;
- 0xffff0001:(-1,1)
- 只有一个写;
- 0xfffe0001:(-2,1);有一个写,又进入一个写waiter,太巧妙了这值;(-65535 - 65536,背后的数学逻辑?)
代码解读:
获取读锁:__down_read
主要路径:
- 尝试获取锁:
atomic_long_inc_return_acquire - 获取锁失败进入失败路径:
rwsem_down_read_failed - 读锁只有加入等待队列睡眠路径,所以有以下代码:
- 加入等待队列:
list_add_tail(&waiter.list, &sem->wait_list); - 判断锁是否获取没:
!waiter.task - 睡眠等待:
schedule();
- 加入等待队列:
一个特殊路径:
可以看见主要路径很符合预期,就是:获取锁,获取锁失败,加入等待队列睡眠等待获取锁;
猜测是作者实验测试的过程中,发现了一个可以优化的点,在正式睡眠之前判断了一次当前锁是否空闲,如果空闲就要唤醒等待队列中睡眠的进程,于是就有了如下代码:
if (count == RWSEM_WAITING_BIAS || /* 只有RWSEM_WAITING_BIAS,表示写锁已经释放了 */
(count > RWSEM_WAITING_BIAS && /* -65536 */ /* 存在的情况是:有一个写着,一个读者代码走到这里的过程中,如果又有锁进来了,并且其他的锁还没有走到atomic_long_add_return把之前加的值减回去 */
adjustment != -RWSEM_ACTIVE_READ_BIAS)) /* != -1 */ /* 理解是限制个数,比如又65535个锁,这里值就刚好是-1了,不知道理解对不对 */
__rwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q); /* 把waiter加入唤醒队列 */
获取写锁:__down_write
主要路径
- 获取锁:
atomic_long_add_return_acquire(RWSEM_ACTIVE_WRITE_BIAS, - 获取锁失败进入失败路径:
rwsem_down_write_failed - 乐观自旋获取锁:
rwsem_optimistic_spin - 加入等待队列睡眠等待:
- 尝试获取锁:
rwsem_try_write_lock(count, sem) - 睡眠等待:
schedule;
- 尝试获取锁:
特殊路径:
估计也是作者实验的过程中发现的,也加了一些特殊处理:
正式睡眠之前检测等待队列是否为空,检测此时写锁是否释放了,如果写锁释放了唤醒读者,代码如下:
if (count > RWSEM_WAITING_BIAS) { /* 如果此时的计数大于0,证明写释放锁了, */
__rwsem_mark_wake(sem, RWSEM_WAKE_READERS, &wake_q); /* 唤醒读者 */
为什么只对读进行了判断,如果此时的wait是写呢?
代码里面没有对写进行特殊处理,估计让写走正常路径获取把
释放锁:rwsem_wake
无论是读还是写都会调用到该函数;
- 唤醒等待队列中的wait:
__rwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q);
做了一个特殊处理:
if (rwsem_has_spinner(sem)) {
if (!raw_spin_trylock_irqsave(&sem->wait_lock, flags)) /* 获取锁 */
return sem; /* 没有获取到锁直接返回 */
goto locked; /* 获取到锁 */
}
认为存在自旋获取锁时,wait_lock获取很难,为了不浪费这点时间,就直接返回了??没太看懂

浙公网安备 33010602011771号