内核版本:5.14
代码路径:
- kernel/locking/rwsem.c
- include/linux/rwsem.h
概述
读写信号量具有如下特点:
- 是一种睡眠锁
- 可以有多个read持有读信号量
- 只允许一个write持有持有写信号量
- read和write之间互斥
- write和write之间互斥
- 以严格的FIFO顺序处理等待读/写信号量的所有进程。如果read或write进程发现信号量关闭,这些进程就被插入到信号量等待队列链表的末尾。
- 当信号量被释放时,检查处于等待队列链表第一个位置的进程。第一个进程被唤醒。如果时一个写者进程,等待队列上的其他的进程就继续睡眠。如果是一个读者进程,那么紧跟第一个进程的其他所有读者进程也被唤醒并获得信号量。不过,在写者进程之后排队的读者进程继续睡眠
数据结构
struct rw_semaphore { atomic_long_t count; atomic_long_t owner; #ifdef CONFIG_RWSEM_SPIN_ON_OWNER struct optimistic_spin_queue osq; /* spinner MCS lock */ #endif raw_spinlock_t wait_lock; struct list_head wait_list; };
- owner
bit0:是否是读者持有信号,是的话为1
bit1:是否可以在读者持有的信号量上自旋等待
bit2~63:当读者持有信号时,owner中会记录获取到信号的最后一个读者进程的task_struct。如果写者持有信号,那么owner记录的是该写者进程的task_struct - count
bit0:是否是写者持有信号
bit1:是否有进程阻塞在等待队列中
bit2:handoff
bit8~62:读者的数量
bit63:read fail
实验
实现一个申请和释放读写信号量rwsem_test的内核模块,然后通过应用来控制申请或者释放,期间使用crash工具查看rwsem_test信号量的内部状态。
- 获取读写信号量rwsem_test的地址
root@ubuntu-vm:~# crash /mnt/linux-5.14/vmlinux KERNEL: /mnt/linux-5.14/vmlinux DUMPFILE: /dev/mem CPUS: 12 DATE: Sat Mar 26 09:34:29 CST 2022 UPTIME: 00:29:52 LOAD AVERAGE: 0.72, 0.23, 0.08 TASKS: 176 NODENAME: ubuntu-vm RELEASE: 5.14.0+ VERSION: #3 SMP Fri Mar 25 08:57:39 PDT 2022 MACHINE: x86_64 (3599 Mhz) MEMORY: 16 GB PID: 578 COMMAND: "crash" TASK: ffff8de0c595ec80 [THREAD_INFO: ffff8de0c595ec80] CPU: 2 STATE: TASK_RUNNING (ACTIVE) crash> sym rwsem_test ffffffffa5645b00 (d) rwsem_test
- 查看读写信号量的状态
crash> rw_semaphore.count,owner,wait_list -x ffffffffa5645b00 count = { counter = 0x0 }, owner = { counter = 0x0 }, wait_list = { next = 0xffffffffa5645b18 <rwsem_test+24>, prev = 0xffffffffa5645b18 <rwsem_test+24> }
可以看到初始状态count和owner都是0
- 查看等待队列的地址
crash> rw_semaphore.wait_list -ox ffffffffa5645b00 struct rw_semaphore { [ffffffffa5645b18] struct list_head wait_list; }
- 遍历等待队列
方式一:
crash> list -o rwsem_waiter.list -O rw_semaphore.wait_list -s rwsem_waiter.task,type -h ffffffffa5645b00 (empty)
方式二:
crash> list -o rwsem_waiter.list -s rwsem_waiter.task,type -H ffffffffa5645b18 (empty)
或者: 由于list在rwsem_waiter中的偏移量为0
crash> list -s rwsem_waiter.task,type -H ffffffffa5645b18 (empty)
- 实验数据
说明:
r: 申请读信号 w:申请写信号 R:释放读信号 W:释放写信号
操作序列 | 状态 | 队列 | 备注 |
初始 | count:0 owner:0 | ||
r1 | count:0x100 owner:0xffff9ba7465b9f01 |
count表示读者数量为1 owner的bit0为1,表示被读者持有,读者的task_struct为0xffff9ba7465b9f00 |
|
w1 | count:0x1 owner:0xffff9ba7465b9f00 |
count的bit0为1,表示写者持有 owner记录的时这个写者的task_struct:0xffff9ba7465b9f00 |
|
r1R1 | count:0x0 owner:0xffff9ba7465b9f01 |
||
w1W1 | count:0 owner:0 |
||
r1r2 | count:0x200 owner:0xffff9ba7465bae81 |
||
r1r2R1 | count:0x100 owner:0xffff9ba7465bae81 |
||
r1r2R1R2 | count:0x0 owner:0xffff9ba7465bae81 |
||
r1r1 | count:0x200 owner:0xffff9ba7465b9f01 |
||
w1w2 | count:0x3 owner:0xffff9ba7465b9f00 |
task = 0xffff9ba7465bae80, type = RWSEM_WAITING_FOR_WRITE |
|
w1w2W1 | count:0x1 owner:0xffff9ba7465bae80 |
||
w1w2W1W2 | count:0 owner:0 |
||
r1w1 | count:0x102 owner:0xffff9ba7465b9f03 |
task = 0xffff9ba7465bae80, type = RWSEM_WAITING_FOR_WRITE |
|
r1w1r2 | count:0x102 owner:0xffff9ba7465b9f03 |
task = 0xffff9ba7465bae80, type = RWSEM_WAITING_FOR_WRITE task = 0xffff9ba748febe00, type = RWSEM_WAITING_FOR_READ |
|
r1w1r2R1 | count:0x3 owner:0xffff9ba7465bae80 |
task = 0xffff9ba748febe00, type = RWSEM_WAITING_FOR_READ |
|
r1w1r2R1W1 | count:0x100 owner:0xffff9ba748febe01 |
||
r1w1r2R1W1R2 | count:0x0 owner:0xffff9ba748febe01 |
||
w1r1 | count:0x3 owner:0xffff9ba7465b9f00 |
task = 0xffff9ba7465bae80, type = RWSEM_WAITING_FOR_READ |
|
w1r1W1 | count:0x100 owner:0xffff9ba7465bae81 |
||
w1r1w2 | count:0x3 owner:0xffff9ba7465b9f00 |
task = 0xffff9ba7465bae80, type = RWSEM_WAITING_FOR_READ task = 0xffff9ba748febe00, type = RWSEM_WAITING_FOR_WRITE |
|
w1r1w2W1 | count:0x102 owner:0xffff9ba7465bae81 |
task = 0xffff9ba748febe00, type = RWSEM_WAITING_FOR_WRITE |
|
w1r1w2W1R1 | count:0x1 owner:0xffff9ba748febe00 |
||
w1r1w2W1R1W2 | count:0 owner:0 |
||
r1w1w2 | count:0x102 owner:0xffff9ba7465b9f03 |
task = 0xffff9ba7465bae80, type = RWSEM_WAITING_FOR_WRITE task = 0xffff9ba748febe00, type = RWSEM_WAITING_FOR_WRITE |
|
r1w1w2R1 | count:0x3 owner:0xffff9ba7465bae80 |
task = 0xffff9ba748febe00, type = RWSEM_WAITING_FOR_WRITE |
|
r1w1w2R1W1 | count:0x1 owner:0xffff9ba748febe00 |
||
r1w1w2R1W1W2 | count:0 owner:0 |
||
r1r2w1 | count:0x202 owner:0xffff9ba7465bae83 |
task = 0xffff9ba748febe00, type = RWSEM_WAITING_FOR_WRITE |
|
r1r2w1R2 | count:0x102 owner:0xffff9ba7465bae83 |
task = 0xffff9ba748febe00, type = RWSEM_WAITING_FOR_WRITE |
|
r1r2w1R2R1 | count:0x1 owner:0xffff9ba748febe00 |
||
r1r2w1R2R1W1 | count:0 owner:0 |
玩完。