rwsem 与读写信号量
这页讲的是:有些共享对象的典型模式不是“所有访问都一样贵”,而是读的人很多、写的人少。学这页的重点,是理解 rwsem 想利用的是读写不对称:让多个读者能并行进去,但写者仍然要独占修改窗口。
这块是什么
rwsem 与读写信号量,讲的是 Linux 怎样在可睡眠上下文中,针对“读多写少”的共享对象,提供一种允许多个读者并行、但写者仍需独占的同步原语。它关注的是“把读写访问模式差异显式利用起来”,而不是对所有访问一刀切地强制串行。
可以把它理解成:图书馆阅览区很多人可以同时看同一本参考资料,但如果要改动这本资料的内容,就得先把所有读者请出去,由修改者独占一段时间。
它负责什么
允许多个读者并行进入
- 如果只是读而不改,很多访问彼此并不冲突
- 让读者全串行会浪费并行性
- rwsem 允许多个读者同时持有读锁
- 把“只看不改”的共享路径放宽一些
给写者保留独占修改窗口
- 一旦要修改共享对象,就不能再让读者继续随意进来
- 写者需要一个完整、独占的更新窗口
- 让对象状态切换保持一致性
- 避免读者看到半更新状态
把读写不对称变成正式设计
- 很多对象访问模式天生偏读多写少
- 不利用这种结构,互斥成本可能偏高
- 让同步原语更贴近真实访问形态
- 把“访问模式差异”转成性能和扩展性收益
为什么它不是“更高级的 mutex”
| 如果想得太简单 | 会怎样 | 真正关键在哪 |
|---|
| 觉得 rwsem 只是多了两个接口 | 会看不见它真正利用的是读写访问模式不对称。 | 它的价值来自允许读者并行,而不是 API 花样。 |
| 把它用在读写都很频繁争抢的场景 | 可能得不到预期收益,甚至让等待关系更复杂。 | 它最适合读明显多于写的共享对象。 |
| 觉得多个读者就永远更快 | 会忽略写者饥饿、切换成本和更复杂的等待秩序。 | 读并行不是免费午餐,仍有公平性和时序代价。 |
| 把它拿到不能睡眠的环境 | 会和 mutex 一样违背其阻塞等待前提。 | rwsem 也是可睡眠同步原语。 |
关键概念
| 概念 | 现在怎么理解 |
|---|
| rwsem | 一种可睡眠的读写锁:读者可共享,写者需独占。 |
| 读锁 | 只读访问者拿到后,可以和其他读者并行共存。 |
| 写锁 | 修改者拿到后,要求自己独占访问窗口。 |
| 读多写少 | 很多对象访问模式天然偏向频繁查询、偶尔修改。 |
| 公平性 / 饥饿 | 如果读者或写者一边倒占优,就要小心另一边长期等不到机会。 |
为什么重要
- 它解释了为什么不是所有共享对象都该用简单互斥串行化处理。
- 很多目录结构、元数据或配置视图之类的对象,本来就更像“很多人看、少数人改”。
- 理解这层后,你会更容易把访问模式分析、同步开销和可扩展性设计联系起来。
- 它让你看到:选同步原语时,真正重要的不只是“能不能互斥”,还有“访问形态像什么”。
常见误解
- 误解一:rwsem 一定比 mutex 更先进。实际上如果读写模式不匹配,它未必更合适。
- 误解二:只要读多就该上读写锁。实际上还要看写路径延迟、公平性和整体复杂度。
- 误解三:有了读锁就等于完全无成本并行。实际上写者等待和锁管理本身仍然有代价。
它不负责什么
- 它不适合原子上下文或中断上下文,这类环境不能依赖睡眠等待。
- 它不天然解决对象生命周期问题,读者看到的对象是否仍然活着还要更大规则保障。
- 它不等于 RCU;两者都偏向读路径优化,但语义和使用边界并不一样。
和其他模块的关系
| 相关模块 | 关系 |
|---|
| mutex | 两者都可睡眠,但一个把所有访问串行化,一个显式区分读者和写者。 |
| RCU | 都常出现在读多写少场景,但 RCU 更强调读侧极轻、回收延后,rwsem 更强调显式持锁。 |
| VFS 与对象元数据 | 很多层级对象和元数据访问模式都天然容易出现读多写少特征。 |
| 生命周期与引用计数 | 即便读路径能并行,也仍要保证读到的对象没有过早消失。 |
读完这页后,你应该能回答
- 为什么 rwsem 的价值不在“多一种锁”,而在显式利用读写访问模式不对称?
- 为什么读多写少并不自动等于 rwsem 最优,还要看写者等待和整体公平性?
- 为什么 rwsem 和 RCU 都常出现在读优化话题里,但它们不是同一种思想?
后面适合继续问:rwsem 和 rwlock 最容易混在哪?什么时候目录、映射或元数据更适合 rwsem,什么时候又会转向 RCU?如果写者总抢不到机会,系统最容易出现什么体感问题?