雑記帳

電子計算機の"明後日"から、他愛もない話まで。

電脳文庫近況 / Scrapstick概要(1)

長らく開発を続けている割りに今まで殆ど情報を出してこなかったのですが、さすがにモチベーションがアレ気味になってきたので、今回はプロジェクトの現況や概要について、かなり掘り下げて書いてみようと思います。

 

今着手しているのはOSの方です。名前は「Scrapstick(スクラップスティック)」と言います。Slapstickの書き間違いではありません。

 

「ソフト・ハードを問わないモジュール実装と高いモジュール可換性を実現する、新しいコンピュータシステムの開発」なんて掲げていますが、これ使って何がやりたいかというと、気軽にハードウェアの世界と戯れることができる環境と、そんな環境用に書いたコードを将来にわたって(何かしらの簡単な方法で)動かすことができる仕組みを両立させたい…というものです。

「あるOSの中でアプリどうしが通信しているのと、バス上で相互接続されたデバイスどうしが通信してるのって、細かい点は抜きにしてもやってること自体は似たようなもんだろ?」という点に着目して、

  • じゃあそれぞれ区別せずにまぜこぜで通信できるよな仕組みを作ってやれば良くね?
  • いっそ通信相手を(作者ではなく)利用者側都合で自由に差し替えられるようになってりゃ便利じゃね?
  • これ使えば、ハードべったりなコードがあったとしても、必要に応じてハード部をエミュレータへ簡単に差し替えられるので、実機無くなっても動かせるっしょ?
  • なんならこの仕組みにシェル(ユーザコンソール)でも付けてしまえばOSの代わりになるじゃねーか?

…といった発想の元に実装を進めています。OSだけでなくバスプロトコルの開発も視野に入れているのは、ネタの根本がこういったソフト / ハード界面みたいなところにある為です*1。当然、カーネルレベルでのPOSIX互換なんて一切取る気もありません。

 

通信相手がソフト実装かハード実装かを区別しない…ということから察しがつく方もいらっしゃると思いますが、このシステムで扱う通信は「バスアクセス」を抽象化したものです。ユーザモードにおけるコードフェッチやデータエリアのR/W、I/O操作に割り込み等々、大凡全てのバスアクセスがシステム管轄の通信として扱われます(!!)。当然マジメにそんなことしてたら死ぬほど遅くなるので、特定の条件を満たした場合に限ってカーネルを介さず直接ページアクセスできるようにする等の工夫を入れて、実用的な速度で動作するようにしています。また、バスに発行可能なアクセスのサイズは可変長としているので、ブロックI/O的な使い方で通信コスト(発行回数)を減らすことも可能です。この辺は、システムコール的なものを持たせない設計*2などと絡んでいる所でもあるのですが…細かい話はいずれ。

 

前掲の話ではハードモジュールをソフトモジュール(エミュレータ)で置き換えることだけ触れましたが、当然逆も可能です。つまり、元々ソフト実装モジュールだったものをハード実装のアクセラレータに、呼出元コードを弄ることなく利用者都合で自由に切り換えることができます。例えば、元々通信を伴うものや演算系等、処理の粒度が大きく、かつ各々一回の呼出で完結できるようなタイプの処理であれば威力を発揮できるはずです。特に、前述した直接アクセスの条件を満たしていればカーネルを介在せずに直接レジスタを叩くことができるので、大幅な高速化が期待できます。あまりにもソフト間通信コストが問題になった場合は、FPGAボードとかと組み合わせてこういう逃げ道もありますよということで…。

 

この辺をさらに発展させると、

  • デバイスドライバ」の概念は必ずしも必要ではなくなる。同時アクセス時の調停機能と、ハードウェアI/FをAPIが定めるI/Fに見せかける機能を持つエミュレータだったのだ…と解釈できるので、いずれも必要無ければ無くて良い。
  • デバドラが通常階層構造になっているように、エミュレータを多段に繋いでも良い。つまり、各エミュレータ段で直接叩いたつもりのレジスタが、実は他のモジュールでエミュレートされた代物だった…というのを多段に繰り返しても良い。
  • これより、わざわざ抽象化レイヤや共通APIを作らなくても上手くいく可能性が出てくる。例えば、機能Aの実現に使えるハードウェアが5種類位あるとして、作者側は手持ちにあわせてどれか1つのI/Fに合わせたコードを書けば良い。利用者側は、手持ちに合わせて、I/F変換のエミュレータを探すなり*3、作るなり*4すれば良い。恐らく、ユースケースが固まっておらずI/Fが乱立しがちな新規分野だったり、共通APIに集約する動機も無いようなニッチ分野で特に効果的。
  • メモリアクセスが通信である以上、メモリだと思って通信した相手が「本物のメモリ」である必要は無い。即ち、ディスクドライブ等を使ってエミュレートできる(メモリマップドディスク的な)。これより外部ストレージ・メモリ空間上の揮発 / 不揮発メモリ等々を全て等価に扱える。ごくごく自然に仮想記憶が実現できるとでも言うべきか。バス空間上に「ファイルシステム」をマップすることさえ可能。
  • CPUエミュレータを経由して他機種のマシン語で書かれたコードを実行しても、実機向けに書かれたコードと全く同等に扱える。バスアクセスを通信として使うので、通信相手から見ればどっちだろうと差異は無い。極端な話、例えばx64 PC上で、SPARC向けに書かれたアプリケーションが、68000向けに書かれたTCP/IPスタックとARM-v6向けに書かれたモデムドライバと実機モデムを経由して通信を行い、Z80向けに書かれたV9958を使うコンソールに結果を出力…なんてことも、ごくごく自然に行える。CPUエミュレータの多段も可(CPUエミュレータを別のCPUエミュレータ上で動かしたりできる)。
  • これをハードレイヤにも適用すると、ヘテロジニアスなマルチプロセッサ環境を自然に扱える。前述の通り、バスアクセスさえ発行できれば実行コンテキストのアーキテクチャは問わない為。DMAの処遇についても同様。但し、当然ながらカーネル側でも専用の対応が必要。
  • 所用の通信さえ実現できれば良いので、最終的に、Scrapstickの殆どの機能をハードウェア化したアーキテクチャのマシンを作ることも可能かもしれない(ただの浪漫)。

といったように、結構面白いプラットフォームになりそうです。「互換性の担保をエミュレータに押しつける」点や、それを利用して「新しく作る部分では互換性を無視して好き放題できるようにする」という思想については、使い方や目的は違えど、とても古のOSASKっぽい所だと思います(当然ですが、コレには多々強い影響を受けて育ちましたので…)。うまくいけば、コンピュータシステムの組み方・あり方を大きく変えることができるシステムに発展できるかもしれません。例えば、枯れた分野のハードウェアはよりインテリジェントなI/Fを持つ方向に進化することが簡単になるし…ストレージがファイルシステムを話すとか…、新たな分野ではI/Fの試行錯誤がより気軽に行えるようになるはずです*5

 

ここまでは割と美味い話ばっかり書いてきましたが、当然、この方式によるデメリットもあります。

先程からもちょこちょこ触れているように、まずは性能上の問題です。バスアクセス全てをカーネル管轄の通信として扱う以上、このコストがとんでもないことになるであろう予測が立ちます。前述したように、通常のコードフェッチやメモリの読み書き程度であれば、カーネルを介さず直接アクセスできるような細工を入れ込んではあるのですが、モジュール間通信部分はどう転んでもカーネル経由です。単段であればUnix系で言うところのパイプを介したプロセス間通信レベル + αで済むと考えられますが、エミュレータを多段で組むような状況下では各モジュール境界でそれぞれ通信が起きるので、マイクロカーネルでサーバ間通信が多発しているような状況 + α程度を覚悟しておくべきと思います。また、作成者都合で通信相手を限定できない(利用者側で勝手にエミュレータへ差し替えられる / 多段にできる)ことから、時間制約が厳しかったりリアルタイム性を保証しなければ動かない用途には不向きです。こういった性能上の懸念については、いくつか代替の腹案はあるのですが、当面は利便性の代償としてあきらめる方向で考えています。ただ、このシステムの中であっても、極力依存する外部モジュールを減らせばベアメタルに近い性能を出すことは可能と思われるので、必要であればその辺を駆使して乗り切ってほしいところです。

次はこの通信方式に関する点です。モジュール間通信の界面は所定のバスアクセスの形で行う必要があるため、既存のシステムで良く見られる関数呼出スタイルのAPI / ライブラリを、そのまま別モジュールとして切り出すことができません。どうしてもやりたければ、呼出元にラッパ関数を噛ませて、その中で通信を発行する形にする必要があります。これはシステムコールをCから呼ぶラッパ等と同種といえば同種ですが、差し替え可能にしたい部分は全てこの形式にしておく必要があるため、他のシステム用に書いたコードをScrapstickへ移植する場合などはこの点をよく意識しておかないと、折角のメリットが全く得られないハメになるかもしれません。

 

システムの概要をざっくり書いてしまうとこんな感じです。じゃあ後はどこまで形になってるのか…という話なのですが…、以前の記事にも書いたように、現在はWindows上で動く版(以下Win32版と呼びます)を書いていまして、その上でI/Fの確認などの実験を重ねています。流石に記事当時よりも実験が進み、Win32版内蔵のQuark D1000エミュレータ( + α相当。i386Flatモデル固定にして命令を減らしたようなヤツ)上でCで書いた実験アプリを走らせて機能や使い勝手を確認できたりする状態です。詳細は後述しますが、ようやく長年の懸念事項があらかた片づき、今後システムの大改造を伴うような手戻りは無いであろう段階まで来たので、あと2~3機能程、ユースケースとして実現性を確認しておきたい大物が上手くいけば(ハマらなければ)公開に踏み切れると思います。

前述の記事でも少し触れていますが、実機版が完成した暁には、何らかのシリアル通信I/F経由でそのまま協調動作できるようになる予定です。ようはバスアクセスをシステム間でまたいで伝達することができれば、相手方が持つアドレス空間の一部(公開されている領域)をローカルに(仮想的に)マッピングしてアクセスできることを利用して、実機版で走るアプリからWin32版のウィンドウマネージャを叩き、ウィンドウを生成してボタン操作やら入出力やらを受け付けたり、逆にWin32版で走るアプリから実機版のI/Oエリアを叩いたりすることなどに使えます(当然、実機のシェル代わりや各種デバッグにも使えます)。最初に公開するのはこのWin32版の予定です。こっちだけだと魅力半減ですが、どういうものかは伝わるかなぁ…と。

 

(以下、2018/12/29修正)

流石に時間かかりすぎじゃねーの…という話なんですが、実は2012~15年にかけて作っていた代物(1回だけ人目に出したことがあります)を一旦放棄したりと、結構大きな手戻りを何度もやらかしてしまっています(実験的要素しかないので仕方ない部分ではあるのですが…もどかしい)。本稿で書いたことのうちの大半は、この放棄したものでも仕組みとしては実現できる構造だったのですが、一言で言ってしまうと「プラットフォーム性」とでも表現できるでしょうか、使い勝手上の難があった為、その仕組みのままI/F類を整備する気になれず、本格的な実験(特に、発展に書いたような部分の確認)にまで至れませんでした。

この「プラットフォーム性」について具体的に言うと、例えば、自分が書いたコードを他の人にも手軽に使ってもらえるように、或いは他の方が作ってくれたコードを手元で手軽に使えるように…というのは当然ですし、システムを使い込んだ際にスケールしないようでは困りますし(例えばシステムが扱うアドレス空間。フラット・固定長でスケールする訳が無い)、何がどこにあるのかわからずとっちらかったり、システムの扱いが複雑怪奇になりすぎても困ります。この辺り、割とUnix(当初の)やPlan9辺りはバランスが良いんですよねぇ(そしてSmalltalk、テメーはダメだ)。

これらを本来やりたいことと両立して実現できる仕組みに組み上げるのがなかなか厄介で、さりとてノウハウも無いので何度も作っては壊しして進めているような状態です。現在の実装についても、大枠となるブレイクスルーは2017年前半までに得られたのですが、やはり使ってみるとどうしても気になる点が次々出てきて、最近まで長期スパンで大改造を繰り返し行っていた位ですので…。そしてこの部分こそ、システムの根幹を成す「Scrapstickバス」です。少々毛色は違いますが、UnixPlan9で言うところのファイルシステムみたいな立ち位置ですかね。これまで「カーネル管轄の通信」と書いていたのは、このバス上で扱う通信のことです。こいつの概要も書いておきたいところですが、ちょっと長くなりすぎたので年明け以降にでも別途改めて書くことにします。

 

それでは皆様よいお年を。

*1:まぁ単独のバスとして使えるもの方はいつ着手できるかわかりませんが…

*2:HLT等、プロセッサ機能に絡む内容以外のカーネルとの対話は、全て通信経由で行う必要がある

*3:単独では揃わなくとも、多段にすれば揃う可能性が高まる

*4:必ずしもフル機能を作る必要は無く、そのアプリが動く程度のザルいものでOK

*5:I/Fが固まるより前に生まれた徒花たちを、エミュレータさえ書けば後世にも使い続けることができる可能性が高まる。よって、書き捨てることに後ろめたさが無くなる