Rust In Linux Kernel
Linux Kernelに導入されそうなRustを読む
Linux KernelにRustを取り入れようという動きは大分前から観測されていたが,遂にこのcommitで大きく動いた.
まだmasterにはマージされていないものの,このcommitではRustによるkernelの開発を可能にしている.
docmentation
コミットの多くはドキュメントで,サポートの詳細が書かれている.
とりあえずそれらを抜粋,要約しておく.
doc-guide/kernel-doc
kernel-doc
はRustで書かれた部分のドキュメンテーションを行わない.
しかし,その代わりにrust_docsでドキュメンテーションが行われる.
kbuild/kbuild
新たにRustコンパイラへのオプション用のKRUSTCFLAGS
が追加された.
process/changes
rustc(nightly)
, bindgen(0.56.0)
, rustdoc(nightly)
が追加された.
この内,rustdoc
はドキュメンテーションに使用される.
rust/arch-support
rustcはLLVMに依存している為,ターゲットに出来るアーキテクチャが限られている.
現在はarm64
, x86_64
のみをサポートしている.
rust/coding
Rustで書かれたカーネル内のコードはrustfmt
によって自動でフォーマットされる.(嬉しいね.マジで)
ただし,コメントどドキュメントに関してはその範囲外である.
フォーマッタの設定はデフォルトのものを用いる.例えば,インデントにはタブではなく4つのspaceを用いる.
clippy
を使う事も出来る.
現時点で全てのカーネルAPIに対して抽象化が提供されている訳では無いが,今後追加されるだろうとの事.
正当な理由がない限り,Rustでコードを書くときにはCバインディングを直接扱わないようにすべきである.
まだRust向けに抽象化されていないAPIを扱うコードを記述する際は,Rust向けに抽象化する事を検討すると良い.
Rustのコード内でkernel configに基づいたコードを書く時は
#[cfg(CONFIG_X)] // `CONFIG_X` is enabled (`y` or `m`)
#[cfg(CONFIG_X="y")] // `CONFIG_X` is enabled as a built-in (`y`)
#[cfg(CONFIG_X="m")] // `CONFIG_X` is enabled as a module (`m`)
#[cfg(not(CONFIG_X))] // `CONFIG_X` is disabled
のようにする事ができる.
rust/docs
ここはrustdoc
の良さを語ってる.
良いよね.
markdownだからテキストベースでも読みやすいし,ドキュメント生成も簡単だよって話をしてる.
rust/quic-start
ビルド,開発に必要なツール群とそのインストール方法が書かれている.この記事では列挙に留め,インストール方法については触れない
ビルド要件
- rustc
- rust-src
- bindgen (0.56.0)
開発要件
- rustfmt
- clippy
- rustdoc
カーネルでRustをサポートするのに必要なconfig
General setup
中にあるRust support(CONFIG_RUST)
を有効化する必要がある.
また,Device Drivers
のCharacter devices
にあるRust example(CONFIG_RUST_EXAMPLE)
を有効化する事でサンプルのドライバがインストールされる.
Hack
さらに詳しい内容は/rust
以下のコードやメニュー中のKernel hacking
以下のRust hacking
を読むとわかる.
GDBやBinutilsを利用していて,Rustシンボルがデマングルされていない場合,toolchainがRustのv0 mangling schemeをサポートしていない可能性がある.
drivers/char/rust_example
実際にconfig
でRust example
を有効化した際にビルドされるRustのコードを読んでみる.
Rustサポート全体を把握するには/rust
以下のコードを読むのが良いが,概観を把握するには実際に使われるコードを読んだほうが早いだろう.
/rust
以下のコードもめちゃくちゃ面白いので,ぜひ読んでほしい.超楽しい.
rust_example.rs
#![no_std]
なので,std
は無い.
/rust
以下にalloc
やcore
等のモジュールが定義されており,これらを利用する.
module!
マクロ
module! {
type: RustExample,
name: b"rust_example",
author: b"Rust for Linux Contributors",
description: b"An example kernel module written in Rust",
license: b"GPL v2",
params: {
my_bool: bool {
default: true,
permissions: 0,
description: b"Example of bool",
},
my_i32: i32 {
default: 42,
permissions: 0o644,
description: b"Example of i32",
},
my_str: str {
default: b"default str val",
permissions: 0o644,
description: b"Example of a string param",
},
my_usize: usize {
default: 42,
permissions: 0o644,
description: b"Example of usize",
},
},
}
これはrust/module.rs
で定義されているproc_macro.
コメントを読めばわかるが,パラメタの型,定義が出来る.
ここでは
bool
i8
u8
i16
u16
i32
u32
i64
u64
isize
usize
str
が使える.
FileOperations
linux/fs.h
のfile_operations
.
struct RustFile;
impl FileOperations for RustFile {
type Wrapper = Box<Self>;
kernel::declare_file_operations!();
fn open() -> KernelResult<Self::Wrapper> {
println!("rust file was opened!");
Ok(Box::try_new(Self)?)
}
}
Cでオブジェクト指向っぽくしてる部分をtrait
で実装してるの,すごい良いよね.
ここではopen
しか実装してないので,read
とかwrite
はできない.
KernelModule::init
モジュールのインストール時に実行される奴
struct RustExample {
message: String,
_chrdev: Pin<Box<chrdev::Registration<2>>>,
_dev: Pin<Box<miscdev::Registration>>,
}
impl KernelModule for RustExample {
fn init() -> KernelResult<Self> {
println!("Rust Example (init)");
println!("Am I built-in? {}", !cfg!(MODULE));
{
let lock = THIS_MODULE.kernel_param_lock();
println!("Parameters:");
println!(" my_bool: {}", my_bool.read());
println!(" my_i32: {}", my_i32.read(&lock));
println!(
" my_str: {}",
core::str::from_utf8(my_str.read(&lock))?
);
println!(" my_usize: {}", my_usize.read(&lock));
}
// Test mutexes.
...
ここのprintln
マクロの中身は,普段のRustで使われるprintln!
ではなくて,rust/kernel/printk.rs
で定義されているprintk
.
色んな機能のテスト(というかサンプル)が書かれている.
たとえば
{
let lock = THIS_MODULE.kernel_param_lock();
...
の所とか,Rustのlifetimeを活かして,ブロックの終了時(lifetimeの終了時)にlock自体もドロップされる様になっている.
非常に良い.
impl Drop for RustExample {
fn drop(&mut self) {
println!("My message is {}", self.message);
println!("Rust Example (exit)");
}
}
Drop
はKernelModule
をimpl
している構造体に対してimpl
しろって書かれているが,どこで定義されてるのかパッと見からなかった.
まあdeinit
でしょう.
install
実際にパッチを当ててインストールしてみると,rust_chrdev
が生えてる.
vagrant@vagrant:~$ uname -a
Linux vagrant 5.12.0-rc3+ #7 SMP Fri Mar 19 18:24:14 JST 2021 x86_64 x86_64 x86_64 GNU/Linux
vagrant@vagrant:~$ cat /proc/devices |grep rust
244 rust_chrdev
vagrant@vagrant:~$
open
すると
vagrant@vagrant:~$ sudo cat /dev/rust_miscdev
cat: /dev/rust_miscdev: Invalid argument
vagrant@vagrant:~$ dmesg|tail -n1
[16159.895305] rust file was opened!
ちゃんとFileOperations
で定義したopen
が実行されている.エモい
楽しいね.
後で自分でも書いてみようかな
Comments