linux内核 —— 读写信号量实验
内核版本: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 ,
prev = 0xffffffffa5645b18
}
可以看到初始状态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 |
玩完。