土法炼钢兴趣小组的算法知识备份

【操作系统百科】RCU 深度

文章导航

分类入口
os
标签入口
#rcu#grace-period#synchronize-rcu#tree-rcu#srcu

目录

RCU(Read-Copy Update)让读操作零开销——不需要锁、不需要原子指令、不需要屏障(在很多架构上)。代价是写操作需要等待 grace period。

一、先看图

sequenceDiagram
    participant W as Writer
    participant R1 as Reader 1
    participant R2 as Reader 2
    participant GP as Grace Period

    R1->>R1: rcu_read_lock
    W->>W: 分配新数据
    W->>W: rcu_assign_pointer
    R2->>R2: rcu_read_lock
    R2->>R2: rcu_dereference → 看到新数据
    R1->>R1: rcu_read_unlock
    W->>GP: synchronize_rcu / call_rcu
    GP->>GP: 等所有旧读者退出
    GP->>W: 释放旧数据

二、核心思想

  1. 读者rcu_read_lock() / rcu_read_unlock(),不阻塞、不获取锁
  2. 写者:创建新版本 → rcu_assign_pointer() 发布 → 等旧读者退出 → 释放旧版本
  3. Grace period:所有持有旧引用的读者都退出临界区的时间段

三、读侧 API

rcu_read_lock();
struct foo *p = rcu_dereference(global_ptr);  // 带 address dependency
// 使用 p
rcu_read_unlock();

rcu_dereference 展开为 READ_ONCE + 编译器屏障。在 ARM 等弱序架构,地址依赖天然保证顺序。

四、写侧 API

struct foo *new = kmalloc(...);
// 初始化 new
rcu_assign_pointer(global_ptr, new);  // smp_store_release 语义

// 同步方式
synchronize_rcu();  // 阻塞等待 grace period
kfree(old);

// 或异步方式
call_rcu(&old->rcu_head, my_callback);  // grace period 后回调释放

五、Grace Period 实现

5.1 经典 RCU(已废弃)

每个 CPU 经过一次上下文切换 = 不在 RCU 临界区 → grace period 结束。

5.2 Tree RCU

flowchart TD
    ROOT[rcu_state<br/>root node] --> L1A[rcu_node<br/>level 1]
    ROOT --> L1B[rcu_node<br/>level 1]
    L1A --> CPU0[CPU 0<br/>rcu_data]
    L1A --> CPU1[CPU 1<br/>rcu_data]
    L1B --> CPU2[CPU 2<br/>rcu_data]
    L1B --> CPU3[CPU 3<br/>rcu_data]

    classDef node fill:#388bfd22,stroke:#388bfd,color:#adbac7;
    classDef cpu fill:#3fb95022,stroke:#3fb950,color:#adbac7;
    class ROOT,L1A,L1B node
    class CPU0,CPU1,CPU2,CPU3 cpu

树形 quiescent state 报告 → O(log N) 而非 O(N) → 适合大核数。

每 CPU 报告 quiescent state(QS)→ 逐层汇聚到 root → grace period 完成。

六、RCU 变体

变体 读侧开销 可睡眠 用途
经典 RCU 零(preempt_disable) 大多数
SRCU 原子 inc/dec 需要睡眠的读侧
Tasks RCU 是(自愿调度点) tracing/BPF
Tasks Trace RCU 轻量 BPF trampoline

七、RCU 的代价

7.1 Lazy RCU

减少唤醒频率 → 省电。grace period 回调延迟批量处理。

7.2 nocb CPU

rcu_nocbs=1-7

指定 CPU 不处理 RCU 回调 → 回调由专门的 rcuog/rcuop 线程处理 → 减少对实时 CPU 的干扰。

八、常见错误

  1. rcu_read_lock 里睡眠(非 SRCU)→ grace period 无法推进 → 死锁
  2. 忘记 rcu_dereference → 编译器优化破坏顺序
  3. 不等 grace period 就释放 → use-after-free

九、观察

cat /sys/kernel/debug/rcu/rcu_preempt/rcudata  # per-CPU RCU 状态
cat /sys/kernel/debug/rcu/rcu_preempt/rcuexp    # expedited GP
dmesg | grep -i rcu
bpftrace -e 'kprobe:rcu_gp_kthread { @++ }'

十、小结


参考文献

工具


上一篇mutex/rwsem 下一篇seqlock

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-05-30 · os

【操作系统百科】percpu_refcount 与 SRCU

高频 refcount 每次 inc/dec 都是 cache line bouncing。percpu_refcount 用 per-CPU 计数器解决——活跃时零竞争,关闭时收敛为原子。本文讲两阶段设计、SRCU、kref 与正确关闭顺序。

2026-04-27 · os

【操作系统百科】内存回收

Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。

2026-04-28 · os

【操作系统百科】交换

swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。

2026-05-03 · os

【操作系统百科】Slab/SLUB 分配器

buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。


By .