通用信息

本文档包含在内核中使用 Rust 支持时需要了解的有用信息。

no_std

内核中的 Rust 支持只能链接 core,而不能链接 std。要在内核中使用的 Crates 必须使用 #![no_std] 属性来选择这种行为。

代码文档

Rust 内核代码使用其内置的文档生成器 rustdoc 来生成文档。

生成的 HTML 文档包含集成搜索、链接项(例如类型、函数、常量)、源代码等。可以在以下位置阅读:

对于 linux-next,请参阅

每个主要版本也都有标签,例如

这些文档也可以在本地轻松生成和阅读。这非常快(与编译代码本身的速度在同一个量级),并且不需要特殊的工具或环境。这还有一个额外的好处,即它们将根据所使用的特定内核配置进行定制。要生成它们,请使用与编译相同的调用命令以及 rustdoc 目标,例如

make LLVM=1 rustdoc

要在网络浏览器中本地阅读文档,请运行例如

xdg-open Documentation/output/rust/rustdoc/kernel/index.html

要了解如何编写文档,请参阅 编码准则

额外的 lint 检查

虽然 rustc 是一个非常有帮助的编译器,但通过 Rust 代码检查工具 clippy 可以获得一些额外的 lint 和分析。要启用它,请在编译时传入 CLIPPY=1,例如

make LLVM=1 CLIPPY=1

请注意,Clippy 可能会改变代码生成,因此在构建用于生产环境的内核时不应启用它。

抽象与绑定

抽象是包装 C 端内核功能的 Rust 代码。

为了使用 C 端的函数和类型,创建了绑定。绑定是 C 端这些函数和类型在 Rust 中的声明。

例如,人们可以在 Rust 中编写一个 Mutex 抽象,它包装了 C 端的 struct mutex 并通过绑定调用其函数。

并非所有内核内部 API 和概念都有对应的抽象,但随着时间的推移,其覆盖范围将会扩大。“叶子”模块(例如驱动程序)不应直接使用 C 绑定。相反,子系统应根据需要提供尽可能安全的抽象。

                                                rust/bindings/
                                               (rust/helpers/)

                                                   include/ -----+ <-+
                                                                 |   |
  drivers/              rust/kernel/              +----------+ <-+   |
    fs/                                           | bindgen  |       |
   .../            +-------------------+          +----------+ --+   |
                   |    Abstractions   |                         |   |
+---------+        | +------+ +------+ |          +----------+   |   |
| my_foo  | -----> | | foo  | | bar  | | -------> | Bindings | <-+   |
| driver  |  Safe  | | sub- | | sub- | |  Unsafe  |          |       |
+---------+        | |system| |system| |          | bindings | <-----+
     |             | +------+ +------+ |          |  crate   |       |
     |             |   kernel crate    |          +----------+       |
     |             +-------------------+                             |
     |                                                               |
     +------------------# FORBIDDEN #--------------------------------+

核心思想是将与内核 C API 的所有直接交互封装到经过仔细审查和文档记录的抽象中。这样,只要满足以下条件,这些抽象的使用者就不会引入未定义行为(UB):

  1. 抽象是正确的(“健全的”)。

  2. 任何 unsafe 块都遵循调用块内操作所需的安全性契约。类似地,任何 unsafe impl 都遵循实现该 trait 所需的安全性契约。

绑定

通过将 include/ 中的 C 头文件包含到 rust/bindings/bindings_helper.h 中,bindgen 工具将为包含的子系统自动生成绑定。构建后,请查看 rust/bindings/ 目录中的 *_generated.rs 输出文件。

对于 bindgen 无法自动生成的 C 头文件部分(例如 C 的 inline 函数或非常规宏),可以在 rust/helpers/ 中添加一个小的包装函数,以便 Rust 端也可以使用它。

抽象

抽象是绑定与内核内部使用者之间的层。它们位于 rust/kernel/ 中,其作用是将对绑定的不安全访问封装为尽可能安全的 API,并将其暴露给使用者。抽象的使用者包括用 Rust 编写的驱动程序或文件系统等。

除了安全性方面,抽象还应该是“符合人体工程学”的,即将 C 接口转换为“惯用”的 Rust 代码。基本的例子是将 C 的资源获取和释放转换为 Rust 的构造函数和析构函数,或者将 C 的整数错误码转换为 Rust 的 Result

条件编译

Rust 代码可以根据内核配置进行条件编译

#[cfg(CONFIG_X)]       // Enabled               (`y` or `m`)
#[cfg(CONFIG_X="y")]   // Enabled as a built-in (`y`)
#[cfg(CONFIG_X="m")]   // Enabled as a module   (`m`)
#[cfg(not(CONFIG_X))]  // Disabled

对于 Rust 的 cfg 不支持的其他谓词(例如带有数值比较的表达式),可以定义一个新的 Kconfig 符号

config RUSTC_HAS_SPAN_FILE
        def_bool RUSTC_VERSION >= 108800