news 2026/10/22 5:12:11

Rust for Linux 内核模块编译实战:5.16-rc3 工具链配置与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Rust for Linux 内核模块编译实战:5.16-rc3 工具链配置与避坑指南

1. 为什么要在意 Rust for Linux 这件事

Rust 进入 Linux 内核主线这件事,从 2021 年那几轮补丁开始就一直是圈内热点。5.16.0-rc3 这个时间点很特殊,它是 Rust for Linux 支持第一次以相对完整形态出现在内核源码树里的版本之一,虽然还挂着 RFC 的尾巴,但基础设施、Kconfig 开关、样例模块都已经能跑通了。很多人看到新闻的第一反应是“内核要换语言了”,实际并不是——Rust 只是作为第二语言被引入,用于编写驱动和模块,核心调度、内存管理这些还是 C 的天下。

那为什么还要折腾编译?因为你想真正上手写一个 Rust 内核模块,第一步不是写代码,而是让整个工具链跑起来。这中间涉及内核源码、Rust 编译器版本、bindgen、libclang、LLVM 版本、目标架构配置等一堆东西的匹配。我前后在三个不同环境上试过,踩的坑足够写一篇避雷指南。这篇内容就是把我实际编译 5.16.0-rc3 的完整过程拆开讲,包括每个参数为什么这么选、版本为什么必须卡死、报错怎么定位。适合已经会编译普通 Linux 内核、想进一步尝试 Rust 模块的开发者,也适合对内核构建系统好奇、想了解 Rust 如何嵌入 C 项目的人。

核心关键词就几个:Rust for Linux、内核源码编译、5.16.0-rc3、bindgen、LLVM、Kconfig。下面所有操作都围绕这些展开,不跑题。

2. 编译前的整体思路与版本选型逻辑

2.1 为什么版本匹配是生死线

Rust for Linux 在早期阶段对版本极其敏感。内核 5.16.0-rc3 对应的 Rust 支持补丁要求 rustc 版本在 1.56 到 1.57 附近,bindgen 要求 0.56 左右,libclang 要求 LLVM 12 或 13。你如果随手装个最新的 rustc 1.75,编译内核模块时大概率会在core::ffi或者alloc相关的 trait 上炸掉,因为内核里用的#![no_std]环境和标准库的 ABI 假设在版本间有变动。

我试过用 rustup 默认的 stable 去编,报错信息是error[E0463]: can't find crate for 'core',看起来像缺组件,实际是 target 配置和编译器内置的 core 版本对不上。后来锁定到 1.57.0 才顺利通过。所以第一条经验:不要用最新版,用内核文档里明确写的版本。5.16-rc3 的Documentation/rust/quick-start.rst里写的是 rustc 1.56.0 及以上,但实测 1.57.0 最稳。

2.2 工具链三件套的依赖关系

整个编译链里三个东西必须协同:

  • rustc:负责把.rs编译成目标文件。它自带core、alloc、compiler_builtins等 crate 的源码,编译内核模块时需要重新编译这些 crate 以适配内核的no_std环境。
  • bindgen:负责把 C 头文件转成 Rust 的 FFI 绑定。它依赖 libclang 来解析 C 代码,所以 libclang 的版本决定了 bindgen 能识别哪些语法。
  • libclang:LLVM 的 C 接口库。内核里大量宏、static inline函数、位域结构体,bindgen 靠它来生成正确的repr(C)布局。

这三者的版本关系是:rustc 决定 bindgen 的可用范围,bindgen 决定 libclang 的最低版本。5.16-rc3 时期,官方推荐 bindgen 0.56.0 + libclang 12.0。我实际用 libclang 13.0 也能过,但 14 以上会出现clang诊断信息格式变化导致 bindgen 解析失败。

2.3 目标架构与交叉编译的取舍

如果你只是在 x86_64 本机上跑,那目标就是x86_64-unknown-linux-gnu,不需要交叉编译。但如果你想在 ARM64 上跑,就需要aarch64-unknown-linux-gnu的 Rust target,同时内核的CROSS_COMPILE也要配好。我建议第一次尝试一定用 x86_64 本机,减少变量。等本机跑通了,再去折腾交叉编译,否则报错你都分不清是 Rust 的问题还是交叉工具链的问题。

另外,内核配置里CONFIG_RUST依赖CONFIG_HAVE_RUST,而HAVE_RUST只在部分架构上被 select。5.16-rc3 里 x86_64 和 arm64 是支持的,riscv 还在补。所以选 x86_64 最省事。

3. 环境准备与依赖安装的实操细节

3.1 基础构建工具不能省

编译内核本身需要一堆工具,Rust 部分又额外加了几样。我列一个完整的清单,按安装顺序来:

# 基础内核编译工具 sudo apt update sudo apt install -y build-essential flex bison libssl-dev libelf-dev \ bc dwarves zstd libncurses-dev # Rust 相关 sudo apt install -y llvm-13 clang-13 libclang-13-dev lld-13

这里注意llvm-13和clang-13要版本一致,libclang-13-dev提供libclang.so,bindgen 运行时通过LIBCLANG_PATH找它。如果你系统里已经有其他版本的 LLVM,比如 14 或 15,那libclang.so可能指向新版本,导致 bindgen 行为异常。我遇到过libclang-14和bindgen 0.56组合下,解析include/linux/compiler_types.h时卡死,换回 13 就正常。

3.2 rustup 安装与版本锁定

不要用系统包管理器装的 rustc,版本太旧且组件不全。用 rustup:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source $HOME/.cargo/env rustup install 1.57.0 rustup default 1.57.0 rustup component add rust-src rustup component add llvm-tools-preview

rust-src是必须的,因为内核编译时会用-Z build-std重新编译 core 和 alloc。llvm-tools-preview提供llvm-objcopy等工具,内核的objcopy步骤会用到。

装完后验证:

rustc --version # 应该输出 rustc 1.57.0 rustc --print sysroot # 确认 sysroot 路径

3.3 bindgen 的安装与路径配置

bindgen 可以用 cargo 装:

cargo install bindgen --version 0.56.0 --locked

--locked很重要,保证依赖树和发布时一致,避免某个间接依赖升级导致 API 变化。装完后~/.cargo/bin/bindgen就是可执行文件。内核构建系统会通过BINDGEN变量找它,默认就在 PATH 里。

libclang 的路径要显式指定,因为内核的 Makefile 不会自动探测:

export LIBCLANG_PATH=/usr/lib/llvm-13/lib

你可以用llvm-config-13 --libdir确认具体路径。如果这个变量没设,bindgen 会报Unable to find libclang。

3.4 内核源码的获取与分支确认

5.16.0-rc3 是候选版本,不在稳定分支里。直接从内核官方仓库拿:

git clone --depth 1 --branch v5.16-rc3 \ https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git cd linux

--depth 1只拉一个提交,省时间。如果你需要后续打补丁,可以不加--depth。确认一下 Rust 相关文件存在:

ls rust/ # 应该看到 Makefile、kernel、alloc 等目录 ls Documentation/rust/ # quick-start.rst 等文档

如果rust/目录不存在,说明你拿到的不是带 Rust 支持的版本,需要检查 tag 是否正确。

4. 内核配置与 Rust 支持的开启方法

4.1 从 defconfig 开始还是从零配

我建议从defconfig开始,然后手动开 Rust 相关选项。直接make menuconfig从零配太耗时,而且容易漏掉依赖。x86_64 的默认配置:

make defconfig

然后打开配置界面:

make menuconfig

在General setup里找到Rust support,把它设为y。这个选项会连带 selectHAVE_RUST、RUST_IS_AVAILABLE等。如果你在 menuconfig 里搜不到 Rust support,说明你的源码树里init/Kconfig没有包含rust/Kconfig,检查一下init/Kconfig末尾有没有source "rust/Kconfig"。

4.2 关键 Kconfig 选项逐个解释

开完 Rust support 后,还有几个子选项需要确认:

选项作用建议值
CONFIG_RUST总开关y
CONFIG_RUST_IS_AVAILABLE检测工具链是否可用自动
CONFIG_RUSTC_VERSION记录 rustc 版本自动
CONFIG_RUST_OPTIMIZATION_LEVEL优化等级2
CONFIG_RUST_DEBUG_ASSERTIONS调试断言y(开发时)
CONFIG_SAMPLES_RUST编译 Rust 样例模块y

CONFIG_SAMPLES_RUST一定要开,它会编译samples/rust/下的样例模块,是验证工具链是否正常的最快方式。如果这个能编过,说明 rustc、bindgen、libclang 都配对了。

4.3 用 scripts/config 做非交互式配置

如果你要写脚本自动化,可以用scripts/config:

scripts/config --enable RUST scripts/config --enable SAMPLES_RUST scripts/config --set-val RUST_OPTIMIZATION_LEVEL 2 make olddefconfig

olddefconfig会把新增的依赖自动补全,避免手动处理 select 关系。这一步之后,.config里应该能看到CONFIG_RUST=y和CONFIG_SAMPLES_RUST=y。

注意:如果你在 menuconfig 里开了 Rust 但olddefconfig后CONFIG_RUST又变回 n,说明RUST_IS_AVAILABLE检测失败,通常是 rustc 版本不对或rust-src组件没装。

5. 编译过程与核心环节拆解

5.1 第一次编译:先编 Rust 样例

不要一上来就make -j$(nproc)全量编译,那样出错信息会被淹没。先单独编 Rust 样例:

make LLVM=1 rust_samples

LLVM=1告诉内核用 LLVM 工具链而不是 GNU 工具链。虽然内核主体还是用 gcc 编,但 Rust 部分需要 clang 来生成 LLVM IR,所以这个变量必须加。如果你不加,会报clang: not found或者llvm-objcopy: not found。

编译过程中你会看到类似输出:

RUSTC [M] samples/rust/rust_minimal.o BINDGEN samples/rust/rust_minimal.bindings.h CC [M] samples/rust/rust_minimal.mod.o LD [M] samples/rust/rust_minimal.ko

如果走到LD这步,恭喜你,工具链通了。如果卡在BINDGEN,多半是 libclang 路径问题;卡在RUSTC,多半是 rustc 版本或rust-src问题。

5.2 全量编译的并行度与内存控制

样例编过后,可以全量编:

make LLVM=1 -j$(nproc)

但注意,Rust 编译比 C 编译吃内存。每个rustc进程可能占 1-2 GB,如果你nproc是 16 而内存只有 16 GB,很容易 OOM。我建议并行度设为min(nproc, 内存GB/2)。比如 16 GB 内存就用-j8。

如果中途 OOM,内核的 OOM killer 会杀掉 rustc,报错信息可能是signal: 9或Killed。这时候降低并行度重跑即可,已经编好的目标文件不会重编。

5.3 编译产物在哪里

Rust 模块的产物和 C 模块一样,在samples/rust/下:

samples/rust/rust_minimal.ko samples/rust/rust_minimal.o samples/rust/rust_minimal.bindings.o

.ko就是最终的内核模块,可以用insmod加载。.bindings.o是 bindgen 生成的绑定代码编译出的目标文件,里面是 C 头文件对应的 Rust FFI 声明。

你可以用modinfo看一下:

modinfo samples/rust/rust_minimal.ko

应该能看到license: GPL、description: Rust minimal sample等信息。

5.4 加载与卸载测试

加载前先确认内核版本匹配:

uname -r # 必须和你编译的内核版本一致,否则 insmod 会报 version magic 错误

如果你是在当前运行内核上编的模块,直接:

sudo insmod samples/rust/rust_minimal.ko dmesg | tail # 应该看到 "Rust minimal sample (init)" sudo rmmod rust_minimal dmesg | tail # 应该看到 "Rust minimal sample (exit)"

如果insmod报Invalid module format,说明模块编译时的内核配置和当前运行内核不一致,需要重启到新编的内核或者用--force(不推荐)。

6. 常见报错与排查速查

6.1 报错信息与对应原因

报错原因解决
can't find crate for 'core'rust-src 没装或版本不对rustup component add rust-src
Unable to find libclangLIBCLANG_PATH 未设export LIBCLANG_PATH=/usr/lib/llvm-13/lib
clang: not found没加 LLVM=1make LLVM=1
unknown argument: -Zbuild-stdrustc 版本太旧升级到 1.57.0
bindgen: command not foundbindgen 没装或不在 PATHcargo install bindgen --version 0.56.0
Killed或signal: 9内存不足降低-j并行度
version magic mismatch模块与运行内核不匹配重启到新内核或重新编译

6.2 我踩过的三个坑

第一个坑是 libclang 版本冲突。我系统里原本有 LLVM 14,libclang.so指向 14,bindgen 0.56 解析include/linux/types.h时直接段错误。后来用update-alternatives把默认 libclang 切到 13 才解决。所以如果你机器上有多个 LLVM,一定要确认LIBCLANG_PATH指向正确版本。

第二个坑是 rustc 的-Z build-std需要 nightly 特性,但 1.57.0 是 stable。实际上内核构建系统用的是RUSTC_BOOTSTRAP=1环境变量来在 stable 上开 nightly 特性。如果你手动跑 rustc 命令测试,不加这个变量会报-Z参数未知。内核 Makefile 里已经设了,不用你操心,但你要知道这个机制。

第三个坑是CONFIG_RUST开了但CONFIG_SAMPLES_RUST没开,导致make rust_samples找不到目标。rust_samples这个 make target 只在SAMPLES_RUST=y时才存在。所以配置时两个都要开。

6.3 如何确认工具链版本匹配

写一个简单的检查脚本:

#!/bin/bash echo "rustc: $(rustc --version)" echo "bindgen: $(bindgen --version)" echo "libclang: $(llvm-config-13 --version)" echo "LIBCLANG_PATH: $LIBCLANG_PATH" ls $LIBCLANG_PATH/libclang.so* 2>/dev/null || echo "libclang.so not found"

输出里 rustc 应该是 1.57.0,bindgen 是 0.56.0,libclang 是 13.x。三者都对了,编译基本不会出大问题。

7. 从编译到写第一个 Rust 内核模块

7.1 样例模块的代码结构

samples/rust/rust_minimal.rs是最简单的模块,核心就两个函数:

#![no_std] #![feature(allocator_api, global_asm)] use kernel::prelude::*; module! { type: RustMinimal, name: b"rust_minimal", author: b"Rust for Linux Contributors", description: b"Rust minimal sample", license: b"GPL", } struct RustMinimal; impl kernel::Module for RustMinimal { fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> { pr_info!("Rust minimal sample (init)\n"); Ok(RustMinimal) } } impl Drop for RustMinimal { fn drop(&mut self) { pr_info!("Rust minimal sample (exit)\n"); } }

module!宏展开后生成 C 侧的module_init和module_exit符号,impl Module对应初始化,Drop对应卸载。pr_info!是内核的打印宏,底层调用printk。

7.2 自己加一个模块的步骤

在samples/rust/下新建my_rust_mod.rs,复制rust_minimal.rs改个名字,然后在samples/rust/Makefile里加一行:

obj-$(CONFIG_SAMPLES_RUST) += my_rust_mod.o

再在samples/rust/Kconfig里加一个配置项:

config SAMPLE_RUST_MY_MOD tristate "My Rust module" depends on SAMPLES && RUST help This is my first Rust kernel module.

然后make menuconfig里选中它,重新make LLVM=1 rust_samples就能编出来。这个流程和加 C 模块几乎一样,区别只是源文件后缀和编译规则由rust/Makefile里的规则处理。

7.3 调试技巧:printk 与 dmesg

Rust 模块里没有println!,只能用pr_info!、pr_err!、pr_debug!这些宏。pr_debug!默认不输出,需要开CONFIG_DYNAMIC_DEBUG并在运行时用echo 'module my_rust_mod +p' > /sys/kernel/debug/dynamic_debug/control打开。

如果模块加载后 dmesg 没输出,先确认dmesg -n 8把控制台日志级别调到最高,再确认pr_info!的日志级别是 6,低于默认控制台级别 7 的话不会打到串口,但 dmesg 里能看到。

8. 这套流程的适用边界与后续扩展

5.16.0-rc3 的 Rust 支持还处于早期,能做的事情有限:只能写最简单的字符设备、platform driver 的骨架,很多内核 API 还没有 Rust 绑定。比如你要操作struct file_operations,得自己用 bindgen 生成绑定,然后手写unsafe块调用。到了 6.x 系列,Rust 抽象层完善了很多,kernel::file、kernel::io_buffer这些模块都有了,写起来舒服得多。

所以如果你只是想体验一下,5.16-rc3 足够;如果你想真正用 Rust 写生产级驱动,建议直接上 6.1 或更新的 LTS 版本,工具链要求也变成了 rustc 1.62+、bindgen 0.60+,但整体流程和这篇里讲的完全一样,只是版本号换一下。

另外,编译过程中生成的.bindings.o文件可以单独拿出来看,里面是 bindgen 生成的 Rust 代码对应的目标文件。如果你想研究某个 C 结构体在 Rust 里长什么样,可以用bindgen --output手动生成一份.rs文件,比在内核构建系统里翻来翻去快得多。

我个人在实际操作中的体会是,Rust for Linux 的编译难点不在 Rust 本身,而在工具链版本的对齐和 libclang 的路径管理。只要把 rustc、bindgen、libclang 三者的版本锁死,剩下的就是普通内核编译的流程。第一次编建议留出至少 30 GB 磁盘空间和 8 GB 以上内存,全量编译大概 20 到 40 分钟,取决于机器性能。编过一次之后,后续增量编译就很快了,改一个 Rust 文件重新编也就几秒钟。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 5:12:08

智能面积:AI Agent 重塑云计算资源治理的新范式

1. “智能面积”到底是个什么思路第一次听到“智能面积”这四个字&#xff0c;我第一反应是云计算圈又开始造新词了。但把阿里云这份 AI Agent Handbook 翻完之后&#xff0c;我的判断变了——这个词不像是在概念包装&#xff0c;倒像是在认真描述一种新的资源管理哲学&#xf…

作者头像 李华
网站建设 2026/10/22 5:12:07

ucore Lab3物理内存管理:从页帧分配到首次适配算法实战

1. 从物理内存说起&#xff1a;ucore Lab3 到底在解决什么问题做过操作系统课程实验的人大多有个共同感受&#xff1a;Lab1 和 Lab2 还能靠“照着文档敲代码”混过去&#xff0c;到了 Lab3 突然就撞上一堵墙。这堵墙的名字叫物理内存管理。你打开实验指导书&#xff0c;看到一堆…

作者头像 李华
网站建设 2026/10/22 5:11:44

手写最优潮流程序:建模、内点法实现与数值坑详解

几年前我做调度辅助决策模块时&#xff0c;拿到一张典型的机组报价表和负荷预测曲线。第一版方案是人工按等微增率分配负荷&#xff0c;再跑一遍潮流去检查电压和线路负载。看着能过&#xff0c;可一算总费用&#xff0c;比后来改用最优潮流控制程序算出的方案贵了将近百分之四…

作者头像 李华
网站建设 2026/10/22 5:10:19

BEVFormer TensorRT部署:int8量化+自定义插件实战指南

简介&#xff1a;本资源是一套面向自动驾驶感知算法工程师与嵌入式AI部署开发者的BEVFormer模型TensorRT高效推理方案&#xff0c;聚焦解决BEVFormer在边缘端部署时的性能瓶颈问题&#xff0c;涵盖int8量化加速与关键自定义算子实现两大核心技术难点。压缩包共452个文件&#x…

作者头像 李华
网站建设 2026/10/22 5:10:15

YOLO11-C3k2-CTA:细长台风螺旋带旋转框检测实战

把YOLO11用在台风螺旋带识别上&#xff0c;最初是我在一次气象影像项目中被逼出来的决定。红外云图里的螺旋带不是人眼找不到&#xff0c;而是它太“长条”——一条雨带可以从台风中心外围甩出去几百公里&#xff0c;宽度却只有十几个像素。常规目标检测框住飞机、车辆这类物体…

作者头像 李华
网站建设 2026/10/22 5:09:46

自动驾驶CNN视觉感知包部署实战:从ZIP解压到Jetson Nano 18FPS

简介&#xff1a;本资源是一份面向人工智能与自动驾驶方向学习者、高校课程设计及科研初学者的完整项目实践包&#xff0c;聚焦卷积神经网络在目标检测、障碍物识别与路径规划等核心环节的实际落地。压缩包共124个文件&#xff0c;含14个Python源码&#xff08;涵盖CNN模型构建…

作者头像 李华