Rustで作るx86_64 自作OS入門シリーズ
Part1【環境構築】 | Part2【no_std】 | Part3【Hello World】| Part4【VGA】 | Part5【割り込み】 | Part6【ページング】 | Part7【ヒープ】 | Part8【マルチタスク】| Part9【ファイルシステム】 | Part10【ELFローダー】 | Part11【シェル】 | Part12【完結】
はじめに
Part11でシェルが動きました。ls、cat、echo、ファイル作成、プログラム実行...
12日間で自作OSが完成しました!!
この最終回では、これまでの振り返りと今後の展望をまとめます。
完成したOSの機能一覧
| Part | 機能 | 状態 |
|---|---|---|
| 1-2 | 環境構築・no_std | ✅ |
| 3-4 | VGAテキスト出力 | ✅ |
| 5 | 割り込み・キーボード | ✅ |
| 6 | ページング | ✅ |
| 7 | ヒープメモリ | ✅ |
| 8 | マルチタスク(async/await) | ✅ |
| 9 | ファイルシステム | ✅ |
| 10 | ELFローダー | ✅ |
| 11 | シェル | ✅ |
スクリーンショット
=====================================
MyOS v0.1 - Welcome!
=====================================
Initializing...
[OK] VGA initialized
[OK] GDT loaded
[OK] IDT loaded
[OK] PIC initialized
[OK] Paging enabled
[OK] Heap allocated (100 KB)
[OK] Filesystem mounted
MyOS Shell v0.1
Type 'help' for available commands.
myos:/> ls
/hello.txt 21 bytes
/readme.md 33 bytes
/bin/ 0 bytes
myos:/> cat /hello.txt
Hello from SimpleFS!
myos:/> echo "Hello World!"
Hello World!
myos:/> run /bin/hello
Hello from user program!
Process exited with code: 0
myos:/> _
これが12日間の成果物です。感無量...
使用した技術・クレート
Rust nightly機能
-
#![no_std]- 標準ライブラリなし -
#![no_main]- 通常のmainなし -
alloc- ヒープ割り当て -
asm!- インラインアセンブリ
クレート
| クレート | 用途 |
|---|---|
| bootloader | ブートローダー |
| x86_64 | CPU操作(IDT, GDT, ページング) |
| pic8259 | PIC制御 |
| pc-keyboard | キーボードデコード |
| spin | スピンロック |
| lazy_static | 遅延初期化 |
| linked_list_allocator | ヒープアロケータ |
| futures-util | 非同期ユーティリティ |
| conquer-once | 一度だけ初期化 |
| crossbeam-queue | ロックフリーキュー |
カスタムターゲット
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": 64,
"os": "none",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float",
"rustc-abi": "x86-softfloat",
"panic-strategy": "abort"
}
苦労したランキング
1位: コンテキストスイッチ(Part8)
レジスタの退避・復元を手動で書こうとして1週間溶けた。結局async/awaitに逃げた。
2位: ページングのアドレス計算(Part6)
仮想アドレスと物理アドレスの変換でバグりまくった。特にbootloaderが用意するマッピングの理解に時間がかかった。
3位: ターゲット設定(Part1-2)
target-pointer-widthを文字列にしたり、rustc-abiを忘れたり。エラーメッセージが不親切で原因特定が大変だった。
4位: PICの初期化順序(Part5)
PICを初期化する前に割り込みを有効にして死んだ。初期化の順番って大事。
5位: ELFの.bssゼロ初期化(Part10)
p_fileszとp_memszの違いを理解するのに時間がかかった。.bssのゼロ初期化を忘れてゴミが入った。
学んだこと
技術的なこと
- メモリ管理の重要性: ページング、ヒープ、アロケータ...全部つながってる
- 割り込みの仕組み: ハードウェアとソフトウェアの橋渡し
- 非同期プログラミングの本質: 状態機械への変換
- ELFフォーマット: 実行ファイルの構造
精神的なこと
- 動かないのが普通: OS開発でいきなり動くことはない
- シリアル出力は神: QEMUのシリアル出力がないと詰む
- 段階的に進める: 一気に全部やろうとしない
- 既存実装を参考に: 車輪の再発明も大事だけど、参考にすることも大事
今後の展望
やりたいこと
プリエンプティブマルチタスク
今回は協調的(async/await)だったので、タイマー割り込みで強制的に切り替えたい。
ユーザーモード
Ring 0(カーネル)からRing 3(ユーザー)への切り替え。セキュリティのため。
本物のファイルシステム
RAMディスクじゃなくて、ATA/SATAドライバを書いてディスクにアクセスしたい。
ネットワーク
TCP/IPスタック実装して、HTTPサーバーを動かしたい。
GUI
フレームバッファを使ってウィンドウシステム。マウス入力も。
セルフホスティング
MyOS上でMyOSをビルドできるようになりたい。
参考にしたい既存OS
| OS | 言語 | 特徴 |
|---|---|---|
| Redox | Rust | マイクロカーネル、UNIX互換 |
| seL4 | C | 形式検証済みマイクロカーネル |
| xv6 | C | 教育用UNIX、シンプル |
| MikanOS | C++ | 30日で作るOS、UEFI |
プロジェクト構成(最終形)
my-os/
├── Cargo.toml
├── x86_64-my_os.json
├── .cargo/
│ └── config.toml
└── src/
├── main.rs
├── vga.rs
├── interrupts.rs
├── memory.rs
├── allocator.rs
├── fs/
│ ├── mod.rs
│ └── simple_fs.rs
├── elf/
│ ├── mod.rs
│ └── loader.rs
├── task/
│ ├── mod.rs
│ ├── simple_executor.rs
│ ├── executor.rs
│ └── keyboard.rs
└── shell/
└── mod.rs
最後に
12日間、本当にお疲れ様でした(自分に)。
最初は「OS作るとか無理でしょ」と思ってたけど、一歩ずつ進めたらシェルが動くところまで来れました。
OS開発は「全部自分で作る」という究極のプログラミング体験。普段使ってるLinuxやWindowsがどれだけ複雑なのか、身をもって理解できた。
そしてRust最高。メモリ安全性をコンパイル時にチェックしてくれるおかげで、バグの原因が「ダングリングポインタ」とか「二重解放」みたいな地獄にならなくて済んだ。
謝辞
- Writing an OS in Rust - 最高のチュートリアル
- Rustで始める自作組込みOS入門 - 日本語で詳しい
- OSDev Wiki - 困ったらここ
- Qiitaの読者の皆さん - モチベーションになりました
シリーズまとめ
| Part | タイトル | 一言 |
|---|---|---|
| 1 | 環境構築 | nightlyつらい |
| 2 | no_stdの世界 | printlnない、かなしみ |
| 3 | Hello World | 文字出た!感動 |
| 4 | VGA詳細 | 80x25の世界 |
| 5 | 割り込み | キーボード動いた! |
| 6 | ページング | アドレス変換むずい |
| 7 | ヒープ | Vec使える!神 |
| 8 | マルチタスク | async/awaitに逃げた |
| 9 | ファイルシステム | OSっぽくなってきた |
| 10 | ELFローダー | プログラム動いた! |
| 11 | シェル | ls cat echo!! |
| 12 | まとめ | ←今ここ |
12日間で「Hello World」から「シェルでコマンド実行」まで到達できました。
自作OS、楽しいのでぜひ挑戦してみてください。最初の「画面に文字が出た!」瞬間の感動は格別です。
このシリーズが役に立ったら、いいね・ストック・フォローしてもらえると嬉しいです!
お疲れ様でした!🎉