😃

中学生が趣味で開発しているOS、mochiOS

に公開1

こんにちは。趣味で低レイヤいじいじしてる中三のたそです。
ここでは、自分が趣味で開発しているOSであるmochiOSを、今どこまでできているのか、他の自作OSと何が違うのか、アーキテクチャ、そしてこれから改善していきたいところをまとめています。

mochiOSは、Rustを使用しておもに開発しているx86_64向けのOSです。名前の由来はこのOSでは「できるかぎりクラッシュしないこと」を目標にしていて、餅は柔らかくて伸びてもすぐにはちぎれたりしないのでmochiOSになりました。

当たり前ですが、現時点で本当にクラッシュしないOSが完成しているわけではありません。むしろ今は、そのために必要な土台を一つずつ作っている段階でしかないです。
目標は、単に画面に文字を出す、ブートする、といった基盤だけで終わらせるのではなく、サービス(後述してます)管理やユーザーランド、ファイルシステム、ドライバ、GUI、セキュリティを固めるなど、実用レベルの基盤を作りたいなと考えています。

このブログは一部の文章の日本語の訂正などにAIを利用しています。ただし、本文の内容などはすべて自分で考えています。

っていうかなんでOS作ってるの?

小5の時(たぶん)、父親と地元の公民館で「ホームページを作ってみよう!」的な奴があり、参加しました。
それのおかげでWebサイトを作る方法というのはわかりましたが、私は一つ疑問に思いました。

どのようにしてブラウザはHTMLとかCSSを画面に描いているんだろう?

と。
そこで、ブラウザはアプリであるということを知りました(悲しいことに、当時の私はアプリであることすら知らなかったのです)。

そこで次に私は、アプリはどのように動いているのだろうかという疑問が浮かびました。
そこでいろいろ調べていくうちに、アプリはどうやらOSなるものの上で動いているらしいということがわかりました。

ですが、私はまたそこで疑問が浮かんだのです。

OSもアプリと同じ機械語で動いているのに、アプリが動くにはOSが必要で、じゃあOSはどうやって動いているの?

そこから私はOSについて調べていましたが、結局何もわかりません。
というわけでわからないなら作ったら理解できるだろ!と思い、みかん本を購入しました。

とりあえずMikanOSをカスタマイズしただけで満足していたらよかったのですが、私は一から自分だけのOSを作ってみたい!!と思ってしまうようになりました。
そこで、liteCoreというOSを作りました。(AIの力を借りながら)
ですが、このOS(というのも憚られる)はすぐクラッシュしてアプリもまともに起動できないような、実用的とはとても言えないOSでした。

そこで私は考えました。

絶対にクラッシュしないOSって作れないんだろうか?

正直こんなことができていればすでに頭のいい大人の人たちが解決している課題だと思いましたが、(というかseL4などすでにクラッシュしないカーネルはあまたとある)やはりやってみたい!という気持ちのほうが強くなりすぎたのでやることにしました。

現在の進捗

mochiOSは、UEFIから(BIOSは未対応です)起動するブートローダーを持っています。ブートローダーはSystem/kernel.elfSystem/initfs.imgを読み込み、カーネルへBootInfoを渡します。ここにはメモリマップ、フレームバッファ情報、initfs/rootfs(Linuxのrootfsとほぼ同じ)の位置など、カーネル初期化に必要な情報が入っています。

initfsには主にカーネルモジュール(.cext)やcore.serviceなどが入っています。
カーネル側ではCPUの初期化、GDT/IDT、ページング、フレームアロケータ、タイマー割り込み、スケジューラ、システムコールなどを初期化します。そのあとはカーネルが直接すべてを管理し続けるのではなく、core.serviceというユーザーランドで動作するマネージャを起動します。

現時点で作れているものは次の通りです。

  • UEFIブートローダー
  • x86_64向けカーネル
  • ページングとメモリ管理
  • ラウンドロビン型のスケジューラ
  • プロセス、スレッド、PID/TID管理
  • ELF実行
  • システムコール層
  • IPC
  • initfs/rootfs
  • ext2ベースのファイルシステムイメージ
  • サービスマネージャ
  • ドライバマネージャ
  • USB/ネットワーク系ドライバの土台
  • シェルサービス
  • Newlib/libc連携
  • lscatcpgreppsなどの基本的なユーザーランドコマンド
  • Kagami、ViewKit、Dock、Terminal、BinderなどのGUIアプリケーション基盤(開発途中です)
  • Linuxとのバイナリ互換性(vimやBusyboxくらいなら動きます)
  • cext(Core Extension)というカーネルモジュール(ディスクIOなどの機能を追加するのに使用しています)
  • サービスのケーパビリティ(開発中)
  • そのほかいろいろなセキュリティ機能

rootfsでは/Binariesにはcoreutilsなどコマンドラインから呼び出すようなバイナリが配置され、/Servicesにはサービスバイナリ、Applicationsにはアプリケーションバンドル(macと同じようなやつです)が置かれています。OSのディスクイメージを作り、その中に実行ファイルや設定ファイルを配置して起動する流れになっています。

rootfsには以下のディレクトリが含まれています(builders/fs_image.rsからの抜粋です。あとResourcesはほぼ使ってません。たぶん後々削除されます)

        "System",       // システム(カーネルやカーネルに関連するファイルを配置)
        "Applications", // ユーザーアプリケーションを配置
        "Binaries",     // コマンドやユーティリティを配置
        "Libraries",    // ライブラリ(libc.aなど)、アプリのデータなどを配置
        "Mount",        // マウントしたやつ配置
        "Boot",         // ブートローダー関連のファイルを配置
        "Resources",    // 各種リソース配置
        "Services",     // サービスを配置
        "Logs",         // ログを配置
        "Home",         // ユーザーディレクトリを配置
        "Device",       // デバイスファイル(nullやttyなど)を配置
        "Config",       // 設定ファイルを配置
        "Variables",    // 環境変数や一時ファイルを配置
        "Temp",         // 一時ファイルを配置

アーキテクチャ

mochiOSは、ハイブリッドアーキテクチャを目指しています。すべてをカーネルに押し込むモノリシックな設計ではなく、かといって最初から完全なマイクロカーネルとして分離しきるわけでもありません。現実的に動くものを作りながら、壊れにくく、復旧しやすい構造へ寄せていく方針です。
簡潔に言うと必要なものが生まれたら作るという方針でやってます。

全体の流れを簡単に書くと、次のようになります。

ブートローダーはkernel.elfを読み込んでPT_LOADセグメントを展開し、カーネルはメモリ管理や割り込み、システムコールといった特権的な処理を担当します。

一方で、起動後のサービス管理は基本的にcore.serviceにすべて委ねます。core.service/Config/services.listを読み、必要なサービスを起動します。設定ファイルがなければ、デフォルトでdriver.serviceを起動するようになっています。driver.service/Config/drivers.listまたはデフォルト設定に従ってドライバを起動します。(/Binaries/drivers/usb.elfなど)

この構造の良いところだと私が考えているところは、将来的にもしサービスがクラッシュしても壊れたサービスだけを再起動する、ドライバクラッシュ時にドライバを再起動する、といった設計へ発展させやすいことです。今はまだ監視ループが中心ですが、サービスマネージャを独立したプロセスとして置いているため、復旧機構を追加する場所がはっきりしているので開発しやすいと考えています。

mochiOSの差別化ポイント

mochiOSで特に大事にしているのは、極力OSをクラッシュさせないための設計です。

自作OSでは、最初はどうしても「ブートした」「文字が出た」「キーボードが動いた」という段階が目標になりがちだと思います。(というか私がそうですが)
もちろんそれもすごく大事だと思っていますが、mochiOSの開発を始めたときは実用レベルにするという目標だったので、実用的にするということを考えたときに、壊れたときにどう復旧するか、権限をどう分けるか、どうやったらユーザー空間からカーネルを壊せないようにするか、を早い段階から考えるようにしました。

例えば、システムコール層ではユーザー空間ポインタを検証し、copy_from_usercopy_to_userを通してページテーブルを確認しながらメモリコピーする設計になっています。IPCにもメールボックス構造があり、メッセージの宛先世代を見て古い宛先へのメッセージを破棄するなど、単純な共有グローバル変数より安全な方向へ進めています。

また、Kagamiというウィンドウシステムの設計も頑張っています。
Kagamiは単なる描画ライブラリではなく、コンポジット型の(Waylandみたいなやつです)ウィンドウサーバーとして設計しています。アプリケーションが直接VRAMへ自由に書き込むのではなく、ウィンドウサーバーが画面を合成し、入力イベントも管理する方向です。これにより、将来的には、一般アプリがシステムUIを偽装できなくて、パスワード入力中は他アプリへキーイベントを渡さない、といった保護を入れたいです。

さらに、ViewKitというUIツールキットも開発しています。OS本体だけでなく、アプリケーションを作るための部品まで用意しているのは、mochiOSを単なる実験用のOSではなく、実際に使える環境へ近づけたいからです。
(というかただただAppleのAppKit的なものを作ってみたかったというのはありますが)

これから改善したいところ

一番したいなと思っている改善は、サービスの監視と自動復旧です。

mochiOSの目標がクラッシュしないOSなら、プロセスやサービスが落ちたときに何が起きるかが重要になると思っています。
現在のcore.serviceはサービスを起動して監視ループに入りますが、まだ本格的な再起動監督機構はありません。今後は、重要サービスの生存確認、クラッシュ時の自動再起動、指数バックオフ、再起動上限、復旧できない場合の縮退モードなどを入れたいです。

次に、権限モデルです。現在は権限周りが完成していません。将来的には、プロセスごとに資格情報を持たせて、ファイルシステムアクセスやIPC、特権システムコールが触れるかといったことを反映させたいです。これができるたら、「一般アプリはドライバ操作できない」「システムサービスだけが特定のIPCを受けられる」という設計ができます。

メモリ保護もやりたいです。ELFロード後のW^X、mprotect、シグナル復帰経路の安全化、例外/IRQ経路での保護統一など、やるべきことはまだ多いです。
特に、ユーザー空間とカーネル空間の境界はOSの信頼性そのものなので、ここは時間をかけてちゃんとしていきたいです。

また、クラッシュテレメトリや監査ログも必要です。クラッシュしたときにただ止まるのではなく、原因をログに残し、再起動後に回収できるようにしたいです。これは壊れないようにするだけでなくて壊れても直せるOSにするために必要だと考えました。

ギャラリー

スクショたちです。(v0.1-dev4なので最新ではありません)

  • 起動したての状態
    起動したてのやつ
  • lsしてみる
    lsする
  • cdしてlsしてみる
    cdしてlsした結果
  • モックのデスクトップGUIとモックDock
    GUI起動
  • ウィンドウ表示してみる(画像は開発初期のものです。今はめちゃくちゃデザイン変わってますしなんならHTMX使ってません)
    ViewKit demo起動

さいごに

mochiOSは、まだ開発途中の自作OSです。これは中学生が趣味で作っているOSなので、だいぶ荒いところもありますが、いったんメモ書き程度にブログを書きました。(まじで本業の方が見たら噴き出すだろうな、というくらい雑な実装なところが多いです)

リポジトリは下です。(mainブランチはまともに更新していないのでdevブランチを見てくれたらうれしいです)
tas0dev/mochiOS

スターなどしていただけたら励みになります!

Discussion