このページでは、HinaOSの実験テーマのアイデアを紹介します。HinaOSを元に自分のOSを作って遊びながら、OSの仕組みを学んでみましょう。
新しいサーバを追加してみましょう。たとえばクライアントから処理要求を受け取ると、ジョークを返すサーバを実装してみると楽しいでしょう。
サーバの実装は以下の手順で行います。
messages.idl
に新しいメッセージを定義し、一回make
を実行してメッセージの型定義を自動生成する。servers
ディレクトリに新しいサーバのディレクトリを作成する (servers/pong
をコピーアンドペーストするとよい)。- サーバの実装を書く。
- Makefileの
BOOT_SERVERS
変数に新しいサーバを追加する。すると、起動時に自動的にサーバが起動するようになる。 - クライアント側の実装を書く。たとえばコマンドラインシェルにサーバと通信する新しいコマンドを追加する。
システムコールを追加してみましょう。たとえば「割り込みの発生回数」といった統計情報を取得するシステムコールを追加してみましょう。コマンドラインシェルから確認できるようにしてみると、数字がどんどん増えていくのが見られて面白いでしょう。
HinaOSカーネルはサーバとアプリケーションを区別せず、どのユーザータスクでも物理メモリのマップといった危険な操作ができるようになっています。また、メッセージパッシングの通信相手や通信内容 (メッセージの種類) についても制限がありません。そこで、OSを堅牢にするための次のような機構を実装してみましょう。
- 各タスクが実行できるシステムコールを制限する。
- タスクが送信できるメッセージの種類を制限する。
- 制限内容を設定するためのシステムコールを実装する。
タスク自身が権限を放棄し、制限を自ら設定できるようにしても面白いでしょう。また、HinaVMを活用した柔軟なアクセス制御機構を作ってみるのも楽しそうです。
OpenBSDのpledgeなど、ちまたのOSのセキュリティ機構も参考になります。
HinaOSカーネルは素朴なラウンドロビン方式のスケジューリングしか実装していません。新たにスケジューリングアルゴリズムを実装してみましょう。たとえば、優先度スケジューリングを実装するには、次のような変更が必要です。
- カーネルのタスク管理構造体に優先度フィールドを追加する。
- 優先度を変更するシステムコールを実装する。
- カーネルのスケジューラ関数を変更する。
上手く動いているかを確認するには、次のようなプログラムを実行してみるとよいでしょう。 「A」を出力するタスクと「B」を出力するタスクを作り、スケジューリングアルゴリズムによって、出力される文字数の割合が変わるはずです(たとえば優先度の高い「A」がずっと出力される)。
void main(void) {
for (;;) {
printf("A"); // "B" を出力するプログラムも作っておく
// 出力が多すぎないように、少し待つ
for (unsigned i = 0; i < 1000000; i++) {
asm volatile ("");
}
}
}
簡単のため、HinaOSにはTCPのクライアント側実装しかありません。TCP実装の不足している部分を付け足して、HTTPサーバのアプリケーションを実装してみましょう。自作OSから送られたHTMLをWebブラウザ上で表示できると、飛び上がるほどの喜びが得られます。
なお、HinaOSのWebサーバにアクセスするには、QEMUの hostfwd
オプションを指定すると便利です。たとえばhostfwd=tcp:127.0.0.1:1234-:80
と設定すると、開発機の1234番ポートにアクセスすると、HinaOSの80番ポートに転送されます。
また、適当なサーバ上でQEMUを動かして、HinaOSのWebサーバにインターネットからアクセスできるようにしてみると感動できます。
QEMUのvirtマシンには「Google Goldfish RTC」という、仮想的なリアルタイムクロック (RTC) デバイスが繋がっています。現在時刻を提供するデバイスです。
Goldfish RTCのドキュメントを参考に、デバ イスドライバを実装し、現在時刻を表示してみましょう。
インタプリタを移植して、C言語以外のプログラミング言語でアプリケーションを書いてみましょう。とはいえ、CPythonのような大規模なインタプリタを移植するのは大変です。そこで使えるのが、組み込みソフトウェア向けの軽量なインタプリタです。移植性が高いため、そこまで大変ではありません。
たとえば、次のようなインタプリタがあります。未検証ですが、JerryScriptは移植が比較的簡単そうです。
- QuickJS (JavaScript)
- JerryScript (JavaScript)
- mruby/c (Ruby)
- MicroPython (Python)
- Lua (Lua)
移植するには、次のような作業が必要です。
- C言語標準ライブラリの移植 (Newlibなど)。
stdio.h
といった機能に依存していることが多いため。 - インタプリタをHinaOS内でビルドできるようにする (コンパイラオプションの調整)。
- インタプリタを起動するプログラムを作成する。
servers/shell
に埋め込むと楽かもしれない。
SQLiteは軽量な関係データベース管理システム (RDBMS) です。実はSQLiteは移植性が非常に高く、HinaOSでも比較的簡単に動かすことができるでしょう。
HinaFSの代替として、SQLiteベースのデータベースサーバを実装してみましょう。
sqlite3
ブランチ に実装例が置いてあります。
サーバがクラッシュしたときに、自動的に再起動する機能を実装してみましょう。ユーザータスクが異常終了する際にはVMサーバにメッセージが届くので、その情報を元に再起動を行うと良いでしょう。
ただし、再起動するだけでは不十分です。HinaOSのユーザータスクたちは、起動時に一度だけipc_lookup
関数を呼び出して通信相手を検索します。そのため、再起動した場合はタスクIDを再び探し直すようにしなければなりません。
本の中では、virtio-netとHinaVMの2つの脆弱性を紹介しています。これらの他にも、HinaOSには脆弱性が存在します。脆弱性を実証するプログラムを作成し、脆弱性をどう防ぐべきかを考え、修正してみましょう。
メッセージパッシング以外のプロセス間通信 (IPC) として、共有メモリを実装してみましょう。既存のシステムコールを上手く使えば、VMサーバ上で実装できるはずです。
HinaOSはコマンドラインシェルを実装しています。ただし、文字を表示したり色や装飾をつけたりするのはHinaOSではなく、シリアルポート経由で接続された開発OSのアプリケーション (macOSだとTerminal.appなど) が描画しています。
文字ではなくマウスポインタやウィンドウといった画面を描画できると、自分のOSを作っている感覚がぐんと高まります。また、ウィンドウシステムを愚直に実装すると、画面のちらつきやマウス操作の遅延といった問題が発生しやすいため、描画処理の最適化を考えるのがまた面白いです。
画面描画には当然画面に繋がるデバイス、キーボード・マウス入力をつかさどるデバイス、そして制御するデバイスドライバが必要です。未検証ですが、QEMUのオプション (QEMUFLAGS
) に -device virtio-gpu-device,bus=virtio-mmio-bus.2
を追加すれば、virtio-gpu経由で画面描画ができるはずです。入力デバイスについてもvirtio-inputが使えるはずです。
GUIをゼロから作るといわれてもイメージがつかないと思います。そんな方はMikanOSが参考になるでしょう。x86-64 CPUの勉強もできて一石二鳥です。また、WaylandやX Window Systemの仕組みを調べるのもおすすめです。
HinaOSはC言語で実装されていますが、もちろん他のプログラミング言語でもOSを実装することができます。おすすめはRust、Zig、C++です。特にRustはOS開発に便利な機能が多く面白い題材です。
GoなどのGC付き言語でも実装できないことはないですが、言語ランタイムライブラリの挙動をしっかり理解する必要があるため少し難易度が上がります。
他のOSのアプリケーションをHinaOS上で動かしてみましょう。たとえば、API互換のライブラリを実装したり、システムコールをフックしてLinuxのシステムコールのエミュレーションを実装したりといくつか方法があります。
HinaOSはQEMUのvirtマシン (32ビットRISC-V CPU) のみ対応していますが、他のCPUへ移植しやすいような実装になっています。
おすすめ、Webや書籍に情報がたくさんある x86-64 CPUへの移植です。RISC-Vに比べかなり複雑ではありますが、その分面白いでしょう。
RISC-Vの実機を購入し、HinaOSで動くようにしてみましょう。秋月電子通商やスイッチサイエンスでいくつかRISC-V搭載のボードが販売されています。
ボードによって、シリアルポートの制御方法や物理メモリ領域の構成が異なるため、各ボードの仕様書を読みながら移植を進めていく必要があります。
「HTTPサーバの実装」と「他のCPUアーキテクチャへの移植 (x86-64)」を完了したら、クラウド上でOSを動かしてみましょう。一般的なIaaSでは、x86-64 CPUの仮想マシンが提供されているためHTTPサーバの実装だけでなく、x86-64への移植とネットワークデバイスドライバ (virtio-net) も必要になります。
筆者の知る限り、以下のサービスで自作OSを動かすことができます。
- さくらのクラウド: ISOイメージをアップロードすることで自作OSを起動できる。
- Google Compute Engine: VMの環境情報がココにまとまっている。
- DigitalOcean: OSの種類を「Unknown」にしてDropletを作成すると、DHCPサーバが応答するようになる。詳しくはこちら。
ただし難易度は非常に高いです。デバッグ手段が非常に限られているのと、クラウド上でしか再現しないバグがあるため、勘デバッグ力が必要です。また、ビルド・デプロイに時間がかかるため非常に億劫なテーマです。しかし、その努力に見合うだけの面白さは確かにあります。