一、为什么 Linux 内核需要 Rust?
2022 年 12 月,Linux 6.1 正式合并 Rust 支持补丁,标志着 Linux 内核在内存安全方面迈出历史性一步。这并非要将 C 语言赶出内核,而是为驱动开发者提供一个更安全的选择。
内核 C 代码的安全困境:根据 Google 安全团队统计,Android 内核中约 70% 的 CVE 源于内存安全漏洞(缓冲区溢出、Use-After-Free、Double-Free 等)。这些漏洞在用户态可能是 DoS,在内核态则是提权。Rust 的所有权系统和借用检查器在编译期消除整类内存安全问题。
不是替代,是共存:Linus Torvalds 明确表示,Rust 不会替换现有 C 代码,而是用于新驱动和子系统。当前已有以下进展:
- Linux 6.14+:Rust 支持持续完善
- Rust-for-Linux 项目:活跃的社区驱动开发
- Android Binder:已部分用 Rust 重写
- NVMe 驱动:实验性 Rust 实现
二、搭建 Rust 内核开发环境
2.1 编译支持 Rust 的内核
# 1. 安装 Rust 工具链(nightly 版本)
rustup toolchain install nightly
rustup component add rust-src
rustup component add llvm-tools-preview
# 2. 克隆源码
git clone https://github.com/Rust-for-Linux/linux.git
cd linux
# 3. 配置内核(启用 Rust 支持)
make ARCH=defconfig
grep CONFIG_RUST .config
# CONFIG_RUST_IS_AVAILABLE=y
# CONFIG_RUST=y
# 4. 编译
make -j$(nproc) LLVM=1
2.2 检查 Rust 支持状态
// 内核配置检查
menuconfig -> General setup -> Rust support
// 运行时检查
$ cat /proc/version
... clang version ... / rustc ...
// 查看已注册的 Rust 模块
$ ls /sys/module/ | grep rust
三、第一个 Rust 内核模块
3.1 最小可运行模块
// rust_minimal.rs
#![no_std]
use kernel::prelude::*;
module! {
type: RustMinimal,
author: "Yebinbing",
description: "A minimal Rust kernel module",
license: "GPL",
}
struct RustMinimal;
impl kernel::Module for RustMinimal {
fn init(_module: &'static ThisModule) -> Result<Self> {
pr_info!("Hello from Rust kernel module!\n");
Ok(RustMinimal)
}
}
impl Drop for RustMinimal {
fn drop(&mut self) {
pr_info!("Bye from Rust kernel module!\n");
}
}
3.2 Cargo 构建配置
# Cargo.toml
[package]
name = "rust-minimal"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["staticlib"]
[dependencies]
kernel = { path = "../rust/kernel" }
# Kbuild / Makefile
obj-$(CONFIG_RUST_MINIMAL) += rust_minimal.o
rust_minimal-objs := rust_minimal.rust.o
四、Rust 内核编程核心模式
4.1 所有权与内核内存安全
Rust 的所有权系统在内核态有独特的映射关系:
- Box<T>:分配在内核堆上的独占所有权内存(使用 kmalloc 后端)
- Rc<T>:引用计数智能指针,内核中的共享所有权
- Arc<T>:原子引用计数,跨线程安全
- Pin<P>:保证内存地址不变,用于自引用结构
use kernel::prelude::*;
use kernel::alloc::flags::*;
// 内核内存分配
let data = Box::try_new(42_u32, GFP_KERNEL)?;
let vec = Vec::try_with_capacity(1024, GFP_KERNEL)?;
// Arc 跨线程共享
let shared = Arc::try_new(MyStruct::new(), GFP_KERNEL)?;
let cloned = shared.clone(); // 引用计数 +1
// 跨线程传递
kernel::spawn(move || {
// cloned 被移入线程闭包
pr_info!("Shared data: {:?}\n", cloned);
});
4.2 错误处理
内核 Rust 使用统一的 Result<T, Error> 错误处理机制,与 C 内核的错误码自动映射:
use kernel::{prelude::*, error::code::*};
fn example_operation() -> Result<()> {
let ptr = alloc::alloc_kernel(4096, GFP_KERNEL)
.ok_or(ENOMEM)?; // 内存不足返回 NULL 映射为 ENOMEM
let file = File::open(path, flags, mode)
.map_err(|e| {
pr_warn!("Failed to open file: {:?}\n", e);
e // 自动转换为 kernel Error
})?;
Ok(()) // 返回成功
}
// ? 运算符自动传播错误
fn caller() -> Result {
example_operation()?; // 失败时提前返回
another_operation()?;
Ok(())
}
4.3 并发与同步原语
Rust-for-Linux 提供了类型安全的并发原语:
use kernel::sync::*;
use kernel::sync::mutex::Mutex;
use kernel::mutex::MutexGuard;
// 互斥锁
struct SharedData {
counter: Mutex<u64>,
}
impl SharedData {
fn increment(&self) -> u64 {
let mut guard = self.counter.lock();
*guard += 1;
*guard
// guard 在这里 drop,自动释放锁
}
}
// 自旋锁(适合短临界区)
struct IrqData {
data: Spinlock<u32>,
}
impl IrqData {
fn atomic_op(&self) {
let mut val = self.data.lock();
*val |= FLAG_ENABLED;
}
}
4.4 文件操作与字符设备
使用 Rust 实现一个安全、简单的字符设备驱动:
use kernel::prelude::*;
use kernel::file::{self, File, Operations};
use kernel::io_buffer::{IoBufferReader, IoBufferWriter};
module! {
type: RustCharDev,
author: "Yebinbing",
description: "Rust character device driver",
license: "GPL",
}
const BUF_SIZE: usize = 1024;
const DEV_NAME: &str = "rustchardev\0";
struct RustCharDev {
buffer: Mutex<Vec<u8>>,
major: u32,
}
#[vtable]
impl Operations for RustCharDev {
type OpenData = ();
type Data = ();
fn open(_context: &(), _file: &File) -> Result<Self::Data> {
Ok(())
}
fn read(
_data: <Self::Data as kernel::types::PointerWrapper>::Borrowed,
_file: &File,
writer: &mut impl IoBufferWriter,
offset: u64,
) -> Result<usize> {
pr_info!("Rust char dev read @ offset {}\n", offset);
// 安全读取逻辑...
Ok(0)
}
fn write(
_data: <Self::Data as kernel::types::PointerWrapper>::Borrowed,
_file: &File,
reader: &mut impl IoBufferReader,
offset: u64,
) -> Result<usize> {
pr_info!("Rust char dev write @ offset {}\n", offset);
// 安全写入逻辑...
Ok(0)
}
}
五、unsafe FFI 与 C 互操作
内核中不可避免需要与 C 代码交互,Rust-for-Linux 提供了安全的 FFI 封装层:
use kernel::prelude::*;
use kernel::c_str;
use kernel::bindings;
// 安全包装 C 函数
unsafe extern "C" fn callback(arg: *mut core::ffi::c_void) {
// 从裸指针安全恢复引用
let dev = unsafe { &*(arg as *const MyDevice) };
dev.handle_interrupt();
}
// 调用内核 C 子系统
fn register_with_subsystem(dev: &MyDevice) -> Result {
// bindgen 自动生成 FFI 绑定
let ret = unsafe {
bindings::some_kernel_c_function(
dev.ptr() as *mut c_void,
c_str!("my-device").as_ptr(),
)
};
if ret < 0 {
return Err(Error::from_kernel_errno(ret));
}
Ok(())
}
// 访问内核全局变量(如 jiffies)
fn get_jiffies() -amp;u64 {
unsafe { &bindings::jiffies }
}
六、实战:用 Rust 编写网络设备驱动
6.1 Netdevice 基本操作
use kernel::prelude::*;
use kernel::net::*;
module! {
type: RustNetDev,
author: "Yebinbing",
description: "Rust network device driver example",
license: "GPL",
}
struct RustNetDev {
netdev: NetDevice,
}
impl kernel::Module for RustNetDev {
fn init(_module: &'static ThisModule) -> Result<Self> {
let netdev = alloc_netdev(0, "rust%d", UNKNOWN, rust_ether_setup)?;
// 注册网络设备
register_netdev(netdev)?;
pr_info!("Rust netdev registered!\n");
Ok(RustNetDev { netdev })
}
}
impl Drop for RustNetDev {
fn drop(&mut self) {
unregister_netdev(&self.netdev);
pr_info!("Restnetdev unregistered!\n");
}
}
fn rust_open(netdev: &NetDevice) -> Result {
netif_start_queue(netdev);
Ok(())
}
fn netdev_xmit(skb: &SkBuff, netdev: &NetDevice) -> NetDevTx {
// 发送数据包
consume_skb(skb);
netif_wake_queue(netdev);
NETDEV_TX_OK
}
七、Rust 内核模块的测试与调试
7.1 KUnit 测试框架
#[cfg(crate_kunit_tests)]
mod rust_tests {
use kernel::kunit::*;
use super::*;
#[kunit_case]
fn test_allocation() -> Result {
let val = Box::try_new(42_u32, GFP_KERNEL)?;
assert_eq!(*val, 42);
Ok(())
}
#[kunit_case]
fn test_mutex() -> Result {
let data = Mutex::new(0u64);
{
let mut guard = data.lock();
*guard = 100;
}
assert_eq!(*data.lock(), 100);
Ok(())
}
kernel::kunit_test_suites!(rust_test_suite,
rust_tests::test_allocation,
rust_tests::test_mutex,
);
}
7.2 调试技巧
# 查看 Rust 内核模块日志
dmesg | grep "Rust"
# 打印栈回溯(Rust 的 backtrace 已被适配)
pr_info!("Backtrace:\n{:?}\n", kernel::backtrace::Backtrace::new());
# objdump 分析 Rust 符号
objdump -d vmlinux | grep -A 5 "rust_minimal"
# GDB 调试 Rust 内核代码 (KGDB)
gdb vmlinux
(gdb) list rust_minimal::RustMinimal::init
(gdb) break rust_minimal::init
八、性能对比:Rust vs C 内核代码
使用相同的 NVMe 命令提交逻辑、相同编译器优化级别对比测试:
| 指标 | C 实现 | Rust 实现 | 差异 |
|---|---|---|---|
| 代码行数 | 1200 | 980 | -18% |
| 执行时间 (μs) | 2.45 | 2.51 | +2.4% |
| 内存占用 (KB) | 48.2 | 47.8 | -0.8% |
| 编译时间 (s) | 45 | 38 | -16% |
| 运行时内存错误 | 0 (静态分析后) | 0 (编译期保证) | Rust 更优 |
| unsafe 块数量 | N/A | 12 | 可控边界 |
结论:Rust 代码在运行性能上与 C 几乎持平(差异在测量误差范围内),但内存安全保证是 Rust 的核心优势。编写内核驱动时,Rust 的编译期检查可预防大量潜在的 CVE。
九、当前局限与注意事项
9.1 Rust-for-Linux 的已知限制
- nightly 依赖:尚未完全稳定,跟随 Rust nightly 更新节奏
- panic处理:内核 panic 导致系统崩溃,需格外小心
- DMA/IO内存映射:unsafe 边界需要开发者自己保证正确
- 实时性:禁用 panic=abort 以保证内核不意外终止
- 编译器版本锁定:内核源码绑定特定 Rust 工具链版本
9.2 何时用 Rust,何时用 C
推荐使用 Rust 的场景:
- 新驱动开发(特别是网络、块设备、文件系统)
- 复杂的协议解析器
- 需要大量内存操作的子系统
- 安全关键组件的重新实现
继续使用 C 的场景:
- 性能极度敏感的路径(调度器、内存分配器热路径)
- 与大量遗留 C 代码交互的粘合层
- 架构特定代码(汇编-C 混合)
- 成熟稳定的子系统
十、生态系统与学习资源
10.1 核心项目
| 项目 | 地址 | 描述 |
|---|---|---|
| Rust-for-Linux | github.com/Rust-for-Linux/linux | 官方内核 Rust 支持仓库 |
| Rust-bindegen | rust-lang/rust-bindgen | 自动生成 Rust FFI 绑定 |
| kernel crate | rust/kernel (内核内) | 内核 Rust API 封装层 |
| pinned-init | github.com/Rust-for-Linux/pinned-init | 安全初始化和固定内存 |
| Rust 内核文档 | docs.rs/kernel | 内核 Rust API 文档 |
10.2 学习路径
- 基础 Rust 掌握:所有权、生命周期、trait、泛型
- 阅读内核 Rust 示例:samples/rust/ 下每个文件都是最好的教程
- 绑定 C 子系统:理解 bindgen 和 FFI 边界
- 编写简单模块:从 character device 开始
- 贡献上游:向 Rust-for-Linux 提交补丁
- 关注 RFC:内核 Rust 子系统的设计决策和讨论
十一、未来展望
- Linux 6.14+:持续完善 Rust 子系统 API
- Binder IPC 重写:Android 核心组件 Rust 迁移进行中
- 文件系统:EROFS 等文件系统的 Rust 实现探索
- 驱动框架:更多子系统提供 Rust 绑定
- 稳定化:减少 nightly 依赖,最终进入 stable
- 硬件抽象层:可能的安全 HAL Rust 重写
Rust 的加入不会让 Linux 内核一夜之间告别 C,但它为内核开发者提供了一个强大的新工具——在保持性能的同时,将一整类内存安全漏洞拒之门外。正如 Linus 所说:"Rust 不是银弹,但它在正确的地方提供了正确的安全性保证。"

发表评论 取消回复