Linuxカーネルへの Rust 導入の研究論文メモ
はじめに
北京郵電大学とUESTC研究者による論文「An Empirical Study of Rust-for-Linux」を読んだメモ。Linuxカーネルへの言語Rust導入(RFL)がどうなっているのか、実際のデータに基づいた分析が面白かった。
Rustとカーネルの出会い
Linuxカーネルは30年以上の歴史を持ち、現代コンピューティングの基盤だ。しかしC言語で書かれているため、メモリ関連やコンカレンシーのバグが絶えない。Rustはこの問題を所有権メカニズムで解決できるかもしれない。
所有権ルール | 説明 |
---|---|
排他的所有権 | メモリ位置は一度に1つの変数だけが所有できる |
所有権の移動・借用 | 所有権は他の変数に移動するか、参照を通じて一時的に借用できる |
スコープ外れの解放 | 所有者がスコープを外れると、変数はドロップされメモリが解放される |
RFLの歴史を辿るとこんな感じだ。
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
年 | 出来事 |
---|---|
2013 | 趣味プロジェクトとして「Hello from Rust!++」が初登場 |
2019 | 「Writing Linux Kernel Modules in Safe Rust」の提案 |
2020 | LPCでRFLがデビュー、最初のPRがコミットされる |
2021 | Linux MLに最初のRFC「[RFC] Rust support」が投稿 |
2022 | RFLがLinux v6.1に正式マージ |
2023 | 最初のRustドライバがLinux v6.8に正式マージ |
三層構造によるC言語との融合
RustとLinuxの融合はなかなか複雑な問題だ。カーネルはメモリを細かく制御したいが、Rustはそうした低レベル操作を制限する哲学を持っている。この相反する要求をどう解決するのか。
論文に載っていた図解によれば、RFLはこんな三層構造で設計されている。
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
層 | 役割 |
---|---|
bindings crate | rust-bindgenで生成されたカーネルデータ構造とインターフェースのFFIバインディング |
kernel crate | 開発者が安全でないFFIバインディングをラップした安全な抽象化レイヤー |
drivers crate | 安全なRFL抽象化を呼び出すドライバ実装 |
この構造において、rust-bindgenがC言語のヘッダーファイル(例えばcdev.h)を解析して、Rustバインディングを自動生成する。直接使うと安全でないため、開発者が安全な抽象化レイヤーで包み、ドライバ開発者に安全なAPIとして提供するわけだ。
例えば、bindings crateでは rust/bindings/bindings_generated.rs
に #[repr(C)] pub struct cdev
のようなバインディングが生成される。これをkernel crateで pub struct Reg<const N: usize>
のように安全な形に包んでいる。安全な抽象化レイヤーを構築するには様々な工夫が必要だ。
手法 | 説明 |
---|---|
Type/Derefコアーション | ・カーネルポインタの参照外し制限 ・型キャストの制御 |
ScopeGuard | ・スコープを出る際に自動的にリソース解放 |
ARef | ・参照カウントの自動管理 |
OOPパラダイム導入 | ・関連関数を構造体のメソッドとしてグループ化 |
トレイトによる関数ポインタ管理 | ・コールバック関数をトレイトとして実装 ・型境界による安全性確保 |
バイナリサイズの膨張問題
RustでLinuxドライバを書くと、コードサイズはどうなるのか。論文のデータを見てみよう。
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
ドライバ | サイズ比較 (Rust/C) | 理由 |
---|---|---|
binder | 1.2倍 | ・関数ポインタとunsafeキーワードを多用 ・安全性を犠牲にするがサイズオーバーヘッドは少ない |
gpio | 2.4倍 | ・Rustの安全機能が多くのコードを生成 ・ジェネリックプログラミングの使用 |
semaphore | 1.9倍 | ・ライフサイクル管理のためのコード増加 ・安全な抽象化レイヤーのオーバーヘッド |
デバッグ情報付きにすると状況はさらに悪化する。Rustドライバは3.9〜6.6倍も大きくなるのだ。ジェネリックプログラミングを多用するとシンボル数が増え、シンボル名も長くなるためらしい。組み込みデバイスではこれは深刻な問題になりうる。
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
パフォーマンスの光と影
実行性能はどうだろう。論文のFigure 8、9、10から興味深いデータを抽出してみた。
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
ドライバ | パフォーマンス比較 | 詳細 |
---|---|---|
e1000 | Rustが11倍遅い | ・C版の最適化(prefetchなど)をRust版が未実装 ・基本機能のみの実装段階 |
binder | ほぼ同等(10%の差) | ・関数ポインタ使用でパフォーマンス維持 ・安全性とパフォーマンスのバランスが取れている |
NVME, NULL block | 設定で大きく変動 | ・最大61%低下から67%改善まで幅広い結果 ・小さなジョブ数とブロックサイズで良好 ・小さな構造体がキャッシュラインに収まりやすい |
パフォーマンス低下の主な要因はこうだ。
要因 | 説明 |
---|---|
ロックの粒度 | ・Rustドライバではロックが粗い傾向 ・言語安全機能が並行プログラミングの負担を軽減しない |
実行時チェック | ・配列アクセスの境界チェックなどの追加コスト ・メモリ集約型ワークロードで顕著 |
ビットフィールドのエミュレーション | ・配列アクセスによるエミュレーションで遅延増加 ・実行時の境界チェックでさらに悪化 |
ポインタの多用 | ・キャッシュ/TLB/分岐予測ミス率の上昇 ・オブジェクトの所有権共有のために多用 |
安全性の幻想
「Rustなら安全」というのは単純すぎる考えだ。実際のドライバコードではどうなのか。
ドライバ | unsafe使用回数 | 内訳 |
---|---|---|
GPU | 114 | ・ドライバロジックの複雑さによる:107 ・安全抽象化の欠如による:7 |
NVME | 60 | ・ドライバロジックの複雑さによる:44 ・安全抽象化の欠如による:16 |
Binder | 54 | ・ドライバロジックの複雑さによる:45 ・安全抽象化の欠如による:9 |
E1000 | 6 | ・ドライバロジックの複雑さによる:4 ・安全抽象化の欠如による:2 |
Null block | 0 | ・完全に安全なRustで実装できた |
Gpio_pl061 | 3 | ・すべて安全抽象化の欠如による |
Semaphore | 4 | ・すべて安全抽象化の欠如による |
GPUのような複雑なハードウェアインターフェースではunsafeの使用が避けられない。「安全」と「使えない」の間にあるのが現実なのだろう。
実装上の苦労
Rustドライバを実装する際の主な課題も調べた。
段階 | 課題 |
---|---|
デバイスプローブ | ・所有権タイプの適切な注釈が必要 ・複雑なネスト型(例:Pin<Box<SpinLock<Box<Ring<RxDesc>>>>>) |
ドライバ機能実装 | ・動的サイズ配列の実装が複雑 ・カーネル実行コンテキストへの配慮が必要 |
デバイスクリーンアップ | ・Rustはdropトレイトでリソース解放を自動化 ・C言語のgoto文による集中的なリソース解放手順が不要 |
論文に載っていた動的サイズ配列の実装例も興味深い。
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
C言語なら数行で書けることが、Rustだと複雑になることもあるようだ。
開発者層の変化
RFLは開発者層にも変化をもたらしている。
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
- novice(0-24ヶ月)
- expert(24-120ヶ月)
- veteran(120+ヶ月)
開発者のラベルの定義は上記の通り。RFLは初心者を引き寄せているが、カーネルコード開発にはあまり参加していないようだ。
開発進捗も追ってみた。
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
時期 | 主な開発対象 | レビュー時間の変化 |
---|---|---|
2021年初期 | ・Kbuildシステム構築が中心(約60%) ・安全抽象化は約20% |
数時間〜数日 |
2022年前半 | ・安全抽象化の割合増加(約40-50%) ・Kbuildの割合減少(約40%) |
数日〜数週間 |
2023年 | ・安全抽象化が約60%を占める ・Kbuildは約15%に減少 |
280時間(平均) ・3年前の約200倍 |
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
RFLのインフラがある程度成熟し、安全抽象化レイヤー構築段階に移行している。しかしレビュープロセスの遅延が開発のボトルネックになっているようだ。
コード品質の向上
数字で見るとRFLは確かに品質を向上させている。
サブシステム | ドキュメント網羅率 | CIエラー/10K行 |
---|---|---|
RFL | 100% | 3.8 |
ebpf | 15% | 7.5 |
io_uring | 31% | 11.9 |
rustdocの統合によるドキュメントとコードの一体管理、そして#cfg(test)属性によるテストコードの容易な組み込みが要因のようだ。
今後の展望
論文の分析によると、RFLの取り組みはこれからどのサブシステムに集中すべきかも示唆している。
https://www.usenix.org/system/files/atc24-li-hongyu.pdf
優先度高 | 理由 |
---|---|
linux-block | ・KLOCあたりのバグ修正率が高い ・113のデータ競合バグと98の未解放ポインタバグ |
linux-ext4 | ・メモリ/スレッド安全関連のバグが多い ・VFS抽象化の提案が既に進行中 |
まとめ
RFLプロジェクトは、完璧な安全性を提供するわけではなく、パフォーマンスも一貫していない。「より安全にできる」環境を提供していると考えるのが適切だろう。
新規開発者参入の効果は注目に値するものの、実際のカーネルコード開発にまで至っていないのは課題だ。今後はより多くのドライバやサブシステムがRustで実装され、安全性とパフォーマンスのバランスが改善されていくことが期待されるのだろうか。
結局のところ、理想と現実の狭間で実用的な妥協点を探る旅がRFLなのかもしれない。
Linuxカーネルへの「Rust」導入をめぐる Torvalds の見解や
Asahi Linux プロジェクトリーダーの Marcan 氏が辞任したことを考えると、RFLの未来はまだまだ不透明だ。Rustの導入が本当にLinuxカーネルにとっての「救い」になるのか、今後の動向を見守りたい。
Discussion