LWN: Rust写的两个kernel module!
关注了就能看到更多这么棒的文章哦~
A pair of Rust kernel modules
By Jonathan Corbet
September 12, 2022
Kangrejos
DeepL assisted translation
https://lwn.net/Articles/907685/
能够用 Rust 语言编写内核代码的想法有一定的吸引力,但在没有实例可看的情况下,很难判断实际效果如何。这些例子,特别是超过 "hello world "复杂程度的模块,已经有点少了,但这正在开始改变。在西班牙奥维耶多举行的 2022 年 Kangrejos 会议上,两位开发者介绍了他们所开发的模块以及从这一实践中所获得的一些经验。
An NVMe driver
Andreas Hindborg 首先谈到了用 Rust 编写的 NVM Express 驱动。他说,开始这个项目,主要是为了利用 Rust 提供的内存安全保障(memory-safety guarantees),并获得一些关于该语言的实际经验。他从这个项目中得出的结论包括:Rust 有很多很不错的工具,它的类型系统(type system)对编写正确的代码很有帮助。他说,用 Rust 编写内核驱动比用 C 语言更容易。
既然内核已经有一个能良好地运行的 NVMe 驱动,为什么还要写一个?他说,现有的驱动没有问题,但是 NVMe 是一个很好的驱动抽象实验的目标。NVMe 本身相对简单,但它有很高的性能要求,并且被广泛部署,而现有的驱动程序提供了一个成熟的参考实现,可以用来进行对比。
Hindborg 对 NVMe 接口的内部情况进行了一些介绍;简而言之,NVMe 接口和计算机之间的通信要通过若干个 queue 队列来进行。通常情况下,如果接口可以处理的话,驱动程序会给系统中的每个 CPU core 都配置一个 I/O 队列。在 Rust 中创建数据结构来模拟这些队列是一个相对简单的任务。最后,当用 FIO 工具测试时,Rust 驱动程序的性能几乎与现有的 C 驱动程序一样好。Hindborg 说,区别在于 C 语言驱动已经被精心调整过了,而 Rust 驱动还没有;它最终应该能够达到相同的性能水平。
他最后说,Rust 的 NVMe 驱动仍然是 "一个游乐场" 而已,目前还不适合用在生产环境。为了推动下一步发展,他希望能创建更多的抽象,从而在驱动中删除剩余的不安全的代码组成部分。它还不支持设备移除,也不支持 nvme-cli 工具的 sysfs 开关。他还想研究一下使用 Rust 的 async 模型,这将 "简化驱动中的很多东西",但可能会牺牲一些性能。
在 Hindborg 演讲的最后,Paul McKenney 询问是否有任何关于 C 和 Rust 驱动之间的 bug 比例的对比信息。Hindborg 回答说,肯定会有一些 bug;围绕现有的 C 代码来建立 Rust 抽象是很难做到完全正确的。这项工作需要大量的仔细 review,但一旦它工作起来,建立在它上面的驱动程序往往就很少出现问题了。
A 9P filesystem server
去年,Linus Walleij 建议,与其用 Rust 编写驱动程序,开发者不如把目标放在那些更加容易被攻击的领域——比如说网络协议。Wedson Almeida Filho 采纳了这个建议,为 9P 文件系统协议编写了一个内核内的服务端程序,希望这个项目能够证明 Rust 能够提供的生产力提升以及安全优势。最初,他曾开始尝试替代 ksmbd 服务端功能,但结果发现这个选择不够理想。SMB 协议太复杂了,服务端需要一些必不可少的用户空间组件才能工作。他想要一些更简单的东西,而 9P 就符合这个要求。
他说,9P 文件协议来自 Plan 9 操作系统。内核有一个 9P 客户端的功能,但没有 9P 服务端。在 QEMU 中有一个 9P 服务端,可以用来将 host 文件系统 export 出来供 guest 使用。Almeida 说,该协议很简单,只定义了一组共十个操作。他的 9P 服务端的实现现在就可以以只读模式正常工作,只需要 1000 多行的代码。
Almeida 还在寻找一种方法来试验内核中的 async Rust。在这种异步模式中,编译器采用类似线程的代码,并把它变成一个状态机,可以用 "executors" 和 "reactors" 来实现,这些都在 kernel crate 中实现了。他创建了一个 executor,可以在内核工作队列中运行异步代码;在任何代码会 block 住的地方,就会释放工作队列的线程用来执行另一个任务。还有一个 socket reactor,在 socket-state 状态发生变化时被被调用;它将调用 Rust kernel crate 中的 Waker::wake(),使相应的 executor 再次运行起来。
当然,还有很多工作要做。他想为其他 I/O submission path 都实现 reactor,包括 KIOCBs(asynchronous I/O)、URBs(USB devices)和 BIOs(block devices)。内存分配部分还需要一些工作;如果 GFP_KERNEL 能够在等待内存管理子系统做一些非常复杂的事情不继续占用当前线程,那就更好了。
最后,编者询问是否已经实现了展示 Rust 的安全优势的目标;例如,是否对服务器进行了 fuzz 模糊测试?Almeida 回答说,基于 Rust 的 parsing 接口使得很多错误都不可能发生了。目前还没有进行过模糊测试,毕竟服务端程序只是几周前刚实现好,但他会进行一下测试。他最后说,他有兴趣看看他的服务端功能在这种测试中比起 QEMU 的实现来说表现如何。
[感谢 LWN 订户支持我参加这次活动。]
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~