当前位置: 首页 > news >正文

Linux RCU (Read-Copy-Update) 机制深度分析

Linux RCU (Read-Copy-Update) 机制深度分析

1. 快速概览

RCU(Read-Copy-Update)是 Linux 内核中针对“读多写少”场景设计的高性能并发原语。核心思想很直接:

  • 读者可以在无锁的情况下并发读取共享数据;
  • 更新通过发布新版本并延迟回收旧版本来保证安全;
  • 回收旧对象必须等待一个宽限期 (Grace Period),确保所有可能正在访问旧对象的读者完成。

这种设计带来极低的读延迟和良好的可扩展性,适合网络协议、缓存层、驱动等读密集型内核子系统。


2. 核心原理

2.1 三个基本要素

  1. 发布/订阅(Publish/Subscribe)
    更新者发布新版本,读者在自己的临界区内直接读取当前版本指针,不阻塞。

  2. 宽限期(Grace Period)
    系统需要确认所有可能的读者都已离开旧版本的临界区,才可以回收旧数据。

  3. 延迟回调(Deferred Callback)
    回收工作通过回调(call_rcu())登记,等到宽限期结束再执行实际释放。

这些要点合起来,就是 RCU 能做到读无锁、写延迟回收的根本原因。

rcu_read_lock
rcu_read_lock
rcu_read_lock
rcu_read_unlock
Update Data
Yes
No
Reader Thread 1
RCU Critical Section
Reader Thread 2
Reader Thread 3
Exit Critical Section
Writer Thread
call_rcu
Register Callback
Wait Grace Period
Execute Callback
Free Old Data
Grace Period Detection
All CPUs passed QS?
GP Complete
Wait for QS

2.2 常见的 RCU 变体(选型要点)

  • TREE_RCU(CONFIG_TREE_RCU):面向多核、大规模系统,使用分层节点做可扩展聚合和跟踪。
  • TINY_RCU(CONFIG_TINY_RCU):面向单核或资源受限设备,实现精简、占用少。
  • PREEMPT_RCU(CONFIG_PREEMPT_RCU):支持内核抢占,适用于实时/低延迟需求的系统。

选型原则:系统规模、是否需要抢占、内存/CPU 受限程度 —— 根据这些条件选合适的 RCU 实现。


3. 关键数据结构

下面列出内核中常见的 RCU 数据结构,阅读时把重点放在“它们在 GP(宽限期)与回调管理中承担的角色”。

3.1 struct rcu_state(全局状态)

该结构管理 RCU 的树状节点、宽限期序列和用于调度 GP 的内核线程等。关键字段包括 CPU 数量、GP 序列号、GP 内核线程、以及屏障和快速 GP 的相关控制字段。

struct rcu_state {struct rcu_node node[NUM_RCU_NODES];    /* 分层树状结构 */struct rcu_node *level[RCU_NUM_LVLS + 1]; /* 层级指针数组 */int ncpus;                               /* CPU 数量 */int n_online_cpus;                       /* 在线 CPU 数量 *//* Grace Period 管理字段 */unsigned long gp_seq;                    /* Grace Period 序列号 */unsigned long gp_max;                    /* 最大 GP 持续时间 */struct task_struct *gp_kthread;          /* GP 内核线程 */struct swait_queue_head gp_wq;           /* GP 等待队列 */short gp_flags;                          /* GP 命令标志 */short gp_state;                          /* GP 状态 *//* 屏障同步字段 */struct mutex barrier_mutex;              /* 屏障互斥锁 */atomic_t barrier_cpu_count;              /* 等待的 CPU 数量 */struct completion barrier_completion;    /* 屏障完成信号 *//* 快速 Grace Period 字段 */struct mutex exp_mutex;                  /* 快速 GP 互斥锁 */unsigned long expedited_sequence;        /* 快速 GP 序列号 */atomic_t expedited_need_qs;              /* 需要 QS 的 CPU 数量 */
};

3.2 struct rcu_node(树节点)

rcu_node 用于把 CPU 分组为层级树,便于在大系统中聚合各组的 QS(quiescent state)信息。它也保存与回调、阻塞任务及优先级提升相关的管理结构。

struct rcu_node {raw_spinlock_t lock;                     /* 节点锁 */unsigned long gp_seq;                    /* Grace Period 序列号 */unsigned long gp_seq_needed;             /* 需要的 GP 序列号 */unsigned long completedqs;               /* 完成的 QS 掩码 */unsigned long qsmask;                    /* QS 掩码 */unsigned long rcu_gp_init_mask;          /* GP 初始化掩码 *//* 树状结构字段 */u8 level;                                /* 树中的层级 */u8 grplo;                                /* 组内最低 CPU */u8 grphi;                                /* 组内最高 CPU */u8 grpnum;                               /* 组号 */struct rcu_node *parent;                 /* 父节点 *//* 阻塞任务管理 */struct list_head blkd_tasks;             /* 阻塞任务列表 */struct list_head *gp_tasks;              /* GP 任务指针 */struct list_head *exp_tasks;             /* 快速 GP 任务指针 *//* 优先级提升 */struct task_struct *boost_kthread_task;  /* 提升内核线程 */unsigned long boost_time;                /* 提升时间 */struct mutex boost_mutex;                /* 提升互斥锁 */
};

3.3 struct rcu_data(每 CPU 的 RCU 数据)

每个 CPU 保留一份 rcu_data,用于跟踪该 CPU 的 QS、回调队列(segmented callback list)、以及与无回调(no-CBs)相关的锁和统计信息。

struct rcu_data {/* Grace Period 相关 */unsigned long gp_seq;                    /* GP 序列号 */unsigned long gp_seq_needed;             /* 需要的 GP 序列号 */union rcu_noqs rcu_qs_ctr_snap;          /* QS 计数器快照 *//* 回调管理 */struct rcu_segcblist cblist;             /* 分段回调列表 */long qlen_last_fqs_check;                /* 上次 FQS 检查时的队列长度 *//* CPU 状态 */bool rcu_urgent_qs;                      /* 紧急 QS 标志 */bool rcu_forced_tick;                    /* 强制时钟中断标志 */bool rcu_forced_tick_exp;                /* 快速 GP 强制时钟标志 *//* 节点关联 */struct rcu_node *mynode;                 /* 关联的 rcu_node */unsigned long grpmask;                   /* 组内掩码 *//* 无回调 CPU 支持 */raw_spinlock_t nocb_lock;                /* 无回调锁 */atomic_t nocb_lock_contended;            /* 锁竞争计数 *//* 统计信息 */unsigned long n_cbs_invoked;             /* 调用的回调数量 */unsigned long n_force_qs_snap;           /* 强制 QS 快照 */long blimit;                             /* 批处理限制 */
};

4. 分段回调列表(segmented callback list)

RCU 将回调按段保存(NEXT、WAIT、DONE 等),便于在不同的宽限期阶段进行移动和处理,从而避免回调在单一链表中长期堆积,利于批量执行与延迟释放。

struct rcu_segcblist {struct rcu_head *head;                   /* 回调列表头 */struct rcu_head **tails[RCU_CBLIST_NSEGS]; /* 分段尾指针数组 */unsigned long gp_seq[RCU_CBLIST_NSEGS];  /* 各段的 GP 序列号 */long len;                                /* 总长度 */long seglen[RCU_CBLIST_NSEGS];           /* 各段长度 */u8 flags;                                /* 标志位 */
};

5. RCU 层次与回调流(图示保留原样)

5.1 树状层次(CPU → rcu_node → root)

Grace Period Flow
Initialize Tree
GP Start
Wait for QS from all CPUs
GP Complete
Execute Callbacks
Root rcu_node Level 0
rcu_node Level 1-1
rcu_node Level 1-2
rcu_node Level 1-3
CPU 0-7 rcu_data
CPU 8-15 rcu_data
CPU 16-23 rcu_data
CPU 24-31 rcu_data
CPU 32-39 rcu_data
CPU 40-47 rcu_data

5.2 回调分段管理(阶段迁移)

New Callbacks
NEXT Segment
NEXT_TAIL Segment
WAIT Segment
WAIT_TAIL Segment
DONE Segment
Execute & Free
Grace Period N
Grace Period N+1
Move WAIT to DONE

(图说明:分段移动实现了“等待至少两个 GP 以后才真正释放”的策略,从而避免竞争窗口内的回收风险。)


6. API 快速参考(读者 / 更新者 / 快速 GP)

6.1 读者 API(必须尽量短的临界区)

  • rcu_read_lock() —— 进入 RCU 读临界区
  • rcu_read_unlock() —— 退出读临界区
  • rcu_dereference() —— 安全读取 RCU 保护的指针(带内存屏障/读取语义)

注意:读临界区不可睡眠(除非使用相应的可睡眠 RCU API),要尽量短以加快 GP 完成。

6.2 更新者 API(发布与回收)

  • rcu_assign_pointer() —— 原子发布新指针(写者使用)
  • call_rcu() —— 异步登记回调,在 GP 完成后执行释放
  • synchronize_rcu() —— 阻塞直到当前 GP 完成(同步等待,代价较大)

6.3 快速(Expedited)GP

  • call_rcu_expedited() / synchronize_rcu_expedited() —— 用于紧急情形,强制快速推进 GP,但开销更高,应该谨慎使用。

7. 与其他同步机制比较(实用角度)

同步机制读性能写性能内存开销典型适用场景
RWLock中等中等读写均衡
Spinlock高(短临界区)短临界区、高一致性需求
Mutex中等可能睡眠的长期操作
RCU极高(读)中等(写)中等读多写少、低延迟读
Synchronization Methods
Reader Performance
Writer Performance
Memory Overhead
RCU: O(1)
RWLock: O(n)
Spinlock: O(1)
RCU: O(n)
RWLock: O(1)
Spinlock: O(1)
RCU: Medium
RWLock: Low
Spinlock: Low

实践提示:若读操作占主导且对延迟敏感,优先考虑 RCU;否则按传统锁方案选择。


8. 最佳实践

8.1 读者端准则

  • 一律用 rcu_read_lock() / rcu_read_unlock() 包围读操作;尽量把临界区缩短到只包含必须的读取/解引用。
  • 读取指针时用 rcu_dereference(),以确保正确的内存序语义。
  • 切忌在 RCU 读临界区做长时间阻塞或可能睡眠的操作。

8.2 写者端准则

  • rcu_assign_pointer() 发布新数据;旧对象的释放放到 call_rcu() 的回调里。
  • 避免在 RCU 回调里做复杂或阻塞的工作;回调只做释放或极轻量的清理。
  • 只有确实需要同步等待时才用 synchronize_rcu(),否则尽量用异步回调以保留写者并发。

8.3 数据结构与性能优化

  • 有回收需求的结构内嵌 struct rcu_head,便于统一回调管理。
  • 若回收对象数量大,优先批量回收而不是一个对象一个回调;可显著降低回调开销。
  • 选择合适的 RCU 变体:TREE_RCU(大系统)、TINY_RCU(嵌入式)、PREEMPT_RCU(实时/抢占)。

示例:批量释放的回调模式(示意)

// 批量释放而不是单个释放
void batch_free_callback(struct rcu_head *head) {struct batch_data *batch = container_of(head, struct batch_data, rcu);// 批量处理多个对象
}

9. 总结

RCU 的价值在于把“低延迟读”与“安全延迟回收”巧妙结合:读者几乎零开销,写者通过宽限期+回调来完成不破坏并发安全的回收工作。

典型适用场景:网络协议栈、页缓存/文件系统缓存、设备驱动、内核中需要高并发读访问的数据结构。


文章转载自:

http://RmyQq9aD.dtpqw.cn
http://6ueIfIGC.dtpqw.cn
http://QT5oU8rP.dtpqw.cn
http://WQpfp1fJ.dtpqw.cn
http://zfeb0Xn8.dtpqw.cn
http://2dHgIN7Y.dtpqw.cn
http://udRIyem8.dtpqw.cn
http://Y48jyuXm.dtpqw.cn
http://CWuorONY.dtpqw.cn
http://vmKI0mHA.dtpqw.cn
http://uPYNPWyK.dtpqw.cn
http://QyTm4xVZ.dtpqw.cn
http://ZUreHliZ.dtpqw.cn
http://yjcCOGo6.dtpqw.cn
http://EZPgNW3o.dtpqw.cn
http://lYODCdES.dtpqw.cn
http://cOLdNl6b.dtpqw.cn
http://cL3YicKl.dtpqw.cn
http://RxbH4CjA.dtpqw.cn
http://JvqbJNd9.dtpqw.cn
http://vjCfSd4a.dtpqw.cn
http://53TBEkCE.dtpqw.cn
http://IcdGrfoq.dtpqw.cn
http://phV1Jn1L.dtpqw.cn
http://o7mmcx3Z.dtpqw.cn
http://g3nW6Xzf.dtpqw.cn
http://temTGSq2.dtpqw.cn
http://KjTh7JXQ.dtpqw.cn
http://X5EQJefO.dtpqw.cn
http://1yiuesSj.dtpqw.cn
http://www.dtcms.com/a/370033.html

相关文章:

  • 贪心算法应用:柔性制造系统(FMS)刀具分配问题详解
  • WSL Ubuntu Docker 代理自动配置教程
  • 基于Scikit-learn集成学习模型的情感分析研究与实现
  • MySQL数据库精研之旅第十七期:深度拆解事务核心(下)
  • Scikit-learn Python机器学习 - 特征降维 压缩数据 - 特征选择 - 单变量特征选择 SelectKBest - 选择Top K个特征
  • 从挑西瓜到树回归:用生活智慧理解机器学习算法
  • LabVIEW无线预警喷淋系统
  • Redis 的三种高效缓存读写策略!
  • 安装MATLAB205软件记录
  • Day28 打卡
  • 【FastDDS】XTypes Extensions
  • 软考 系统架构设计师系列知识点之杂项集萃(142)
  • 【音视频】H264编码参数优化和cbr、vbr、crf模式设置
  • 软考 系统架构设计师系列知识点之杂项集萃(141)
  • 竞价代运营:百度竞价账户托管优化
  • Python实战:爬取百度热搜榜,制作动态可视化报告
  • Windows 设备音频录制 | WASAPI 音频数据采集 / 环回录制
  • uniapp新增页面及跳转配置方法
  • 西门子S7-200 SMART PLC:编写最基础的“起保停”程序
  • UDP-Server(2)词典功能
  • 最大似然估计:损失函数的底层数学原理
  • 今日分享:C++ -- list 容器
  • 报错:OverflowError: Python integer 4294967296 out of bounds for uint32
  • 贪心算法应用:蛋白质折叠问题详解
  • AI-调查研究-71-具身智能 案例分析:从ROS到Tesla Optimus的开源与商业化实践
  • 【嵌入式C语言】七
  • [数据结构] LinkedList
  • 【C++】引用的本质与高效应用
  • Date、BigDecimal类型值转换
  • 基于Node.js和Three.js的3D模型网页预览器