開源項目:使用 Rust 寫一個兼容 Linux 的內(nèi)核
前些時間,Linus 表示不排斥 Linux 開發(fā)使用 Rust。今天為大家?guī)硪黄榻B Rust 寫 Linux 內(nèi)核的文章。
注意,用 Rust 寫一個兼容 Linux 的內(nèi)核,目的不是取代 Linux,而是為了好玩、學(xué)習和練習。
近幾個月來,我一直在研究一個新的操作系統(tǒng)內(nèi)核 Kerla[1],這是一個使用 Rust 從零開始實現(xiàn)的,希望在 ABI 級別兼容 Linux。換句話說,支持運行未經(jīng)修改的 Linux 二進制文件!
開源項目地址:https://github.com/nuta/kerla
我已經(jīng)實現(xiàn)了基本功能:fork(2) and execve(2) ,文件操作, initramfs,TCP/UDP sockets, 信號, tty/pty, 管道, poll 等。
你可以通過 ssh 進入 Kerla,它運行在臨時創(chuàng)建的 Firecracker 微 VM 上,這是專門問你創(chuàng)建并啟動的。(你本地也可以通過以下命令進入)
[email protected]

在這篇文章中,我將分享我在操作系統(tǒng)內(nèi)核中使用 Rust 的第一印象。
01 Rust 在業(yè)務(wù)領(lǐng)域的成功
Rust 是一門深受用戶喜愛的[2]編程語言,使用它,你可以高效開發(fā)出可靠且性能好的軟件。
Rust 的所有權(quán)概念和類型系統(tǒng)可以讓你專注于修復(fù)邏輯錯誤,而不是痛苦的記憶 error 和 race。enum 強制你處理所有可能的 input/ouput 模式。此外,它的構(gòu)建系統(tǒng)(cargo)和 IDE 支持(rust-analyzer[3])真的很棒。
生產(chǎn)環(huán)境慢慢有使用 Rust 的案例:AWS 的輕量級 VMM[4],npm registry 的部分模塊[5]以及許多其他公司[6]。Rust 無疑是 C/C++ 之外一個很好的選擇(但并不一定是替代 C/C++)。
02 Rust 在內(nèi)核領(lǐng)域
近年來,用 Rust 重寫現(xiàn)有軟件一直受到關(guān)注。
Bryan Cantrill 在《是時候用 Rust 重寫操作系統(tǒng)了嗎?》[7] 的演示中,建議采用混合方法:
因此,一種混合方法是,你保留現(xiàn)有的基于 C/assmbly 的內(nèi)核,就像我們多年來一直采用的方式一致。然后,你允許開發(fā)基于 Rust 的東西、基于 Rust 的驅(qū)動程序、基于 Rust 的文件系統(tǒng)、基于 Rust 的內(nèi)核軟件。
這就是在 Rust for Linux[8] 工作的人的情況。你可能聽說過最近 LKML 上建議的補丁[9]。
我完全同意,重寫現(xiàn)有的,龐大的,功能豐富,健壯的操作系統(tǒng)內(nèi)核不是一個好主意。然而,我想到了一個問題:從零開始,用 Rust 寫一個類 UNIX 的實用操作系統(tǒng)內(nèi)核的利弊是什么?它看起來像什么?這就是我開始這個項目的原因。
為了探索 Rust 在內(nèi)核領(lǐng)域的長處和短處,我決定開發(fā)一個實用的、特定的,與 Linux 兼容的內(nèi)核,可用作虛擬計算機上的[" Faas"](https://en.wikipedia.org/wiki/Function_as_a_service "" Faas"")運行環(huán)境。為此,你只需要很少的設(shè)備驅(qū)動程序(例如 virtio[10]),我們就不必支持高級 Linux 功能(如 eBPF)。此外,與其他長期運行的工作負載相比,內(nèi)核崩潰不會是一個關(guān)鍵問題。
03 內(nèi)核領(lǐng)域使用 Rust 有好處嗎?
在我看來,有!
內(nèi)核有一點特別:panic! 會導(dǎo)致系統(tǒng)崩潰,內(nèi)存分配故障應(yīng)處理而不是 panicking,隱藏控制流和隱藏分配通常不能接受。
Rust 的優(yōu)勢也適用內(nèi)核領(lǐng)域。我最喜歡的例子是無空指針處理問題:如果指針是無效的(即 Option),你不能解引用它,直到你明確處理空(None)情況!此外,使用新類型習語[11],你可以區(qū)分內(nèi)核和用戶指針。
但是,我們在內(nèi)核領(lǐng)域使用 Rust 時仍然存在一些問題:
分配失敗會 panic!
這個問題在 Linus Torvalds on the Linux kernel's Rust support patch[12] 中提到的。
我們來看看 Arc::new()[13] 的定義,一個創(chuàng)建線程安全參考計數(shù)指針的構(gòu)造器:
pub?fn?new(data:?T)?->?Arc
看起來超級直觀,對吧?然而,它有一個隱含的 panic 情況:內(nèi)部緩沖分配失敗。
處理分配故障很無聊。當我開始一個 C 項目時,第一步是寫我自己的 xmalloc(3)[14],這樣我不需要檢查結(jié)果是否是 NULL。如果它耗盡了內(nèi)存, 直接 crash 即可,沒什么大不了的。我只需要生成一個新的有更多內(nèi)存的 VM 或或在亞馬遜上購買新的內(nèi)存。
然而,在內(nèi)核空間中,kernel panic 是一件大事。我們應(yīng)該設(shè)法從低內(nèi)存狀態(tài)中恢復(fù)過來,以保持系統(tǒng)的工作狀態(tài)。沒人想看藍屏。
在我的項目中,為了利用現(xiàn)有的便利 crates,我決定走目前的 Rust 路:允許因分配失敗而 panic。這就是說,在不久的將來,我確實認為需要使用(或編寫我們自己的)動態(tài)分配容器的可故障版本。
更大的二進制大小
Resea[15],一個基于微內(nèi)核的操作系統(tǒng),使用 C 編寫的,僅僅 845KiB,包括用戶地應(yīng)用程序, 如 TCP/IP 服務(wù)器,英特爾基于 hypervisor 的 VT-x 和 Linux ABI 模擬支持。
相比之下,Kerla 鏡像需要大約 1.1 MiB,不包括 initramf。雖然極簡的微內(nèi)核和單片內(nèi)核有著相反的理念,但在我看來,Rust 實現(xiàn)往往大于 C 實現(xiàn)。
雖然有一些大小優(yōu)化方法[16]可用,但在極其受限的設(shè)備(例如 1 類設(shè)備[17])中,這可能是一個問題。
make menuconfig 丟失
操作系統(tǒng)內(nèi)核有許多參數(shù)。在 Linux 內(nèi)核中,可以使用配置工具進行配置,如 make menuconfig 和 make xconfig。在 Rust 中,我們有特性標志[18],可啟用/禁用 crate 中的特性。但是,如果你想要更改硬編碼參數(shù)(如心跳間隔),該怎么辦?你是否通過環(huán)境變量和 env![19] 宏來配置它?不, 它只接受一個字符串。
我們可能需要一個功能豐富的構(gòu)建配置機制,就像 Cargo 中的 Kconfig[20] 一樣。
這些問題遲早會解決!
我要強調(diào),這些問題并非源于語言設(shè)計。
Rust 正在不斷改善。關(guān)于分配失敗,人們已經(jīng)開始著手處理它(見跟蹤問題[21])。此外,你不必使用 liballoc,heapless crate[22] 將是一個很好的選擇。
04 Rust 在內(nèi)核領(lǐng)域的優(yōu)點
盡管有我上面提到的問題,但我覺得用 Rust 寫內(nèi)核一定是富有成效的。Rust 用在內(nèi)核領(lǐng)域我最喜歡的優(yōu)點:
Rust 讓我有信心:其類型系統(tǒng)和所有權(quán)、生命周期概念使我意識到,我的實現(xiàn)編譯不通過,因為它違反了共享的 XOR 可變規(guī)則,例如,一旦編譯通過,能正常運行就一點都不奇怪(如沒有討厭的數(shù)據(jù)競爭和危險的空指針)。 強制處理所有的 input 模式: enum和模式匹配 (match) 讓你可以處理所有可能的情況,不會有漏掉的情況。它具備寫內(nèi)核的特性:packed struct[23],raw pointers[24],改進的內(nèi)聯(lián)匯編語法[25],embedding assembly files[26] 等等。 便利的 no_std(獨立) crates 可用:bitflags 操作庫[27],基于數(shù)組的 vector 和字符串實現(xiàn)[28],多生產(chǎn)者和多消費者隊列[29] 等等。內(nèi)置單元測試:在 Rust 中編寫和運行單元測試非常簡單。此外,由于 custom_test_frameworks[30] 功能,你可以在 QEMU 或真實機器上運行單元測試。 開發(fā)者友好的超棒工具鏈:linter[31] 幫助你編寫友好的 Rust 代碼,交叉編譯[32] 很容易,而 rust-analyzer[33] 可以把你喜歡的編輯器變成類似 IntelliJ IDEA 這樣的 Rust IDE。
05 期待你的參與
這個內(nèi)核還處于非常早期的階段。一些關(guān)鍵功能,如 futex、epoll、UNIX 域 socket 等尚未實現(xiàn)。換句話說,代碼仍然簡單易懂!如果你有興趣在 Rust 中編寫操作系統(tǒng)內(nèi)核,歡迎參與。
— written by Seiya Nuta CC BY 4.0
原文鏈接:https://seiya.me/writing-linux-clone-in-rust
參考資料
Kerla: https://github.com/nuta/kerla
[2]深受用戶喜愛的: https://insights.stackoverflow.com/survey/2020#technology-most-loved-dreaded-and-wanted-languages
[3]rust-analyzer: https://rust-analyzer.github.io/
[4]AWS 的輕量級 VMM: https://aws.amazon.com/blogs/opensource/why-aws-loves-rust-and-how-wed-like-to-help/
[5]npm registry 的部分模塊: https://www.rust-lang.org/static/pdfs/Rust-npm-Whitepaper.pdf
[6]許多其他公司: https://www.rust-lang.org/production/users
[7]《是時候用 Rust 重寫操作系統(tǒng)了嗎?》: https://www.infoq.com/presentations/os-rust/
[8]Rust for Linux: https://github.com/Rust-for-Linux/linux
[9]補丁: https://lkml.org/lkml/2021/4/14/1023
[10]virtio: https://wiki.osdev.org/Virtio
[11]新類型習語: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
[12]Linus Torvalds on the Linux kernel's Rust support patch: https://lkml.org/lkml/2021/4/14/1099
[13]Arc::new(): https://doc.rust-lang.org/alloc/sync/struct.Arc.html#method.new
[14]xmalloc(3): https://www.freebsd.org/cgi/man.cgi?query=xmalloc&apropos=0
[15]Resea: https://resea.org/
[16]一些大小優(yōu)化方法: https://github.com/johnthagen/min-sized-rust
[17]1 類設(shè)備: https://tools.ietf.org/html/rfc7228
[18]特性標志: https://doc.rust-lang.org/cargo/reference/features.html
[19]env!: https://doc.rust-lang.org/std/macro.env.html
[20]Kconfig: https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html
[21]跟蹤問題: https://github.com/rust-lang/rust/issues/32838
[22]heapless crate: https://docs.rs/heapless/
[23]packed struct: https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked
[24]raw pointers: https://doc.rust-lang.org/std/primitive.pointer.html
[25]改進的內(nèi)聯(lián)匯編語法: https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html
[26]embedding assembly files: https://doc.rust-lang.org/unstable-book/library-features/global-asm.html
[27]bitflags 操作庫: https://docs.rs/bitflags/1.2.1/bitflags/
[28]基于數(shù)組的 vector 和字符串實現(xiàn): https://docs.rs/arrayvec/0.7.0/arrayvec/
[29]多生產(chǎn)者和多消費者隊列: https://docs.rs/crossbeam/0.8.0/crossbeam/queue/struct.ArrayQueue.html
[30]custom_test_frameworks: https://rust-lang.github.io/rfcs/2318-custom-test-frameworks.html
[31]linter: https://github.com/rust-lang/rust-clippy
[32]交叉編譯: https://rust-lang.github.io/rustup/cross-compilation.html
[33]rust-analyzer: https://rust-analyzer.github.io/
我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標準庫》等。
堅持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio
