操作系统内核同步原语:从自旋锁到RCU的深度剖析

引言

并发控制是操作系统内核设计的核心挑战之一。当多个CPU核心同时访问共享数据时,如果没有适当的同步机制,就会导致数据竞争和不一致。本文将深入探讨Linux内核中常用的同步原语:自旋锁、信号量、读写锁、顺序锁以及RCU。

1. 自旋锁 (Spinlock)

自旋锁是一种忙等待锁,当锁被其他CPU持有时,当前CPU会不断循环检查锁状态。它适用于临界区非常短的场景,避免了上下文切换的开销。

Linux内核的自旋锁实现采用了ticket lock机制:每个等待者取一个递增的号,当持有者的"服务号"与等待者匹配时获得锁。这保证了FIFO公平性。

在多核系统中获取自旋锁时必须关闭本地中断,防止中断处理程序导致的死锁。spin_lock_irqsave()是最常用的变体,它保存中断状态并关闭中断。

2. 信号量 (Semaphore)

信号量是一种睡眠锁,当锁不可用时当前进程进入等待队列并让出CPU。它适用于可能长时间持有的场景。

Linux提供了计数信号量(支持多个持有者)和二值信号量(互斥锁)。down()操作尝试获取信号量,如果不可用则睡眠;up()释放信号量并唤醒等待者。

3. 读写锁 (RW Lock)

读写锁区分读者和写者:多个读者可以同时持有锁,但写者必须独占。这在读多写少的场景下显著提高了并发度。

Linux内核的读写锁采用无等待读侧路径,写者自旋等待所有读者完成。但传统的读写锁存在写者饥饿问题——如果有持续的读者,写者可能永远无法获取锁。

4. 顺序锁 (Seqlock)

顺序锁是一种读写锁的变体,写者优先。读者在读取前后检查序列号,如果序列号变化则说明写者介入,需要重试。

这使得读者永远不会阻塞,适用于读取极快而写入极少的场景,如系统时间的更新(jiffies_64)。

5. RCU (Read-Copy-Update)

RCU是Linux内核最独特的同步机制之一。它允许读者在没有任何锁的情况下访问数据,写者通过创建副本来修改数据。

RCU的核心思想是:读者进入"读临界区"(rcu_read_lock),写者创建数据副本、修改副本、然后原子替换指针。旧版本的数据在所有读者结束后才释放。

RCU的实现涉及两个关键机制: grace period(宽限期)检测和回调处理。call_rcu()注册一个回调函数,在grace period结束后执行。

6. 实战案例

网络子系统中的路由表查找使用RCU,因为查找极其频繁而添加/删除路由相对稀少。进程调度域也使用RCU保护,使得调度器读取调度域信息时无需加锁。

总结

选择合适的同步原语取决于临界区的特性。短临界区且不可睡眠时用自旋锁;长临界区且可睡眠时用信号量;读多写少时考虑读写锁或RCU;写者优先时用顺序锁。Linux内核的演进方向是尽可能使用无锁或低锁的同步机制。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部