乐趣区

关于阿里云:如何使用-rust-写内核模块

作者:卜比

近年来,Rust 语言以内存平安、高可靠性、零形象等能力取得大量开发者关注,而这些个性恰好是内核编程中所须要的,所以咱们看下如何用 rust 来写 Linux 内核模块。

Rust 与内核模块

尽管 Rust 反对曾经在 LinuxKernel6.1 版本合并到主线了,所以实践上来说,开发者能够应用 Rust 来为 Linux6.1 写内核模块。

但理论开发工作中,内核版本不是最新的,比方 Debian 11 的内核就是 5.10 版本的,那么在这种状况下,该如何用 Rust 写内核模块呢?

原理

  1. Rust 如何向内核注册回调、如何调用内核代码。Rust 和 C 的互操作性
  2. Rust 如何编译到指标平台上。Rust 的 target 配置
  3. Rust 如何申明内核模块入口、并增加非凡 section。Rust 内核模块的二进制约定

Rust 和 C 的互操作性

第一个问题基本上就是 C 和 Rust 的互操作性了。

得益于 Rust 的抽象层次,C 语言和 Rust 的相互调用都是比拟容易的。rust 官网也提供了 bindgen 这样,依据 .h 文件生成 .rs 文件的库。

这样一来,貌似间接应用 bindgen 将内核头文件翻译成 .rs 就能够了?

但还有一个问题,如何获取内核头文件门路呢?

能够应用一个 dummy 内核模块,在编译过程中把编译参数导出来,其中蕴含了头文件门路,编译参数等,用于 bindgen 生成代码。

Rust 和 target 配置

内核模块和一般的程序相比,次要的不同在于:

  1. 内核模块是 freestanding 的,没有 libc、内存调配也比拟原始
  2. 内核模块对于异样解决等有非凡约定

Rust 提供了 no_std 机制,能够让 rust 代码编译成 freestanding 二进制;Rust 也提供了自定义 target 的形式,能够自定义申明生成二进制的标准。

内核模块的二进制约定

内核对内核模块有一些约定:

  • 通过 .modinfo 等 section 来申明模块信息
  • 提供 init_module、cleanup_module 来提供内核模块的装置和卸载性能

在这一块,Rust 提供了 link_section 来自定义 section,也反对 extern “C” 来导出函数。

此外,这些底层的操作,能够由内核提供一些 C 语言宏来简化代码,Rust 也提供了宏,能够用来做相似的事件。

一个小例子

说了这么多,咱们来看一个带正文的例子:

#![no_std]
// no_std 用于示意没有 std 库,即 freestanding 环境
extern crate alloc;

use alloc::borrow::ToOwned;
use alloc::string::String;

// 咱们以 printk 为底层,提供了 println
use linux_kernel_module::println;

// 这个 struct 代表内核模块
struct HelloWorldModule {message: String,}

// 实现内核模块初始化办法
impl linux_kernel_module::KernelModule for HelloWorldModule {fn init() -> linux_kernel_module::KernelResult<Self> {println!("Hello kernel module from rust!");
        Ok(HelloWorldModule {message: "on the heap!".to_owned(),
        })
    }
}

// 提供内核模块卸载办法
impl Drop for HelloWorldModule {fn drop(&mut self) {println!("My message is {}", self.message);
        println!("Goodbye kernel module from rust!");
    }
}

// 通过 kernel_module 宏,export 了内核模块的相干信息
linux_kernel_module::kernel_module!(
    HelloWorldModule,
    author: b"Fish in a Barrel Contributors",
    description: b"An extremely simple kernel module",
    license: b"GPL"
);

具体的构建和运行:

$ cd linux-kernel-module-rust/hello-world
$ RUST_TARGET_PATH=$(pwd)/.. cargo +nightly xbuild --target x86_64-linux-kernel-module
$ make
$ insmod helloworld.ko
$ rmmod helloworld
$ dmesg | tail -n 3
[521088.916091] Hello kernel module from rust!
[521174.204889] My message is on the heap!
[521174.204891] Goodbye kernel module from rust!

已在内核 5.10.0-17-amd64 上测试。

具体的代码以及相干配置,能够参考 GitHub 仓库:https://github.com/robberphex…

一些小细节

  • VSCode 反对

因为 rust-analyzer 对于自定义 target,多模块的反对不够,所以咱们临时须要手动配置下 settings.json 能力失常开发:

{
    "rust-analyzer.cargo.extraEnv": {"RUST_TARGET_PATH": "/root/linux-kernel-module-rust"},
    "rust-analyzer.cargo.target": "x86_64-linux-kernel-module",
    "rust-analyzer.server.extraEnv": {
        "RA_LOG": "lsp_server=debug",
        "RUST_TARGET_PATH": "/root/linux-kernel-module-rust"
    },
    "rust-analyzer.trace.server": "verbose",
    "rust-analyzer.linkedProjects": [
        "hello-world/Cargo.toml",
        "Cargo.toml"
    ],
}
  • 其余高级性能

比方字符设施、sysctl 等性能,能够参考我的项目中相干的测试代码。

更多布局

  • 和 Rust-for-Linux 放弃 API 统一。(Rust-for-Linux 例子:https://github.com/Rust-for-L…
  • Rust 提供的内存安全性、零形象等能力,恰好是内核畛域亟需的个性和能力。比方内核态如果呈现内存透露、野指针,一会造成很大影响、二来也很难调试。在这个畛域,咱们能够借助 Rust 的能力来结构更加平安、更大的我的项目。

原始我的项目是 fishinabarrel/linux-kernel-module-rust,但目前提醒应用 rust-for-linux,曾经 archived。然而,思考到目前旧版本内核还有很多,所以我从新修复了这个我的项目的一些环境,让大家在旧版本内核上可能用 Rust 编写内核模块。

退出移动版