一、为什么 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 实现差异
代码行数1200980-18%
执行时间 (μs)2.452.51+2.4%
内存占用 (KB)48.247.8-0.8%
编译时间 (s)4538-16%
运行时内存错误0 (静态分析后)0 (编译期保证)Rust 更优
unsafe 块数量N/A12可控边界

结论: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-Linuxgithub.com/Rust-for-Linux/linux官方内核 Rust 支持仓库
Rust-bindegenrust-lang/rust-bindgen自动生成 Rust FFI 绑定
kernel craterust/kernel (内核内)内核 Rust API 封装层
pinned-initgithub.com/Rust-for-Linux/pinned-init安全初始化和固定内存
Rust 内核文档docs.rs/kernel内核 Rust API 文档

10.2 学习路径

  1. 基础 Rust 掌握:所有权、生命周期、trait、泛型
  2. 阅读内核 Rust 示例:samples/rust/ 下每个文件都是最好的教程
  3. 绑定 C 子系统:理解 bindgen 和 FFI 边界
  4. 编写简单模块:从 character device 开始
  5. 贡献上游:向 Rust-for-Linux 提交补丁
  6. 关注 RFC:内核 Rust 子系统的设计决策和讨论

十一、未来展望

  • Linux 6.14+:持续完善 Rust 子系统 API
  • Binder IPC 重写:Android 核心组件 Rust 迁移进行中
  • 文件系统:EROFS 等文件系统的 Rust 实现探索
  • 驱动框架:更多子系统提供 Rust 绑定
  • 稳定化:减少 nightly 依赖,最终进入 stable
  • 硬件抽象层:可能的安全 HAL Rust 重写

Rust 的加入不会让 Linux 内核一夜之间告别 C,但它为内核开发者提供了一个强大的新工具——在保持性能的同时,将一整类内存安全漏洞拒之门外。正如 Linus 所说:"Rust 不是银弹,但它在正确的地方提供了正确的安全性保证。"

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部