很多关注内核进展的开发者可能已经注意到,Linux 7.0 版本里 Rust 语言支持不再是一个需要手动开启的实验性选项,而是作为正式特性直接出现在主线内核中。这个变化在外界看来可能只是多了一种语言选择,但对内核社区和长期维护系统的人来说,它背后的信号远不止于此。

内核代码绝大部分由 C 语言写成,它足够灵活、足够接近硬件,但几十年来也一直背着“不安全”的包袱。内存越界、释放后使用(use‑after‑free)、双重释放这些典型的 C 语言问题,即使是最有经验的开发者也无法完全避免,补丁列表中时不时就能看到修复这类漏洞的提交。Rust 进入内核,本质上是内核社区对“在编译期就消灭一整类内存安全问题”这件事的一次认真下注。而这次转正,意味着社区认为这套机制已经可靠到可以交给所有内核开发者去使用了。
从实验到正式,Rust 在内核里经历了什么
回看时间线,Linus Torvalds 在 2022 年合并了初始的 Rust for Linux 基础设施,但当时明确将其标记为实验性,开发者需要显式配置 CONFIG_RUST 才能启用。那会儿能做的事情很少,主要是在内核里跑几个玩具模块,验证编译器和绑定生成工具是否工作正常。此后两年里,围绕 Rust 的绑定抽象、内核 crate 设计、与 C 语言 API 的交互规范都在不断迭代,同时一批早期采用者开始尝试用 Rust 编写实际驱动,比如简单的网络 PHY 驱动、块设备驱动框架等。
到了 7.0,实验标签被摘掉,意味着内核开发者可以在不添加任何额外配置的情况下直接使用 Rust 编写模块,并且这些模块会被视为正式支持的内核组件。这对驱动开发者尤其重要——内核里数量最庞大的代码就是驱动程序,而驱动也是内存安全问题的高发区。现在,用 Rust 写一个新驱动不再是“先锋实验”,而是摆在台面上的正式选项。
真正解决的是什么问题
很多人会简单地把 Rust 等同于“内存安全”,但内核语境下这一点需要展开。Rust 的所有权模型和借用检查器可以在编译期保证没有数据竞争、没有悬垂指针、没有非法内存访问,这直接对应着内核中最头疼的两类漏洞:slab 越界和 use‑after‑free。对于长期运行的内核线程或共享数据结构,这种保证尤其有价值。
但要注意,内核 Rust 代码里并不是完全没有 unsafe。凡是调用 C 语言内核 API 的地方,或者操作裸指针、访问硬件寄存器,都必须在 unsafe 块中进行。Rust 的优势在于把这些不安全操作显式隔离出来,让其余大部分代码处于安全子集的保护之下。这与纯 C 代码相比,审查重点可以更集中,出问题的范围也更容易控制。
下表对比了在内核开发中 C 和 Rust 各自的特点,可以帮助理解什么时候该选哪一种语言:
| 维度 | C 语言 | Rust (for Linux) |
|---|---|---|
| 内存安全 | 完全依赖开发者 discipline,容易出漏洞 | 编译器在 safe 代码中强制保证,unsafe 块需显式标注 |
| 性能 | 可直接映射硬件,无额外抽象开销 | 零成本抽象,性能与 C 相当,但某些场景需要微调 unsafe 代码 |
| 现有生态 | 内核内部 API 全部为 C 定义,历史代码极其庞大 | 依赖 bindgen 生成的绑定,高层抽象还在逐步丰富 |
| 学习曲线 | 内核 C 代码风格特殊,但语言本身简单 | 所有权、生命周期等概念需要时间适应,尤其与内核交互部分 |
| 维护成本 | 代码审核主要靠经验,内存相关 bug 修复成本高 | 编译器能尽早发现问题,但需要维护两套语言基础设施 |
一个真实的 Rust 内核模块长什么样
为了感受一下在内核里用 Rust 到底是什么体验,可以看一个最简单的“hello world”模块。尽管它什么都没做,但已经展示了 Rust 模块的基本结构:
//! A simple hello world module using Rust for Linux.
use kernel::prelude::*;
module! {
type: HelloWorld,
name: "hello_world",
author: "Developer",
description: "A simple example",
license: "GPL",
}
struct HelloWorld;
impl kernel::Module for HelloWorld {
fn init(_module: &'static ThisModule) -> Result<Self> {
pr_info!("Hello, world!\n");
Ok(HelloWorld)
}
}
impl Drop for HelloWorld {
fn drop(&mut self) {
pr_info!("Goodbye, world!\n");
}
}
与 C 语言模块相比,这里没有 module_init() / module_exit() 宏,而是通过实现 kernel::Module trait 和 Drop 来自动管理生命周期。内核 crate 提供了很多安全的包装,比如 pr_info! 宏直接对应 printk,而不需要手动处理格式字符串的安全问题。这种写法对习惯了 Rust 的开发者来说很自然,但对内核老手来说确实需要一段适应期。
转正后,哪些场景最先受益
不是所有内核组件都适合马上用 Rust 重写。从工程实际来看,下面这几类场景是 Rust 当前最容易落地的方向:
- 全新编写的设备驱动。 驱动代码量往往不小,逻辑相对独立,且频繁与用户输入交互,安全收益明显。网络驱动、块设备驱动、简单字符设备驱动都是合适的切入点。
- 对安全性有强制要求的子系统扩展。 比如 Android 的 Binder 机制已经有 Rust 实现被合入主线,因为它在用户空间与内核之间传递大量数据,攻击面很大。
- 内核内的小型独立组件。 比如某些文件系统格式解析器、加密算法实现等,它们逻辑复杂但接口清晰,适合用 Rust 重写以降低解析过程中的内存风险。
而以下情况则需要谨慎:
- 需要大量修改现有 C 代码结构的重构。 除非有明确的隔离边界,否则 Rust 和 C 之间的频繁互操作会让 unsafe 块比例飙升,安全收益大打折扣。
- 高度依赖特定硬件时序或内核内部未稳定接口的代码。 Rust 的安全抽象通常建立在稳定的内核 API 之上,如果底层 API 经常变动,维护成本会很高。
- 小团队维护的老旧驱动。 如果没有懂 Rust 的成员,强行引入可能反而增加维护风险。
几个容易被误读的点
Rust 正式转正后,不少讨论里出现了一些常见的理解偏差,值得澄清一下。
误区一:“Rust 要取代 C 了”——这是最大的误解。内核代码库里数千万行 C 代码不会消失,也不可能全部重写。Rust 的定位是提供一个新的选择,尤其适合增量开发。未来很长一段时间里,C 仍然是内核的绝对主力语言,Rust 更多是在新模块和安全性敏感区域里发挥作用。
误区二:“用 Rust 写内核就不用关心内存安全了”——safe Rust 确实能消除很多内存错误,但内核里的 unsafe 代码不可避免。如何正确地封装 unsafe 操作、确保对外暴露的安全 API 没有漏洞,仍然需要开发者有很强的内存模型意识。Rust 只是把问题从“到处都可能出错”变成了“集中在 unsafe 块里出错”。
误区三:“转正就意味着生态已经成熟”——现在的 Rust for Linux 抽象层还在快速演化,很多子系统还没有完整的 safe 绑定。例如,文件系统、网络协议栈的高层抽象仍然比较薄弱。团队如果决定在生产环境里使用,需要评估所需 API 的稳定性和覆盖度。
团队该如何评估和开始
假设你是一个负责嵌入式 Linux 或发行版内核维护的团队,现在可能正在考虑要不要尝试 Rust。可以从下面几个步骤来降低风险:
- 从最外围、最独立的驱动开始。 选一个功能简单、不涉及复杂内核子系统的字符设备驱动作为试点,用 Rust 实现并跑通测试流程。这能让团队熟悉工具链和开发流程,同时不会影响核心功能。
- 检查 bindings 覆盖情况。 在动手之前,用
make rustdoc生成内核 crate 的文档,看看你需要调用的内核 API 是否已经有安全抽象。如果关键函数还在用 unsafe 裸函数调用,那就需要权衡是否值得等待社区完善。 - 关注内核版本兼容性。 Rust 代码需要与 C 代码一起编译,内核构建系统对 Rust 编译器版本有要求。如果你的产品内核版本长期停留在一个 LTS 分支,可能需要 backport 相关的 Rust 基础设施补丁,这会带来额外的维护工作。
- 培养混合代码审查能力。 即使只有一两个 Rust 模块,代码审核时也需要有人能看懂 unsafe 块的正确性以及 Rust 侧的生命周期设计。最好让至少两名核心开发者深入掌握相关概念,而不是只依赖外部贡献者。
在实际生产环境中,已有团队在尝试用 Rust 编写简单的硬件监控驱动或固件升级辅助模块。这类代码通常逻辑不复杂,但直接与硬件交互,过去在 C 里容易因边界检查遗漏引发越界。用 Rust 实现后,即使逻辑简单,至少能在编译期拦住很多低级错误,这对长期维护来说是有吸引力的。
转正只是一个开始
Rust 正式进入主线,更大的意义在于内核社区终于接受了一个事实:单一语言无法满足所有场景下的安全需求,多语言共存可能是内核开发未来几十年需要面对的现实。这不仅仅是技术问题,也是社区治理和开发流程的挑战。C 和 Rust 两种语言、两套思维模式要在同一个代码库里长期协作,如何保持接口清晰、文档完善、审查标准统一,这些软性的工作可能比技术本身更难。
对于普通开发者来说,现在或许还不需要立刻去学 Rust 写内核模块,但至少可以开始理解它的所有权模型、unsafe 边界以及它如何与 C 交互。因为无论你喜不喜欢,当有一天你维护的驱动旁边突然出现一个 .rs 文件时,这件事就不再遥远了。
原创文章,作者:fudengji,如若转载,请注明出处:https://fudengji.cn/article/402/