关注了就能看到更多这么棒的文章哦~
Rust for Linux redux
By Jake Edge
July 7, 2021
DeepL assisted translation
https://lwn.net/Articles/862018/
7 月 4 日,Rust for Linux 项目又提供了一版 patch set,仍是为内核加入了 Rust 语言的支持。看来,该项目认为它已经准备好可以合入 mainline 了。但其实还有一个更大的问题悬而未决:内核开发社区是否已经准备好接受 Rust?目前看来仍然不明朗。
Round 2
Miguel Ojeda 已经在全职(been hired)从事该项目,他发布了这组 patch set。在封面邮件中,他列出了自 4 月发布 RFC patch set 以来的一些改动。尤其是替换了在 allocation 分配失败时调用 panic!()的做法。专门为 kernel project 修改了一版 alloc crate,不过计划仍然是让这些改动能被合入 upstream,这样就不需要带着这些修改了。顺便说一下,对这个 crate 进行修改的 patch 对于 lore 存档来说显然超出了 size 限制,不过它还是出现在了 LWN 的存档中。
内核设施的 Rust 抽象工作有了更多进展,包括实现了 "红黑树 red-black tress、reference-counted objects 引用计数对象、file descriptor creation 文件描述符创建、tasks 任务、files 文件、io vectors 向量等等……",他还说增加了对驱动的支持。除此之外,Android Binder 进程间通信机制的 Rust 驱动也有更多的功能,而且目前正在为一些 Raspberry Pi 的若干型号的硬件随机数发生器开发驱动程序。
但是,在 4 月份的时候人们就说它仍然缺乏一个 "真正的" 驱动程序,这次这一点又被提出来了。克里斯托夫-赫尔维格(Christoph Hellwig)说,如果这次发表的时候仍是故意不加这一部分的话,那么他强烈反对合并这组代码。在申请邮件中,Ojeda 说该 patch set 已经添加到了 linux-next 中,正在申请合并。赫尔维格希望看到 Rust 在被合入之前能先证明自己的能力:
[……]首先证明它确实有用。这里说的有用,是指实现一个真实的驱动,比如 nvme 或 usb host controller 的驱动程序,证明其可以正常工作并展示出比现有驱动更多的优势。在它能展示这种程度的用处之前,它对大家只是个拖累。
Ojeda 指出 Binder 驱动并不是一个 "微不足道的小模块",并且这个模块 "已经能正常工作了",但他承认真正的硬件驱动确实是一个重要的部分(但目前还没有)。但申请邮件中列出了一些已经表示对该项目感兴趣或参与其中的组织,Ojeda 认为这也有助于证明该项目的价值:
它的价值的一个 "证明"在于,我们已经开始获得了用户在等待使用它,而且还有其他一些感兴趣的组织都表示了支持。
然而,其他一些人则比较保守,他们只会在代码进入 mainline 之后才会开始在这上面投入精力,也就是说,他们会等到大家先决定是否会在 kernel 里面支持 Rust。
Greg Kroah-Hartman 说,由于一些原因,Binder 的 "驱动程序" 并不能算作一个真正的好例子。首先,这个驱动目前还缺少一个相当大的功能(binderfs),并且它也没有展示出 Rust 如何跟内核中其他部分来配合。和以前一样,他强烈建议先开展一些工作来帮助澄清内核开发者对 Rust 的一些疑问:
并不是说它没有用处,但是 binder 和内核其他部分之间的互动确实是非常少的,而且只局限在具体几处。几乎没有人会再对这个功能进行进一步开发。
请在一个真正的驱动上工作,来帮助证明这一切是否能够正常工作。当你做这样的事情时,会遇到很难未解决的问题,需要把这些问题解决了先。
Kernel Summit topic
希望 Rust 能证明自己的这个话题也出现在 ksummit-discuss 邮件列表中。6月底,Ojeda 提议将 Rust for Linux 作为今年 Linux Plumbers 大会上 Kernel Summit 部分(track)的一个技术主题。7月 6 日,Linus Walleij 回复说,同意这是一个应该讨论的话题。他指出,内核开发者已经要掌握相当多的语言了(例如 C、汇编、Make、Bash、Perl、Python……),所以他心中存在一个问题,就是 Rust 会带来些什么,使得它能够值得被加入进来作为有一种新的语言支持起来。
这些邮件就开始了讨论 Rust 如何能表现自己的价值——以及将 Rust 加入内核之后下一步会是怎样。内核开发者显然不愿意先要了解 Rust 之后才能继续 Linux kernel 的开发工作。Ojeda 尝试着在一定程度上打消这些顾虑,但也承认大多数内核开发者最终会需要了解这种语言。Ojeda 说,就目前而言,项目的目的是与那些希望在其子系统中添加 Rust API 的 maintainer 来合作完成这些工作。这将能在 kernel 中实现 Rust 的 boostrap(自引导启动),并将有助于回答 Leon Romanovsky、James Bottomley 等人所表达的一些担忧。
Romanovsky 担心的是重构(refactoring)问题,特别是牵涉到要在一个拥有不同语言实现的多个驱动程序的系统中同时重构多个子系统的时候。Bottomley 猜想因为 Rust 代码的 review 审查人员会更少,所以可能更容易会有代码错误被合入 mainline 中 Rust 的部分:
因为现在我们大多数的 CVE 类型的问题通常是编程代码错误,那么缺乏 review 就可能会导致编程错误这种问题增加,而更安全的内存模型也并不能阻止这些问题的出现。
这些都是近期需要处理掉的问题,但最终目标是要让 Rust 能内核开发者中更加普及。正如 Ojeda 所说:
毕竟,如果我们要让 Rust 成为内核中的第二语言,那么我们应该在合理的时间之内就让尽量多的人加入进来,至少要是熟悉到某种程度。
正如 Laurent Pinchart 所指出的,这是一个重要的观点,需要在讨论中更清楚地强调出来:
[……]在内核中采用 Rust 作为第二语言,不仅仅是一个影响不大的技术决策,同时它也是一个流程上的决策,它将会导致大多数内核开发者需要面临一个要学习 Rust 的新要求。这是否应该发生、将会何时发生,这是是我们正在争论的问题。无论结果如何,重要的是要能正确地把这个问题讲出来,并对其带来的影响能有一个完整的分析。
当然,也还有很多其他技术障碍需要解决。其中一类问题是 Linux 驱动模型和与之相关的 object-lifetime handling(对象生命周期的处理)。Roland Dreier 建议,像 devres(用来管理 device resource 的)这样的接口可以避免他在驱动程序中之前看到的很多关于生命周期的资源管理问题,特别是异常情况的处理方面,但它并没有被广泛采用。Walleij 不同意这个它没有被广泛采用的判断,但完全不知道切换到 Rust 是否更好:
我认为这是一个艰难的但是成功的做法,人们只是需要更好地学习如何使用它。
但是,如果为了能使用一种更加简单的机制,但却需要付出对 kernel 进行重新洗牌的代价也就是把驱动程序都换成 Rust,我也不确定,这样好吗?
对于 Kroah-Hartman 来说,看到 Rust 写出来的真正的驱动程序将有助于理清这些问题如何在 Rust 语言中得到解决。这里还有一些困难的问题需要解决:
这将是 Rust 工作中比较 "有趣" 的部分,它必须先弄清楚如何将我们目前在驱动模型中的引用计数管理的对象来转换成 Rust 控制的对象,并保持一切都能正常工作。
对于通常代码来说,事实上内存是被分配给了一个特定的对象(the struct device),但却从另一个对象(cdev)中被引用了。devm_* 方式的用户似乎没有意识到这里有两个完全独立的 object lifecycle,因为两者的交互经常很不显眼。
我期待着看到 rust 的实现会如何处理所有这些问题,因为我搞不懂这一点。
还有一个问题是,所有这些工作最后会带来什么。Walleij 想知道,从展示语言的好处的角度来看,内核的 "leaf nodes"(即指设备驱动程序)是否真的是最好的上手点。他在 4 月份的长篇留言中也问到了这个问题。他的问题的一部分是,是不是出于其他原因才决定从驱动程序这部分来开始:
如果先做设备驱动的整个理由是出于一些战略原因,而并不是一定要给设备驱动子系统带来一些好处,而是拿它作为一个试验场和小白鼠,那么这个策略就需要明确出来,并让大家得以理解这一点。因此,也就是在我们明白 Rust 作为所有这些 $UPSIDES (优势),大家首先用它来做设备驱动开发,这是纯粹一个战略策略,对吗?我认为这里还需要指出如果未能成功的话该有哪些后备手段?
他还问到用 Rust 编写整个子系统的问题。这需要将 Rust 的 API 暴露给内核中其他地方的 C 代码来使用。这也有可能会导致内核向着使用更多 Rust 代码进行演变:
如果我们想在 Rust 中真正地写一个子系统,那么它当然会走另一条路:就是让 Rust 暴露 API 给 C 来使用。那么我看来最终的宏大目标就是让 Rust 完全实现 Linux,这是一个逐步蚕食的过程。如果它被证明比 C 语言更好,那么就是会向这个方向发展。
Ojeda 说,虽然可以将 Rust 的 API 暴露给系统的 C 部分来使用,但他并不推荐这样做。这样做的问题是,C语言里的调用者会无法享受到 Rust 带来的很多好处:
一般来说,我会避免将 Rust 子系统暴露给 C 语言。
当然,这是可以做的,它可以在 Rust 写成的子系统中享受到 Rust 的优势。然而,如果你不得不暴露出一个 C 语言的 API,那么就会失去更丰富的 type system 的大部分优势,也会失去 Rust 为使用到这个子系统的调用者本来能带来的安全保证。
类似地,Johannes Berg 问到如何用 Rust 替换子系统的一部分、但驱动程序仍然是用 C 语言来写成,这实际上是现有计划的相反做法。Ojeda 再次表示,这是可以做到的,要注意会失去 Rust 的特性和保证。此外,有些架构目前还没有 Rust 的编译器,所以现在看基于 Rust 的子系统可能还没有达到成熟实际。
Future
这个项目的最终目标并不完全清楚。如果所有的 Linux 驱动都是用 Rust 编写的,那么仍然会有很多重要代码在运行不安全的 C 语言;如果 Rust 在这期间证明了自己,那么下一步是否要取代这些其他部分?但在这种情况下,"证明自己" 这个要求其实也没有真正定义清楚。
学习一种新的语言,包括其所有特有行为、怪癖和特异性,对于已经在维护现有内核代码方面处理了相当多的复杂性的开发人员来说,会是一个相当重大的要求。更不用说在现有基础上提供新功能所带来的新增的复杂性了。在这两个邮件讨论中(以及其他一些地方)的评论中,那些相当普遍的怀疑态度很可能就来源于这个学习负担上。
将 Rust 添加到内核中需要大量的工作,要想能完全实现 Rust 带来的那些好处,就必须要使用 Rust 重写整个 Linux kernel,这样一来对于许多人来说就没有什么明确的好处了。运气好的话,这个项目可以在早期提供一些明确的 "优势",能清楚地证明这种语言的潜力,以及能够在像内核这样的大型复杂代码库中逐步应用起来。如果没有这些,那么 Rust in kernel 项目可能很难在打入 Linux kernel 这个目标上取得大的进展。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~