約1か月でUnixクローンを作成
(drewdevault.com)- 個人の息抜きプロジェクトとして始まった Bunnix は、x86_64 向けの Unix系オペレーティングシステム を約1か月でどこまで作れるかを試した実験で、実作業日ベースでは 27日 かかった
- カーネルは主に Hare で書かれており、ext4 対応の lwext4 や、カーネルのビデオ端末向け libvterm といった C 製コンポーネントも併用している
- legacy boot と EFI の両方をサポートし、一部の実機ノートPCでもテストされたが、USB非対応 のため PS/2 キーボードや BIOS の PS/2 エミュレーションが必要
- ユーザー空間は dash、Doom、gzip、less、mandoc、sbase、tcc、Vim 5.7 など サードパーティー製ソフトウェア が中心で、libc は musl libc を Bunnix 向けに修正したもの
- Bunnix は動作するもののバグが多く、単一ユーザーシステムにとどまっており、長期保守というより Helios の作り直しとカーネル設計改善につながる実験に近い
Bunnixの範囲と実行
- Bunnix は 2024年4月21日に始まった x86_64 向け Unix系オペレーティングシステム のプロジェクト
- 実際に作業しなかった日を除くと、投入されたのは合計 27日
- 自分で起動できる Bunnix 0.0.0 iso が提供されている
- qemu では次のコマンドで ISO を起動できる
qemu-system-x86_64 -cdrom bunnix.iso -display sdl -serial stdio
- ISO を USB メモリに書き込めば実機でも起動できる
- たいていの AMD64 マシンで動作する可能性がある
- ThinkPad X220 と Starlabs Starbook Mk IV でテスト済み
- legacy boot と EFI の両方に対応
- 最大の実行上の制約は USB非対応 であること
- PS/2 キーボード、または BIOS の PS/2 エミュレーションが必要
- ほとんどのノートPCのキーボードは PS/2 方式で接続されている
- USB キーボードで PS/2 エミュレーションが動くかどうかは環境次第
- Doom ポートにはキー割り当てと終了動作に制約がある
- WASD で移動
- 右 Shift で射撃
- Space でドアを開ける
- ゲーム終了が動作しないため、プレイ後は再起動が必要
カーネル構造と対応機能
- Bunnix カーネルの大半は Hare で書かれており、一部に C 製コンポーネントも使っている
- ext4 ファイルシステム対応には lwext4 を使用
- カーネルのビデオ端末には libvterm を使用
- 対応ドライバーは、基本的なハードウェアとストレージからの起動に必要な範囲に集中している
- PCI legacy
- AHCI ブロックデバイス
- GPT および MBR パーティションテーブル
- PS/2 キーボード
- プラットフォームのシリアルポート
- CMOS クロック
- ブートローダーが設定したフレームバッファ
- ext4 および memfs ファイルシステム
- Unix系システムに必要な基本的なカーネル機能も含まれる
-
仮想ファイルシステムとデバイス
- ブロックデバイス、null、zero、full の疑似デバイス、
/dev/kbd、/dev/fb0、シリアルおよびビデオ TTY、/dev/tty制御端末を備えた/devを提供する - 比較的完成度の高い端末エミュレーターと、ある程度動作する termios 対応が入っている
- ブロックデバイス、null、zero、full の疑似デバイス、
-
システムコールとユーザーモデル
clock_gettime、poll、openat、fork、exec、pipe、dup、dup2、ioctlなどを含む約 40個のシステムコール をサポート- 現在の Bunnix は 単一ユーザーシステム
- Unix のファイルモードや所有権を強制しない
- あと数日作業すればマルチユーザーシステムにできる状態
-
ブートローダーとユーザー空間
- Bunnix には2つの ブートローダー が含まれる
- legacy boot 用ブートローダーは multiboot 互換で、Hare で書かれている
- EFI 用ブートローダーは C で書かれている
- 2つのブートローダーは必要に応じて、カーネルを ELF ファイルと initramfs として読み込む
- EFI ブートローダーは initramfs の展開用に zlib を含む
- multiboot 互換ブートローダーは展開処理を代行する
- ユーザー空間の大半は サードパーティー製ソース で構成される
- Colossal Cave Adventure
advent dash/bin/sh- Doom
- gzip
- less
lok/bin/awk- lolcat
- mandoc
- sbase core utils
- tcc C コンパイラ
- Vim 5.7
- Colossal Cave Adventure
- libc は musl libc 派生で、Bunnix の要件に合わせてさまざまな修正が入っている
- curses ライブラリは netbsd-curses ベース
- システムは動作するがバグが多く、一部の実装は急ごしらえのため、クラッシュに備える必要がある
高速実装を可能にした要素と難所
- Bunnix のコードの一部は以前のプロジェクト Helios から来ている
- GDT、IDT など、一般的な CPU 設定に関するカーネルコードの一部が含まれる
- AHCI などの一部ドライバーも Bunnix のシステムに合わせて調整された
- Helios の経験がなければ、Bunnix をこれほど速く作るのは難しかっただろうと見ている
- ext4 対応 と 仮想端末 の統合は特に難しかった
- lwext4 と libvterm という外部依存を取り込んだ
- ファイルシステム層は何度か書き直され、現在もバグが残っている
openatや inode 処理を含む Unix のファイルシステム設計をきちんと実装するには、lwext4 の内部をさらに深く見る必要があった
- Hare プロジェクト内で Hare、アセンブリ、C のソースを一緒にリンクする経験も得られた
- 全体としてはうまく動いたが、ABI 統合の仕組みを作る部分に不便さがあった
- C ヘッダーを Hare の forward declaration モジュールへ自動変換できるとよいという必要性が生じた
- 関連作業は
hare-cに一部存在するが、まだ不足している
- Vim の移植を目標にしたことで、端末実装 の難度が上がった
- libvterm は優れた端末状態機械ライブラリだが、ドキュメントが不足している
- 正確に統合するには多くの微調整が必要だった
- 滑らかに動かすための 性能最適化 にも時間を割いた
- スケジューラー は Helios ベースのコードをかなり捨てて書き直した領域
- Helios も Bunnix も単一 CPU システム
- Bunnix は Helios と異なり、カーネル内でのコンテキストスイッチを許可する
- プリエンプティブなタスク切り替えもカーネル経由で出入りする
- この構造には複数のカーネルスタックと、異なるタスク切り替え方式が必要
- 十分に堅牢なスケジューラーがあれば、ディスク読み取りや
pipe(2)のようなブロッキング処理を wait queue で単純に実装できる
- シグナル 実装は Unix 互換のために必要だった
- Helios は Unix を目標にしていないため、シグナルなしでも動作する
- Bunnix では dash の移植のため、主に
SIGCHLDが正しく動作するように合わせた - 最終的なシグナル実装は非常に基本的な水準
Helios につながる設計上の教訓
- Bunnix は モノリシックカーネル、Helios は Unix ではないマイクロカーネル設計
- ファイルシステムではキャッシュの重要性が確認された
- Helios ではファイルシステム実装を複数のドライバーと別プロセスに分けている
- 生きているオブジェクトの追跡だけを目的にしても、ファイルシステム層のキャッシュは重要
- Helios の作り直しでは、ファイルシステムコードのリファクタリングや再実装が必要な作業が多くなる
- ドライバーへのアクセスはモノリシックカーネルのほうが自然に単純になる
- ただし、ring 0 に多くを入れた点には完全には満足していない
- Helios のスケジューラーにモノリシック設計の制御フロー要素を一部反映できる余地がある
- メモリ管理では ビットマップアロケーター が予想以上によく機能した
- Helios ではビットマップアロケーターを避けようとしており、メモリ管理は大きな不便ポイントだった
- Bunnix はシステムの一般ページ全体に単純なビットマップアロケーターを使っている
- 懸念していたほどオーバーヘッドは大きくなく、非常によく動作した
- 30日以内に Bunnix を作れたのは、マイクロカーネル設計では不可能だっただろうと見ている
- モノリシックカーネルは実装がはるかに単純
- マイクロカーネル設計の利点も魅力的で、よりよい答えはハイブリッドカーネルかもしれない
プロジェクトの現状と残る改善候補
- Bunnix は今後多くの時間を注ぐ対象というより、ほぼ完成した アートプロジェクト に近い
- ときどき数日単位で作業することはある
- コミュニティからの改善は public inbox でパッチを受け付けている
- 今後の OS 開発は、Bunnix で得た教訓をもとに Helios に戻って主要な再設計を進める方向
- 改善の優先候補は次の通り
- ファイルシステム向けディレクトリキャッシュと全体的なキャッシュ改善
- ext4 のバグ修正
- procfs と top
- ファイル mmap
SIGSEGVのような追加シグナル- マルチユーザー対応
- NVMe ブロックデバイス
- IDE ブロックデバイス
- ATAPI と ISO 9660 対応
- Intel HD audio 対応
- ネットワークスタック
- ベースシステム向け Hare ツールチェーン
- セルフホスティング
1件のコメント
Hacker Newsのコメント
本当にすばらしい。もともとの Unix も、リッチー家が義理の両親に会いにカリフォルニアへ休暇に出ていた数週間のあいだに作られた、という話を思い出す
出典は Brian W. Kernighan の UNIX: A History and a Memoir
今は記憶があいまいなので確認してみる必要がある
「シグナルが上から下までどう動くのかをようやく学んだが、本当に醜い。Unix 設計の中でも最も弱い部分の1つだとずっと感じていたし、このプロジェクトでもその考えは変わらなかった」という箇所について、もっと詳しい資料があれば見てみたい。HN ユーザーや著者が何か知っているなら気になる
https://www.amazon.com/Advanced-Programming-UNIX-Environment...
ユーザー空間でシグナルやプロセスを含む Unix API を扱うための責務についての本
カーネル内でシグナルを実装したいなら何を薦めるべきかはよく分からないが、おそらく https://pdos.csail.mit.edu/6.828/2012/xv6.html あたりが思い浮かぶ
Unix がどう動くのかを、自己完結した例とともに明確かつ体系的に説明する本を読むのは本当に新鮮。C が分からないと障壁になるかもしれないが、ブログ記事を読むときも同じこと
同等の情報が Web のどこかにあるとは思わない。自分のブログにも今でも読まれている Unix の雑学は多いが、同じ水準ではない
Unix シグナルを理解するには、ブログ記事、Google、LLM で取り組むのは非常に効率が悪いテーマの1つだと思う。中古でも「安い」とは言いにくいが、情報に価値があるから高値が維持されている本で、仕事をしているプログラマーにとっては比較的安いほうだ
シグナルは設計に非同期 IPC を無理やり継ぎ足したぎこちない試みなので、競合状態に弱い。シグナル処理中にさらにシグナルを受けたらどうなるのか、プロセスがシステムコール中のときにシグナルをどう扱うのかも曖昧。遅延させるのか、キューに入れるのか、システムコールから抜けさせるのかを決めなければならない
すべてのシステムコールが非同期なら、多くの現代的なオペレーティングシステムが採る設計原則のように、その側面は解決される。IPC に信頼できるチャネルのようなシステムがあれば、シグナルだけでなく、より洗練された非同期のプロセス間通信や手続き呼び出しも実装できる
SIGSTOP/SIGCONT/SIGKILL は、実際にはプロセスに信号を送るというより、一時停止、再開、終了といったプロセス制御を行う
SIGHUP, SIGUSR1, SIGUSR2, SIGTTIN, SIGTTOU のような単純な非同期メッセージは、設定の再読み込みなどに濫用され、デーモン化のために nohup のようなハック的な回避策が付いている。gunicorn は後ろの2つを動的なスケールアウト・スケールインに使うこともある。この分類には SIGWINCH のように妙に具体的なものもある
さらに SIGILL, SIGSEGV, SIGFPE のように、不正命令、セグメンテーション違反、浮動小数点例外を表すものもある。そもそも非同期にしておくのがよいのか曖昧な SIGSYS のようなものもある
他のアプローチにもトレードオフはある。Windows にはイベント、SEH、CTRL+C/CTRL+BREAK/終了処理ルーチン、IOCP、コールバックなどがあり、Plan 9 の notes は文字列なので他プロセスに任意のデータを送れる点はよいが、プロセス制御にも同じ仕組みを使うのは *nix と同じ欠点があり、数字の代わりに文字列であるだけだと思う
Unix シグナルの問題を扱い、Linux がそれを解決しようとして作った signalfd がなぜうまく機能しないのかも説明している
https://pubs.opengroup.org/onlinepubs/009604499/functions/bs...
シグナルハンドラ内のコードは再入可能でなければならない、という点も重要。「再入不能な関数は、一般にシグナルハンドラから呼び出しても安全ではない」
https://man7.org/linux/man-pages/man7/signal-safety.7.html
Hareには関心があったが、このFAQ項目を見て、かなり自滅的な方針だと感じた: https://harelang.org/documentation/faq.html#will-hare-suppor...
基本的には、開発者が望むライセンスを使い、望むOSを対象にし、望むコードを書くことは支持する。
とはいえ、この特定の方針が良い考えになるわけではない。自由ソフトウェア哲学の中でも最も極端、あるいは原則主義的な集団と見なされることの多いFSFでさえ、WindowsとPOSIXをサポートしている。不満を言いながらWoe32と呼ぶことはあっても、Stallmanは、自由ソフトウェアプロジェクトをプロプライエタリなシステム上でも動くようにするほうが、プロプライエタリソフトウェアのない世界を目指す闘いにより役立つ、とかなり説得力をもって語ってきた。
ライブラリコードはMPLでライセンスされているので、Hareを使うだけで特定のライセンスに縛られるわけではない。だが、デスクトップの95%以上に対して「サポートしない、フォーラムで質問するな、ここに来るな」という態度を取る言語がどれだけ長続きするのかは疑問だ。
皮肉なことに、Googleで「harelang repo」と検索すると、最初の結果は非公式のmacOSポートで、実際のSourceHutリポジトリは1ページ目に出てこない。
言語は雪だるま式に大きくなるか、消えていくかだ。今はMacでこれを書いているが、やろうと思えば今すぐLinuxマシンも使える。なのに、FSFでさえやらない純粋性テストを開発者に強いる言語を、なぜ学ばなければならないのか。オープンソースと自由ソフトウェアのかなりの部分はMacで書かれており、思った以上に多くの部分はWindowsでも書かれている。
私の目には、HareをOdinやZigと分けているのは、まさにこの純粋性と排除の姿勢だ。楽しいハッキングと成功を願ってはいるが、後者については悲観的だ。
他方で、FAQで眉をひそめたくなる箇所はそれだけではない。
「パッケージマネージャはなく、共有価値としてコード再利用をあまり推奨しない」
「qbeはLLVMより遅いコードを生成し、性能は同等のLLVM生成コードに対して実行時性能の25〜75%の範囲だ」
「Hareでマルチスレッドを使えるか? おそらく使えない」
「ではハッシュテーブルを自分で実装しなければならないのか? そうだ。ハッシュテーブルは、多くのHareプログラムが一から実装しなければならない一般的なデータ構造だ」
現時点では、大衆的な採用を目指して設計された言語でないことは確かだ。それで問題ないし、少なくともその点を率直に明かしている。
意見を述べる自由はあるが、開発者がすでに指針を定めている状況では、こうした批判は建設的ではないと思う。
聴く価値のあるバンドがすべてBillboardチャートに載らなければならないわけではない。
開発者たちが使っていないOSをサポートするのは大きな要求だ。
全体として人気が高いわけではなくても、確固たるニッチで繁栄し、重要な役割を果たしている言語はかなりある。
印象的で、とても格好よく、刺激になる。「X日で印象的なものを作る」系の例には、何年も積み重ねた経験と才能が必要だ。
英語の文字列をカタログに入れ、複数のテストを更新し、ローカルシステムでテストを走らせ、変更をステージングクラスタに上げ、予期しないテスト失敗を直し、本番に上げ、翻訳担当者に複数言語への翻訳を依頼し、ドキュメントも更新しなければならなかった。
https://www.ticalc.org/archives/files/fileinfo/463/46387.htm...
それを完成度の高いものへ広げるのに必要なのは、特別な天才性というより粘り強さだ。
Mastodonでほぼ毎日上がるアップデートを見るのは本当に素晴らしかった。熟練した人が複雑なソフトウェアを少しずつ組み上げていく過程を見ることができた。
https://fosstodon.org/@drewdevault/112319697309218275
コードはここにある: https://git.sr.ht/~sircmpwn/bunnix/tree/master
GPLv3ライセンスだ。
ユーザー空間はほとんどサードパーティのソースから組み立てられたものだ。
ISOをクリックしたときに60MBのダウンロードが出てきて最初は驚いたが、その理由は理解できた。
比較すると、Linux 0.01は71KBのダウンロードだったが、カーネルソースだけが入っていた。
Hareは興味深い言語のように見える
ただし、このマルチコア時代には、以下の制限が採用の妨げになりそう
FAQ https://harelang.org/documentation/faq.htmlによると、Hareでマルチスレッドを使えるかという質問に対して「おそらく無理」と答えている
入出力処理の多重化にはイベントループを、CPUリソースを並列に使う必要がある場合は共有メモリを伴うマルチプロセスを推奨している
厳密にはHareプログラムでスレッドを作成することはできる。libcにリンクしてpthreadsを使うか、clone(2)システムコールを直接使うこともできる。HeliosのようなHareで実装されたオペレーティングシステムは、通常マルチスレッドを実装している
しかしアップストリームの標準ライブラリは再入可能性を保証しないため、自分で足を撃たないよう、すべて自己責任で扱う必要がある
個人的にはほとんどの用途でこの方式を好む。データ競合の可能性を共有メモリ領域にだけ限定できるから。データ競合の観点では、メモリにおける「unsafe block」のような感覚
「Linux System Call Table – Chromiumos」 https://www.chromium.org/chromium-os/developer-library/refer... https://news.ycombinator.com/item?id=33395777で出ていた内容
google/syzkalleR
Fuschia / Zirconシステムコール: https://fuchsia.dev/fuchsia-src/reference/syscalls
“Memory Sealing ‘Mseal’ System Call Merged for Linux 6.10”
https://news.ycombinator.com/context?id=40474551