開源項目:使用 Rust 寫一個兼容 Linux 的內核

大家好,我是螃蟹哥。

前些時間,Linus 表示不排斥 Linux 開發使用 Rust。今天爲大家帶來一篇介紹 Rust 寫 Linux 內核的文章。

注意,用 Rust 寫一個兼容 Linux 的內核,目的不是取代 Linux,而是爲了好玩、學習和練習。

近幾個月來,我一直在研究一個新的操作系統內核 Kerla[1],這是一個使用 Rust 從零開始實現的,希望在 ABI 級別兼容 Linux。換句話說,支持運行未經修改的 Linux 二進制文件

開源項目地址:https://github.com/nuta/kerla

我已經實現了基本功能:fork(2) and execve(2) ,文件操作, initramfs,TCP/UDP sockets, 信號, tty/pty, 管道, poll 等。

你可以通過 ssh 進入 Kerla,它運行在臨時創建的 Firecracker 微 VM 上,這是專門問你創建並啓動的。(你本地也可以通過以下命令進入)

$ ssh root@kerla-demo.seiya.me

Kerla

在這篇文章中,我將分享我在操作系統內核中使用 Rust 的第一印象。

01 Rust 在業務領域的成功

Rust 是一門深受用戶喜愛的 [2] 編程語言,使用它,你可以高效開發出可靠且性能好的軟件。

Rust 的所有權概念和類型系統可以讓你專注於修復邏輯錯誤,而不是痛苦的記憶 error 和 race。enum 強制你處理所有可能的 input/ouput 模式。此外,它的構建系統(cargo)和 IDE 支持(rust-analyzer[3])真的很棒。

生產環境慢慢有使用 Rust 的案例:AWS 的輕量級 VMM[4],npm registry 的部分模塊 [5] 以及許多其他公司 [6]。Rust 無疑是 C/C++ 之外一個很好的選擇(但並不一定是替代 C/C++)。

02 Rust 在內核領域

近年來,用 Rust 重寫現有軟件一直受到關注。

Bryan Cantrill 在《是時候用 Rust 重寫操作系統了嗎?》[7] 的演示中,建議採用混合方法:

因此,一種混合方法是,你保留現有的基於 C/assmbly 的內核,就像我們多年來一直採用的方式一致。然後,你允許開發基於 Rust 的東西、基於 Rust 的驅動程序、基於 Rust 的文件系統、基於 Rust 的內核軟件。

這就是在 Rust for Linux[8] 工作的人的情況。你可能聽說過最近 LKML 上建議的補丁 [9]。

我完全同意,重寫現有的,龐大的,功能豐富,健壯的操作系統內核不是一個好主意。然而,我想到了一個問題:從零開始,用 Rust 寫一個類 UNIX 的實用操作系統內核的利弊是什麼?它看起來像什麼?這就是我開始這個項目的原因。

爲了探索 Rust 在內核領域的長處和短處,我決定開發一個實用的、特定的,與 Linux 兼容的內核,可用作虛擬計算機上的 ["Faas"](https://en.wikipedia.org/wiki/Function_as_a_service ""Faas"") 運行環境。爲此,你只需要很少的設備驅動程序(例如 virtio[10]),我們就不必支持高級 Linux 功能(如 eBPF)。此外,與其他長期運行的工作負載相比,內核崩潰不會是一個關鍵問題。

03 內核領域使用 Rust 有好處嗎?

在我看來,有!

內核有一點特別:panic! 會導致系統崩潰,內存分配故障應處理而不是 panicking,隱藏控制流和隱藏分配通常不能接受。

Rust 的優勢也適用內核領域。我最喜歡的例子是無空指針處理問題:如果指針是無效的(即 Option<NonNull<T>>),你不能解引用它,直到你明確處理空(None)情況!此外,使用新類型習語 [11],你可以區分內核和用戶指針。

但是,我們在內核領域使用 Rust 時仍然存在一些問題:

分配失敗會 panic!

這個問題在 Linus Torvalds on the Linux kernel's Rust support patch[12] 中提到的。

我們來看看 Arc::new()[13] 的定義,一個創建線程安全參考計數指針的構造器:

pub fn new(data: T) -> Arc<T>

看起來超級直觀,對吧?然而,它有一個隱含的 panic 情況:內部緩衝分配失敗。

處理分配故障很無聊。當我開始一個 C 項目時,第一步是寫我自己的 xmalloc(3)[14],這樣我不需要檢查結果是否是 NULL。如果它耗盡了內存, 直接 crash 即可,沒什麼大不了的。我只需要生成一個新的有更多內存的 VM 或或在亞馬遜上購買新的內存。

然而,在內核空間中,kernel panic 是一件大事。我們應該設法從低內存狀態中恢復過來,以保持系統的工作狀態。沒人想看藍屏。

在我的項目中,爲了利用現有的便利 crates,我決定走目前的 Rust 路:允許因分配失敗而 panic。這就是說,在不久的將來,我確實認爲需要使用(或編寫我們自己的)動態分配容器的可故障版本。

更大的二進制大小

Resea[15],一個基於微內核的操作系統,使用 C 編寫的,僅僅 845KiB,包括用戶地應用程序, 如 TCP/IP 服務器,英特爾基於 hypervisor 的 VT-x 和 Linux ABI 模擬支持。

相比之下,Kerla 鏡像需要大約 1.1 MiB,不包括 initramf。雖然極簡的微內核和單片內核有着相反的理念,但在我看來,Rust 實現往往大於 C 實現。

雖然有一些大小優化方法 [16] 可用,但在極其受限的設備(例如 1 類設備 [17])中,這可能是一個問題。

make menuconfig 丟失

操作系統內核有許多參數。在 Linux 內核中,可以使用配置工具進行配置,如 make menuconfigmake xconfig。在 Rust 中,我們有特性標誌 [18],可啓用 / 禁用 crate 中的特性。但是,如果你想要更改硬編碼參數(如心跳間隔),該怎麼辦?你是否通過環境變量和 env![19] 宏來配置它?不, 它只接受一個字符串。

我們可能需要一個功能豐富的構建配置機制,就像 Cargo 中的 Kconfig[20] 一樣。

這些問題遲早會解決!

我要強調,這些問題並非源於語言設計。

Rust 正在不斷改善。關於分配失敗,人們已經開始着手處理它(見跟蹤問題 [21])。此外,你不必使用 liballoc,heapless crate[22] 將是一個很好的選擇。

04 Rust 在內核領域的優點

儘管有我上面提到的問題,但我覺得用 Rust 寫內核一定是富有成效的。Rust 用在內核領域我最喜歡的優點:

05 期待你的參與

這個內核還處於非常早期的階段。一些關鍵功能,如 futex、epoll、UNIX 域 socket 等尚未實現。換句話說,代碼仍然簡單易懂!如果你有興趣在 Rust 中編寫操作系統內核,歡迎參與。

— written by Seiya Nuta CC BY 4.0

原文鏈接:https://seiya.me/writing-linux-clone-in-rust

參考資料

[1] 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 重寫操作系統了嗎?》: 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] 一些大小優化方法: https://github.com/johnthagen/min-sized-rust

[17] 1 類設備: 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] 改進的內聯彙編語法: 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] 基於數組的 vector 和字符串實現: https://docs.rs/arrayvec/0.7.0/arrayvec/

[29] 多生產者和多消費者隊列: 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/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Ucnim1aziW1e5Wmy_8U3ww