Asahi Lina講述了使用Rust編寫Linux驅動程式的經驗

banq 發表於 2022-10-06
Linux

關於核心中的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塊。