嵌入式Linux——深入 RCU 写端:synchronize_rcu() 和 call_rcu() 到底在干什么?
·
问题理解
- RCU中四大操作
- 读锁定
- 读解锁
- 同步RCU
- 挂接回调
- 这个四大操作中的同步RCU和挂接回调操作,分别起什么作用?
基本概念
- 同步RCU 和 挂接回调,正是 RCU 实现“读-复制-更新”的最后一步,也就是“写端(更新者)如何安全地释放旧数据”。
rcu_read_lock/unlock是给读端用的。 同步RCU / 挂接回调 是给写端用的。- 假设你(写端)已经用一个
new_data替换了old_data。现在你的任务是:必须等所有正在读取old_data的读端全部读完,你才能安全地kfree(old_data)。 - 这个“等待所有老读者退出”的时间,就叫做“宽限期” (Grace Period)。
- 同步RCU 和 挂接回调 就是写端用来“等待宽限期结束”的两种不同策略。
策略一:同步 RCU ( synchronize_rcu() )
- 这个策略的名字非常直白,“同步” 的意思就是:“我就在这里“同步”等待,直到宽限期结束为止”。
- 用一个比喻来解释:
- 场景:你是一个图书馆管理员(写端),你刚用一个“新书架”替换了“旧书架”(指针替换)。
- 问题:图书馆里还有一些学生(读端)正在看“旧书架”上的书。你必须等这批学生全部离开图书馆后,才能把“旧书架”拆掉(释放内存)。
synchronize_rcu()的做法:- 你(写端线程)亲自搬个凳子坐在图书馆出口。
- 你就坐在那里“睡觉”(阻塞/睡眠),啥也不干,就是等。
- RCU 系统(图书馆的门禁)会帮你观察,当它确认所有“老学生”都已经刷卡离开(即所有 CPU 都经历了上下文切换)后,宽限期就结束了。
- RCU 系统会“叫醒”你(
synchronize_rcu()函数返回)。 - 你醒来后,你自己(写端线程)走进图书馆,把“旧书架”拆掉(即调用
kfree(old_data))。
- 总结一下:
- 它会阻塞! 你的代码会停在
synchronize_rcu()这一行不动,进入睡眠。 - 它会等待一个完整的宽限期。
- 当它返回时,RCU 向你保证:“所有老读者都走了。”
- 由你(调用者)负责在它返回后,执行
kfree()。 - 缺点:因为会睡眠,所以它绝对不能在中断上下文、或持有自旋锁(
spinlock)时使用。
- 它会阻塞! 你的代码会停在
策略二:挂接回调 ( call_rcu() )
- 这个策略要聪明得多,“挂接回调” 的意思是:“我(写端)很忙,没空等。我把清理工作‘挂接’(注册)给 RCU 系统,你(RCU)等宽限期结束后,‘回调’我的清理函数吧”。
- 还是用图书馆的比喻:
- 场景:同样,你刚替换了“旧书架”,需要等老学生离开后拆掉它。
call_rcu()的做法:- 你(写端线程)根本不想在门口傻等,你还有别的书要上架。
- 你写了一张便条(回调函数),便条上写着:“请在所有老学生离开后,按照这个地址
old_data,把旧书架拆掉(my_cleanup_function)”。 - 你把这张便条贴在了“RCU 的待办事项”的板子上。这个贴便条的动作就是
call_rcu()。 - 贴完便条,你(写端线程)立刻转身就走,继续干别的工作去了。
call_rcu()函数立即返回,根本不阻塞。 - 在未来某个时刻,RCU 系统(图书馆的门禁)发现宽限期结束了(老学生都走了)。
- RCU 系统(通常是一个软中断)会去检查“待办事项”板子,发现了你的便条。
- RCU 系统代替你执行了便条上的指令——调用了你写的那个
my_cleanup_function,并把old_data作为参数传给它。 - 旧书架被安全拆除。而你(写端线程)早就在忙别的事情了。
- 总结一下:
- 它不阻塞! 它会立即返回,你的代码继续往下跑。
- 它很“异步”。它把“等待”和“清理”这两项工作外包给了 RCU 系统。
- 由 RCU 系统负责在宽限期结束后,调用你注册的那个回调函数。
- 真正的
kfree()发生在回调函数里。 - 优点:速度极快,因为它不阻塞。它可以在中断上下文、或持有自旋锁时安全使用。这是 RCU 高性能的关键。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)