首页
/
新闻
/
愚蠢的RCU技巧:边界案例RCU实现
愚蠢的RCU技巧:边界案例RCU实现
摘要
Paul McKenney 讨论了非常规和边界情况的 RCU(读-拷贝-更新)实现,包括早期 Unix 系统中使用的定时等待 RCU 方法,以及与内存隔离相关的固定缓冲区 RCU 概念,展示了内核开发中具有创造性但潜在危险的同步技术。
<p><a href="https://lobste.rs/s/it5xwh/stupid_rcu_tricks_corner_case_rcu">评论</a></p>
查看缓存全文
缓存时间:
2026/04/20 14:43
# 愚蠢的RCU技巧:边界情况下的RCU实现
来源:https://people.kernel.org/paulmck/stupid-rcu-tricks-corner-case-rcu-implementations
RCU总是会在意想不到的地方出现。用Fedor Pikus的话来说:“事实上,你可能已经在程序中使用了RCU方法,却完全没有意识到!”(https://cppcon2017.sched.com/event/BgtF/read-copy-update-then-what-rcu-for-non-kernel-programmers)本文将介绍两种非常不正统(有人可能会说“完全不负责任”)的RCU实现类型的使用情况,既有偶然为之的,也有有意识设计的。
## 定时等待RCU
一类非常简单的RCU实现使用固定的时间周期作为宽限期。这显然可以在硬实时内核和应用中很好地工作,尽管它也曾在非实时内核的原型中使用过。在1990年代早期的一次口头讨论中,正是Van Jacobson(https://en.wikipedia.org/wiki/Van_Jacobson)指出,在他正在研究的一个专有UNIX操作系统的研究版本中,15秒的延迟就足够了。我当时口头回应说,在DYNIX/ptx中,中断处理程序有时会执行超过一分钟(即超过15秒),但Jack Slingwine和我想出了一种方法,可以在不需要对读者施加硬实时约束的情况下,获得同样的低开销/高可扩展性效果。Van表示感兴趣,于是我给他寄了第一份RCU会议论文(http://www.rdrop.com/users/paulmck/RCU/rclockpdcsproof.pdf)的早期草稿。几年后,我有幸听到Van对Linux内核RCU赞不绝口。
为了照顾那些像我一样脑海中自带“让它变得合理”过滤器的长期RCU用户,这里给出一个Linux内核中对Van的RCU实现的`synchronize_rcu()`版本:
`void synchronize_rcu(void) { schedule_timeout_uninterruptible(15 * HZ); }`
换句话说,`synchronize_rcu()`执行一个15秒的固定等待,之后在没有证据的情况下假设所有已存在的读者已经完成。
在1990年代中期,Aju John撰写了USENIX论文《动态vnode——设计与实现》(https://www.usenix.org/publications/library/proceedings/neworl/full_papers/john.a),其中提议在一个专有UNIX系统DEC OSF/1 3.0版中,为回收vnode设置固定的10分钟等待时间。
这种方法在硬实时环境中或许确实有意义,但即使在那里也是极其危险的。另一方面,你必须承认,Van和Aju是在采取一种不留余地的性能和可扩展性追求,而且是在非常早的阶段就这么做了!
据我所知,这两种专有UNIX操作系统现已不再投入生产使用,但当年定时等待RCU很可能确实在某些生产环境中使用过。也许它至今仍在使用,尽管在硬实时环境之外使用定时等待RCU可能非常危险!另一方面,这是硬实时使得某事变得简单的罕见例子,至少只要禁止非实时线程使用RCU读侧临界区即可。
## 固定缓冲区RCU
上一节讨论了完全基于时间的RCU宽限期检测。但最近与Hui Xie的一次讨论让我思考,是否也可以完全基于空间(即地址空间)来实现宽限期检测。这可能吗?
Linux内核地址消毒剂(KASAN)(https://docs.kernel.org/dev-tools/kasan.html)会将新释放的内存块保留在固定大小的隔离区中。这样做的目的是增加在对应内存块尚未被重新分配时发生释放后使用的概率,从而减少如果释放的内存被立即重新分配时可能出现的假阴性数量。
然而,这可以被视为另一种边界情况下的RCU实现,其中“读者”被假定在对应内存块被重新分配之前完成。
而rcutorture也做了类似的事情,它将`rcu_torture_current`此前引用的`RCU_TORTURE_PIPE_LEN`(即10个)内存块的释放延迟。此外,总共有10倍于此的元素数量(即100个)。与KASAN一样,其目的是降低假阴性的发生率,这样,一个极长的RCU读者结合一系列极短的RCU宽限期,每个读者仍有大约99%的概率对过短的宽限期发出诊断。
虽然在这两个例子中,固定缓冲区的目的是进行错误检测,但它们也可以用来创建一个极其有限且非常尖锐的RCU宽限期检测机制。因此,对于与Hui Xie讨论中提出的问题,答案是“可以”!
## 时间与空间及其影响
简而言之,一方面仅基于时间,另一方面仅基于空间来实现RCU宽限期检测是可能的(尽管有风险!)。这很好地衬托了同时使用时间和空间进行底层同步的RCU(https://www.linuxfoundation.org/webinars/unraveling-rcu-usage-mysteries),其中`rcu_read_lock()`、`rcu_read_unlock()`、`synchronize_rcu()`和`call_rcu()`处理时间,而`rcu_assign_pointer()`和`rcu_dereference()`处理空间。
谁知道呢?也许有人正在生产环境中使用固定缓冲区RCU。但如果你就是那个人,请理解你需要严格且确定地约束读者持续时间和更新速率。否则,你(以及你的用户!)就需要容忍一定概率的内存损坏。
一些历史背景由传闻中运行BSD UNIX的1980年代DEC VAX系统提供,该系统管理员选择使用已知有缺陷的补丁,据称可以提供大约15%的性能提升。而代价是每周大约多一次崩溃。当时,一些人认为这是系统管理的极致,而另一些人则认为这完全是鲁莽之举。
一个不那么有意的例子来自Linux内核RCU,就在挂起和休眠被接纳入内核后不久。这些特性引入了CPU热插拔,这悄悄地抵消了我对非CPU热插拔内核测试RCU的努力。这意味着rcutorture未能发现我注入RCU的一个愚蠢错误,该错误在非CPU热插拔内核中导致RCU宽限期变成固定的几毫秒延迟。
而且没有人注意到。
除了一个在两CPU系统上测试了数百个并发内核构建的开发者,他报告了该错误,但当我询问他的.config文件时却消失了。几个月后,Nick Piggin的dentry缓存测试触发了这个错误。Nick发现并修复了我那个愚蠢的错误。然后我修改了rcutorture,使其更加关注被测内核的.config文件,并以更有针对性的方式施加压力。考虑到如今Linux内核测试的激进程度,我怀疑类似的错误今天会被更快地注意到。
所以,一如既往,明智选择!;–)
## 历史
- 添加了`synchronize_rcu()`定时等待实现的示例,以强化这些RCU实现方法的不正统性质。
- 添加了1980年代DEC VAX系统选择性能而非可靠性的传闻。
相似文章
Lobsters Hottest
TokioConf 2026 的一篇演讲/博客文章探讨了如何通过为复杂指针结构实现追踪式垃圾回收,将安全 Rust 推向极限,并分享处理循环引用与原始指针 GC 设计的技巧。
Lobsters Hottest
# 新 Linux 内核补丁提议引入"killswitch"原语,可即时禁用存在漏洞的内核函数
一项新的 Linux 内核补丁提议引入一种"killswitch"原语,允许管理员立即禁用存在漏洞的内核函数(例如 `af_alg_sendmsg`),使其返回 `-EPERM`,从而为安全问题提供快速的临时缓解措施,无需重启系统或重新编译内核。
Hacker News Top
# Rust 零拷贝页面:我是如何停止焦虑并爱上生命周期的
来源:[https://redixhumayun.github.io/databases/2026/04/14/zero-copy-pages-in-rust.html](https://redixhumayun.github.io/databases/2026/04/14/zero-copy-pages-in-rust.html)
*你可以在[这里](https://github.com/redixhumayun/simpledb/)找到该项目的源代码*
零拷贝是一种旨在消除内核与用户空间缓冲区之间 CPU 数据复制的技术,尤其在数据处理等高吞吐量应用中极具价值。
Lobsters Hottest
一篇博客文章介绍了一种玩具语言,它在运行时而非静态类型系统中强制执行借用检查,通过在栈上使用轻量级引用计数来支持内部指针和单一所有权,适用于动态类型环境。
Reddit r/LocalLLaMA
社区释出去除安全拒答的 Qwen3.6-27B,并以专为 llama.cpp 与 LM Studio 优化的 K_P GGUF 量化格式打包。