本页目录

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 后调用
call_rcu(&p->rcu_head, my_callback);
// → RCU 在 grace period 后调用 my_callback(p)

// 常见模式: 释放对象
kfree_rcu(p, rcu_head);  // 等价于 call_rcu(..., (void(*))kfree)

synchronize_rcu

// 同步等待: 阻塞直到所有现有 reader 离开
synchronize_rcu();  // 可能睡眠数百毫秒!
// 不能从中断上下文调用

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 数据)
srcu_read_lock(&ss);
// ... 可以睡眠 ...
srcu_read_unlock(&ss, idx);

// 写者:
synchronize_srcu(&ss);
// 开销比 synchronize_rcu 高

调试

# RCU 统计
cat /sys/kernel/debug/rcu/rcu_expedited  # 是否使用了快速 grace period
cat /sys/kernel/debug/rcu/rcugp          # grace period 状态

# RCU stall 检测 (CPU 在 rcu_read_lock 中太久)
#   → 内核打印 "INFO: rcu_sched detected stalls on CPUs/tasks"
#   → 说明某 CPU 长时间没离开 reader → 可能是死循环或长时间关抢占

# 触发 stall 检测的超时
cat /sys/module/rcupdate/parameters/rcu_cpu_stall_timeout

参考

  • 内核文档⁠: 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