关注了就能看到更多这么棒的文章哦~
Kernel time APIs for Rust
By Jonathan Corbet
March 2, 2023
DeepL assisted translation
https://lwn.net/Articles/924746/
虽然 6.3 内核已经增加了对 Rust 语言支持,但目前仍然只能用 Rust 来创建一个“hello world” module。当然,这个功能在 C 语言中早就可以做到了,而且安全性和 Rust 提供的差不多。但是,人们越来越希望合并一些实际有用的用 Rust 编写的 module;这就需要比目前已有的能更强大的基础设施。最近关于在 Rust 中如何处理 time 值的讨论就展示了这项工作所面临的挑战(和机遇)。
Asahi Lina 正在用 Rust 实现一个苹果硬件上的图形驱动程序,她发布了一些 Rust 基础设施的代码,包括一个用于 timekeeping 函数的 module。这个 module 只是一个最开始的样子,只有 25 行代码:
// SPDX-License-Identifier: GPL-2.0
//! Timekeeping functions.
//!
//! C header: [`include/linux/ktime.h`](../../../../include/linux/ktime.h)
//! C header: [`include/linux/timekeeping.h`](../../../../include/linux/timekeeping.h)
use crate::bindings;
use core::time::Duration;
/// Returns the kernel time elapsed since boot, excluding time spent
/// sleeping, as a [`Duration`].
pub fn ktime_get() -> Duration {
// SAFETY: Function has no side effects and no inputs.
Duration::from_nanos(unsafe { bindings::ktime_get() }.try_into().unwrap())
}
/// Returns the kernel time elapsed since boot, including time spent
/// sleeping, as a [`Duration`].
pub fn ktime_get_boottime() -> Duration {
Duration::from_nanos(
// SAFETY: Function has no side effects and no variable inputs.
unsafe { bindings::ktime_get_with_offset(bindings::tk_offsets_TK_OFFS_BOOT) }
.try_into()
.unwrap(),
)
}
这个模块将两个内核函数——ktime_get()和 ktime_get_boottime()——转换为返回 Rust Duration 类型的值的 Rust 实现版本。在 C 语言中,这两个函数都返回一个 ktime_t 类型,它是一个有符号的 64 位值,表示纳秒级别的时间。该时间的起点(0值)具体是对应于哪个真实日期和时间的,就取决于查询哪种时钟。例如,在 ktime_get_boottime() 的情况下,返回值表示系统启动后所经过的时间。
内核时间本质上是一个差值;是从某个初始 event 开始计算的纳秒数。这里提出的 Rust 实现也遵循了这一思路,因此使用了 Duration 类型。但 Thomas Gleixner 是负责内核中大部分 timekeeping 代码的,他质疑了这种方法。由于这两个函数都是希望用各自方式来表示绝对时刻的,他建议 Rust 函数应该返回绝对时刻的类型;他建议使用 Instant 或 SystemTime 。两者都表示绝对时刻;Instant 是单调递增的(也就是永远不会倒退)而 SystemTime 则不是。
不过这个方案在内核中不会有好结果,原因有两个。首先,正如 Boqun Feng 指出的,这两种类型是在 Rust 标准库(“std”)中定义的,而 Rust 标准库就像 C 标准库一样,在内核中是不可用的。所以,如果一定要用的话,这两种类型必须要为了内核使用场景来重新实现。但另一个问题是,内核支持多种时钟;可以在 clock_gettime() 的手 man 手册中找到相关列表。每种时钟都有其存在的理由,并且每种时钟的行为都有些不同。像 Instant 这样的类型被定义为使用其中一种时钟,但内核代码需要访问好几种。
Gleixner 并不真正关心使用的具体类型,但他呼吁确实需要对绝对时间(时间戳)和增量时间(间隔)要使用不同的类型。内核目前没有在其 time 的类型中做出这样的区分,但许多语言的库里都做了这样的区分。鉴于 Rust 是希望能够轻松编写更安全代码而引入内核的,所以利用 Rust 的类型系统来阻止开发者尝试将两个绝对时间值相加这种错误是有好处的。
事实上,正如 Lina 指出的那样,在 Rust 接口中的类型安全甚至应该再进一步。从另一个绝对时间减去一个绝对时间将得到一个增量时间——但这个增量时间只有在两个绝对时间都来自同一时钟时才有意义。所以类型系统应该阻止那些错误地混用了来自不同时钟的时间。
那么增量时间呢?Gleixner 最初建议增量时间可以独立于任何时钟;通过从另一个 CLOCK_BOOTTIME 值中减去一个 CLOCK_BOOTTIME 值得到的增量时间将与作为 CLOCK_TAI 值之差计算出来的增量是具有相同的类型的。Heghedus Razvan 同意了这个想法,并发布了一个示例实现;然后 Gary Guo 将这个想法改进成了一个"更 Rusty" 的实现。Miguel Ojeda 也建议增量时间可以是跟特定时钟相关联的。Gleixner 并不完全赞同需要这样做,但他认为这可能也是有价值的,特别是在处理 timer 时。内核 timer 也可以基于特定 clock,所以确保与这些 timer 一起使用的任何增量时间都是用相同 clock 生成的,可能是有好处的。
Feng 建议使用 Duration 来表示所有增量值,并使用一个跟 clock 类型相关的、类似于 Instant 的东西来表示绝对时间。Lina 赞同这个做法,并似乎准备按照 Razvan 发布的例子这个方向继续推进。新的 patch 可能很快会出现。
我们很可能会看到这类对话反复发生,随着更多 Rust 基础设施向 mainline 靠近。复制现有内核 API 到 Rust 是完全可能的,并且这样做会使得生成的 Rust 代码看起来对当前内核开发者来说更加熟悉。但是这种方式也会导致损失一些原本希望 Rust 能带来的一些好处。把这些工作做细做好,就能更好地展示 Rust 如何能带来比当前的 kernel 更大的好处、更安全的 API。因此有许多 API 都会需要从头做设计。这个过程可以利用好 kernel 社区过去多年的经验,但也会把 kernel 中的许多惯例破坏掉。看起来这个项目还是会让人们忙一段时间的。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~