Asahi Lina讲述了使用Rust编写Linux驱动程序的经验

22-10-06 banq

关于内核中的Rust是否有用,有很多奇怪的争论......根据我的经验,它比我想象的要有用得多!"。
在我的驱动程序上只调试了大约两天,我就从第一次渲染变成了一个可以运行游戏、浏览器等的稳定桌面。

所有的并发性错误都在Rust中消失了!内存在需要释放的时候就会被释放!
一旦你学会让Rust和你一起工作,我觉得它就会引导你写出正确的代码,甚至超越语言的安全承诺。它真的很有魔力!

如果我用C语言写这篇文章,我绝对不可能不遇到锁竞争竞态条件、UAF、内存泄漏和各种各样的坏问题。
在Rust中只是一些逻辑错误和一些核心内存管理问题。
一旦这些被修复,驱动程序其余部分就会工作!!

我试了一下kmscube,它顺利地渲染帧。然后我试着启动一个KDE会话,过了一会儿它就崩溃了,而你知道什么原因没有导致它?3个进程试图同时使用GPU,并行分配和提交命令,是并行!!

在这样一个复杂的驱动中,当事情在单线程中运行后,所有的锁定和线程都神奇地按计划运行,没有奇怪的锁竞争或事情互相踩踏,就我而言,对于一个如此复杂的驱动来说,这是完全闻所未闻的。

然后所有的内存管理……就像魔术一样发生。使用 GPU 的进程退出,它使用的所有内存和结构都被释放。我的日志中有几十行都被正确释放了。我没有写任何胶水,Rust 为我做了这一切!
(我写了将 DRM 子系统连接到 Rust 的部分,包括在文件关闭时删除一个File结构,这就是触发所有内存管理的原因......剩下的部分由Rust完成!)

我实际上花了更多的时间来追踪一个被遗忘*的 DCP 驱动程序(由Alyssa和Janne用C语言编写,已经过测试),它导致堆溢出,在上面花费的时间比我花在Rust中追踪我的全新驱动的CPU端安全问题(unsafe 代码)的时间还要多。

甚至像正确处理ERESTARTSYS这样的事情。Linux Rust鼓励你在任何地方使用Result<T>(Err 是 errno 的内核变体),然后你只需在你睡眠/等待sleeping/waiting条件的地方后面加一个?(就像编译器告诉你的那样),它就能正常工作了。

说真的,C和Rust在这里有很大的区别。
Rust的炒作是真实的!无畏的并发性是真实的!
虽然有一些unsafe {} 块并不能掩盖否定Rust的优势!

Asahi Lina补充:
从真正的驱动程序的第一次渲染到稳定的驱动程序,需要两天时间。当我拿到第一张渲染图的时候,几乎所有的驱动程序都已经写好了!"。

我真正想说的是,我花了两天时间来修复错误,使事情能够正常和稳定地进行,而如果驱动是用C语言编写的,我还必须处理并发和内存安全问题,那就会花更多的时间。
请记住,这是内核方面的驱动程序,它的工作只是管理内存和向GPU提交请求。
要达到这个目的有很多复杂的因素,但是一旦你达到了这个目的,从内核的角度来看,不同的应用在使用GPU的方式上几乎没有任何差异。所有OpenGL的东西都是在用户空间处理的,Alyssa在这方面已经工作了很长时间(在macOS下测试)。因此,最终,对于这种驱动,在你获得了渲染和(相对较少的)功能/用例支持之后,剩下的大部分工作就是使其稳定并修复安全和并发错误。而......在Rust中并没有多少这样的东西!"。

以下是这两天的大致情况:

  • 移除mesa中的一些旧的黑客,这些黑客会破坏事情。
  • 修复mesa中的kmsro/buffer导入问题
  • 实现PRIME缓冲区共享工作所缺少的东西
  • 重写我的DRM抽象,因为我误解了C语言的API,导致它不能和PRIME一起工作。
  • 重构了一些驱动代码,因为它也破坏了驱动内部的PRIME缓冲区共享,这也是因为我误解了它应该如何工作。
  • 弄清楚如何修复固件端的TLB无效性。
  • 为GPU端TLB不能正常失效而导致的内存损坏问题伤透脑筋,然后想出了一个愚蠢的解决方法,即每次渲染后等待GPU关闭。

因此,实际上,大部分工作是使缓冲区共享工作,这对于让多个图形应用程序相互发送帧是必要的,然后与TLB失效问题作斗争(这在硬件层面上导致内存安全问题,所以这不是Rust可以帮助解决的问题--这场战斗仍在进行,我现在有的只是一个解决它的黑客)。

在最后一个解决方法之后,一切都开始稳定地工作,所以事实证明,所有明显的剩余内存安全问题的根源实际上是GPU TLB,而驱动本身实际上是非常可靠的。这就是令人惊奇的部分!

Reddit网友:
1、我是一名长期的 C 开发人员,C++ 开发人员已经有几年了,现在已经使用 Rust 作为我的主要语言 3 年了。
我发现 Rust 是最容易阅读的语言。跳入 Rust 核心或其他项目的某些部分并快速开始理解代码流是多么容易,这让我感到非常惊讶。

2、阅读 Rust 代码时,这种语言使得隐藏棘手的错误变得非常困难(显然并非不可能,但惯用代码通常非常简单)。
阅读 C 语言时,您需要时刻注意一切,因为该语言不允许抽象。您需要了解指针可能变为无效的每个位置,您需要手动跟踪资源所有权,以确保它们在正确的时间释放,您需要了解给定函数可能调用未定义行为的每种情况,以及有时,规则可能会根据您所针对的平台或正在使用的编译器版本而改变。内核代码也不是严格的标准 C,而是使用 GCC 扩展,这是您需要注意的更多内容。
C++ 允许抽象,但也伴随着糟糕的设计决策、不能很好地协同工作的特性以及许多未定义行为的实例。
Rust 只是一种较新的语言,其高级功能从一开始就被设计为难以滥用,并且安全性是一等公民。

3、Rust 是迄今为止最容易审查的代码,甚至比 Python 更容易。您可以非常准确地了解某些代码的运行时行为,而无需调查可能采用的路径的实现。在哪里存在潜在问题,您总是知道它们可能源自哪里,因为它们需要显式的函数调用或 unsafe块。



 

1