DragonFly - User API ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Creating a Portable User API 移植性のあるユーザAPIを作る Most standard UNIX systems employ a system call table through which many types of data, including raw structures, are passed back and forth. The biggest obstacle to the ability for user programs to interoperate with kernels which are older or newer than themselves is the fact that these raw structures often change. The worst offenders are things like network interfaces, route table ioctls, ipfw, and raw process structures for ps, vmstat, etc. But even nondescript system calls like stat() and readdir() have issues. In more general terms the system call list itself can create portability problems. 多くの標準的な UNIX システムでは、生の構造体データを含む多種のデータを、シ ステムコールテーブルを通じてやりとりします。ユーザプログラムがそれ自身より 古いあるいは新しいカーネルとやりとりする上で最大の障害は、これらの生の構造 体データはよく構造が変わるということです。最も厄介なのはネットワークインタ フェイス、ルーティングテーブルの ioctl 、 ipfw 、 ps, vmstat が直接アクセ スするプロセス構造体などです。しかし、 stat() や readdir() のようなどうと いうことのないシステムコールでさえ問題があります。もっと一般的な言い方をす れば、システムコールはそれ自体が移植性の問題を生むことがあるということです。 It is a goal of this project to (1) make all actual system calls message-based, (2) pass structural information through capability and element lists instead of as raw structures, and (3) implement a generic 'middle layer' that looks somewhat like an emulation layer, managed by the kernel but loaded into userspace. This layer implements all standard system call APIs and converts them into the appropriate message(s). このプロジェクトの目標として以下のものがあります。 (1) 実質全てのシステムコールをメッセージベースにする。 (2) 構造体の情報を、直接ではなく機能や要素のリストを通じて渡すようにする。 (3) 汎用の「中間層」を実装する。 (3) はある種のエミュレーション層のように見えるもので、管理はカーネルが行う がユーザ空間にロードされます。この層は全ての標準的なシステムコール API を 実装し、それらを適切なメッセージに変換します。 For example, Linux emulation would operate in (kernel-protected) userland rather then in kernelland. FreeBSD emulation would work the same way. In fact, even 'native' programs will run through an emulation layer in order to see the system call we all know and love. The only difference is that native programs will know that the emulation layer exists and is directly accessible from userland and won't waste an extra INT0x80 (or whatever) to enter the kernel just to get spit back out again into the emulation layer. 例えば、 Linux エミュレーションはカーネルランドでなく(カーネルに保護された )ユーザランドで動作します。 FreeBSD のエミュレーションも同じように動作しま す。実際「ネイティブ」なプログラムもシステムコールという私達がよくなじんだ ものを見るためにエミュレーション層を通ります。ただ違うのは、ネイティブなプ ログラムはエミュレーション層が存在し、ユーザランドから直接アクセスできるの を知っているので、ただカーネルに入ってすぐにエミュレーション層に戻るためだ けに余分な INT0x80 (でも何でも)を無駄にしない、ということです Another huge advantage of converting system calls to message-based entities is that it completely solves the userland threads issue. One no longer needs multiple kernel contexts or stacks to deal with multiple userland threads, one needs only one kernel context and stack per user process. Userland threads would still use rfork() to create a real process for each CPU on the system, but all other operations would use a thread-aware emulation layer. In fact, nearly all userland upcalls would be issued by the emulation layer in userland itself, not directly by the kernel. Here is an example of how a thread-aware emulation layer would work: システムコールをメッセージベースの構成要素に変更することによるもう一つの大 きな利点は、ユーザランドスレッドの問題を完全に解決できるということです。も う複数のユーザランドスレッドを処理するのに複数のカーネルコンテクストやスタ ックは必要なくなり、プロセスあたり一つのカーネルコンテキストとスタックがあ ればよいのです。ユーザランドスレッドはシステムの各 CPU ごとに実プロセスを 生成するのに rfork() を使い続けますが、他全ての処理はスレッドに対応したエ ミュレーション層を使えます。実際、ほとんど全てのユーザランドの upcall (コ ールバック)はカーネルから直接発行されるのではなくユーザランドのエミュレー ション層から発行します。以下はスレッド対応エミュレーション層が動作する例で す: size_t read(int fd, void *buf, size_t nbytes) { syscall_any_msg_t msg; int error; /* * Use a convenient mostly pre-built message stored in * the userthread structure for synchronous requests. */ msg = &curthread->td_sysmsg; msg->fd = fd; msg->buf = buf; msg->nbytes = bytes; if ((error = lwkt_domsg(&syscall_port, msg)) != 0) { curthread->td_errno = error; msg->result = -1; } return(msg->result); } And there you have it. The only 'real' system calls DragonFly would implement would be message-passing primitives for sending, receiving, and waiting. Everything else would go through the emulation layer. Of course, on the kernel side the message command will wind up hitting a dispatch table almost as big as the one that exist in FreeBSD 4.x. But as more and more subsystems become message-based, the syscall messages become more integrated with those subsystems and the overhead of dealing with a 'message' could actually wind up being less then the overhead of dealing with discrete system calls. Portability becomes far easier to accomplish because the 'emulation layer' provides a black box which separates what a userland program expects from what the kernel expects, and the emulation layer can be updated along with the kernel (or a backwards compatible version can be created) which makes portability issues transparent to a userland binary. たったこれだけです。 DragonFly が実装する「本当の」システムコールは送信、 受信、待機に必要な原始的なメッセージ通信機能のみです。他はエミュレーション 層を通過します。もちろん、カーネルの側ではメッセージコマンドは FreeBSD 4.x にあるのと同じ規模のディスパッチテーブルにたどりつきます。でもサブシステ ムがメッセージベースになっていくにつれて、 syscall メッセージはよりいっそ うこれらのサブシステムに統合されてゆくので、「メッセージ」を処理するための オーバヘッドは最終的には独立したシステムコールを処理するオーバヘッドよりも 小さくなるでしょう。「エミュレーション層」はユーザランドプログラムが期待す るものとカーネルが期待するものを分離するブラックボックスの役割をするので、 移植性を確保することははるかに簡単になります。エミュレーション層はカーネル といっしょに更新する(または後方互換性のあるバージョンを作る)ことができるた め、ユーザランドのバイナリからは移植性の問題は見えなくなります。 Plus, we get all the advantages that a message-passing model provides, including a very easy way to wedge into system calls for debugging or other purposes, and a very easy way to create a security layer in the kernel which could, for example, disable or alter certain classes of system calls based on the security environment. 加えて、メッセージパシングモデルが提供する利点を全て受けられます。それはデ バッグや他の目的のためにシステムコールに割込んだり、たとえばセキュリティ環 境のもとづいて特定クラスのシステムコールを無効にしたり変更したりするといっ たセキュリティ層をカーネル内に構築するというものです。