本页目录
RCU (Read-Copy-Update)
覆盖: RCU 核心机制 → grace period → rcu_read_lock/unlock → call_rcu/kfree_rcu → SRCU → Tasks RCU → RCU 调试 内核版本: 2.6 ~ 6.x
概述
RCU 是一种"等所有人都走了再回收"的同步机制。它允许读者在不拿锁的情况下访问共享数据——读者只需要 rcu_read_lock() 和 rcu_read_unlock()(这两个操作在大多数配置下是零开销的)。写者复制数据、修改副本、然后等所有读者都完成旧数据的访问后回收旧数据。
RCU 是 Linux 中最独特的同步原语,大量用于核心数据结构:VFS (dentry)、网络 (路由表)、文件系统 (inode)、cgroup 等。
核心思想
读者 (无锁, 非阻塞):
rcu_read_lock();
p = rcu_dereference(ptr); // 读指针
// ... 使用 p ...
rcu_read_unlock();
写者:
old = ptr;
new = copy(old); // 复制
modify(new); // 修改副本
rcu_assign_pointer(ptr, new); // 原子切换指针
synchronize_rcu(); // 等所有读者离开 (阻塞)
// 或: call_rcu(&old->rcu, callback); // 异步回收
关键约束:读者在 rcu_read_lock/unlock 之间不能睡眠(rcu_read_lock 关抢占,睡眠意味着其他任务可能在临界区中,破坏 RCU 的保证)。
Grace Period
Grace Period = 从 rcu_assign_pointer 到所有之前存在的 reader 都完成的时间窗口
CPU 0 (writer): CPU 1 (reader): CPU 2 (reader):
rcu_assign_...
| rcu_read_lock()
| |--- reader 在旧数据上 ---|
| rcu_read_lock()
| |-- 在新数据上 --|
| rcu_read_unlock()
| // CPU 1 离开了
synchronize_rcu() 返回
// 此时: 任何 CPU 上的读者都看不到 old 了
// → 安全回收
关键不变量: 一个 grace period 内,所有 CPU 都经历了一次上下文切换 (quiescent state)。RCU 利用调度器的 context switch 作为"读者离开了"的信号——这就是为什么 rcu_read_lock 必须关抢占。
实现
// kernel/rcu/tree.c
// 主要实现: Tree RCU (可扩展到数千 CPU)
// 以树状结构组织 CPU 的 quiescent state 报告
// rcu_read_lock():
// preempt_disable() ← 大多情况下的全部内容
// 实际: 关抢占 = 告诉 RCU "我在临界区"
// rcu_read_unlock():
// preempt_enable() ← 如果这是最后一个 preempt_disable,RCU 记录 quiescent state
call_rcu / kfree_rcu
// include/linux/rcupdate.h
// 异步回收: 注册回调, 在 grace period 后调用
;
// → RCU 在 grace period 后调用 my_callback(p)
// 常见模式: 释放对象
; // 等价于 call_rcu(..., (void(*))kfree)
synchronize_rcu
// 同步等待: 阻塞直到所有现有 reader 离开
; // 可能睡眠数百毫秒!
// 不能从中断上下文调用
RCU 变体
| 变体 | 读者阻塞类型 | 适用场景 |
|---|---|---|
| Classic RCU | 关抢占 (rcu_read_lock) | 大多内核代码 |
| SRCU (Sleepable RCU) | 显式 srcu_read_lock (可睡眠) | 文件系统, VFS |
| Tasks RCU | 关抢占 (trace 专用) | ftrace, kprobes |
SRCU
// include/linux/srcu.h
// 读者可以睡眠 (适合在拿 mutex 的场景下保护 RCU 数据)
;
// ... 可以睡眠 ...
;
// 写者:
;
// 开销比 synchronize_rcu 高
调试
# RCU 统计
# RCU stall 检测 (CPU 在 rcu_read_lock 中太久)
# → 内核打印 "INFO: rcu_sched detected stalls on CPUs/tasks"
# → 说明某 CPU 长时间没离开 reader → 可能是死循环或长时间关抢占
# 触发 stall 检测的超时
参考
- 内核文档:
Documentation/RCU/目录 (最全面的 RCU 文档) - LWN: "What is RCU?" 系列, "The RCU API"
- 源码:
kernel/rcu/tree.c,include/linux/rcupdate.h,kernel/rcu/srcutree.c
关键词: RCU, grace period, rcu_read_lock, call_rcu, kfree_rcu, SRCU, quiescent state, rcu_dereference