RCU(Read-Copy Update)是 Linux 内核中非常重要、也非常巧妙的一种 高效读写同步机制。
RCU 的目标
-
让读者 无锁、极快地读取数据,
-
写者在更新数据时也 不会阻塞读者。
RCU 的核心概念和主要 API
-
读侧临界区(Read-Side Critical Section)
指的是被rcu_read_lock()和rcu_read_unlock()包围的区域。
在这个区域里,读者可以安全访问旧数据。 -
宽限期(Grace Period)
指的是系统保证“所有正在进行的读者(此处的读者包括写者)都已经退出”的时间段。
一旦宽限期结束,表示没有人再访问旧数据,这时旧数据可以安全释放。
| API | 作用 |
|---|---|
rcu_read_lock() / rcu_read_unlock() |
标记 RCU 读侧临界区 |
rcu_dereference(p) |
安全读取 RCU 保护的指针 |
rcu_assign_pointer(p, v) |
安全更新指针,发布新版本 |
synchronize_rcu() |
等待所有旧读者退出临界区(同步点) |
call_rcu(cb, func) |
注册回调函数,在宽限期结束后执行(延迟释放) |
流程
读者
-
调用
rcu_read_lock()进入读侧临界区- 在非可抢占 RCU 实现中,会执行
preempt_disable(),禁止任务被抢占。
- 在非可抢占 RCU 实现中,会执行
-
用
rcu_dereference()读取数据 -
调用
rcu_read_unlock()离开读侧临界区
写者
-
拷贝旧数据;
-
修改副本(直接用
rcu_dereference()读取数据) -
用
rcu_assign_pointer()替换指针; -
用
call_rcu()或synchronize_rcu()等待旧读者退出; -
释放旧数据。
核心难点之一:写操作和新读者的并发一致性问题
考虑以下情况
-
写者复制一份副本,在副本上修改;
-
还没把新副本放回去(即还没执行
rcu_assign_pointer()); -
此时有新的读者来了;
-
新读者会通过
rcu_dereference()读到 旧数据; -
那新读者读到旧数据怎么办?是不是不一致?
结论
RCU 允许读者在短时间内读到“旧数据”,但保证数据永远一致、不会崩。 换句话说: RCU 保证一致性(Consistency),但不保证最新性(Freshness)。