ここでは、Ruby から下の部分について整理。
----------------------- <<<<< VOS API(その1) ライブラリ(Minix + BSD) stdio math ansi net ohters ------------------------ <<<<< VOS API(その2) Virtual OS FileSystem (V7) Host OS wrapper ------------------------ <<<<< Host OS API Host OS
ここでは、API について整理したい。
__vos_ プレフィックスが付く VOS API には 2 つの種類がある。
それ以外に API は、
この4種類がある。整理されてなかったり、変だったりするが、これが現状(005c)
---
以下特に 気をくばった(or くばるべき(未完))ポイント
API と データ構造
API は、struct dirent を使った API
これは、新規につくらなければならない。
MINIX の dirent.h では、
struct dirent { /* Largest entry (8 slots) */ ino_t d_ino; /* I-node number */ unsigned char d_extent; /* Extended with this many slots */ char d_name[61]; /* Null terminated name */ };
という定義になっている。これは本来 ディレクトリ の物理構造 とは 関係なく、opnedir などのライブラリが提供する 構造体である。
もともと ファイルシステムがサポートする ファイル名の長さを 長くしたかった という事情があったので、V7FS のディレクトリ自体を 上記の構造にする。
ファイルシステム内の構造は、struct direct 。これが一致する。
dirent == direct
である。
opendir 等の API は、other/dir.c で作成。
readdir は、
という構造になっている。
なお、DIR という 型があるが みていない、FILE だとして扱っている。
問題点(TODO)
ファイルディスクリプタは、VOS のファイルシステムが提供する。
ファイルシステム内の ファイルディスクリプタはそれで良いわけだが、 stdin/out , socket 等のリソースを どう扱うか 決めなければならない。
hfd : HOST OS のファイルディスクリプタ vfd : Virtual OS のファイルディスクリプタ と表現する これだけでは説明しにくいので、 hfd == 15 vfd == 7 であったとして 表記する。 vfd(7) は、/dev/fd/15 を open したときの ファイルディスクリプタ と定義する。 たとえば、 hfd = socket(); で HOS の ファイルディスクリプタ 15 が得られたとする。 この場合 vfd = open(/dev/fd/15) として、vfd を返す。 vfd に対する read/write は、ファイルシステムを通して、/dev/fd/15 に対する read/write になるわけだ。 で、ファイルシステムには、/dev/fd のドライバを仕込んでおいて、 minor(dev) に対する ファイルに対し read/write する。 それ以外 たとえば send/recv といった ものは、ファイルシステムを通さない。 これは、socket で生成するときに、 __vos_fdconv_tab[] というテーブルを作っておいて、 hfd = __vos_fdconv_tab[vfd] で hfd に変換し、HOST OS に対して send/recv する。 dup/dup2 はかなり面倒。 ファイルシステムに閉じている場合は良いのだが、... そうでないとき -- HOST OS の dup を呼び出し、新たにファイルディスクリプタを得る。 -- 上の socket の 例のように して vfd を作成。 という手順になる。 普通は、 vfd1 ---\ inode vfd2 ---/ おなじ inode を使うが、socket や stdin/stdout の 場合 HOST OS vfd1 --- /dev/fd --- hfd1 ------- | ---- \ | inode vfd2 --- /dev/fd ---- hfd2 ------ | ---- / HOST OS にまで いくわけだ。 なぜ こんな構造をしているかというと、select サポートのため。 いまの select の id 変換で vfd と hfd は、1:1 にしたいという 事情がある dup の概念は、UNIX 独自だから、HOS 側に それを期待するのは、 ちょっとまずい。 しかし、本来このようなことをせずとも良いので、select 側を 対応して HOS の dup を使わないように したい。(TODO)
ちょっと話がずれるかもしれないですが、ruby/io.c は、dup, dup2つかってます。
特に$stdin, $stdout への代入のフックではdupを使っているのでdupまたはその代替は必須かな。
cygwinはどうやってるんだろう?
CGI (cgi.rb)まわりで、ファイルディスクリプタの番号0, 1でなくて、$stdin, $stdout を使ってるなら
ごまかす余地はあるかもしれないけれど。
HOST の dup を使わないしくみは以下のとおり
dup では、ローカルに dup するだけでなく、 __vos_fdconv_tab[vfd] にも登録してしまう。__vos_fdconv_tab[vfd] が いくつ重複しているか わかるように __vos_hfd_refcnt[] も 作成 。 で、__vos_fdconv_tab[vfd] に登録されているものは、 __vos_sockclose を呼びだすが、実際に close するのは、 リファレンスカウントが 0 になったとき。 select で hfd がどの vfd なのか判断する必要がある。 どの vfd に対して bit を立てたはわかるので、その vfd を選ぶ。 ようにした。 dup2 は、入れ忘れた __v7_dup2() が 成功した後 #ifndef USE_HOST_DUP if (__vos_issock(fdes)) __vos_sockdup2(fdes,ret); #endif とすれば OK 。
シグナルは、VOS では まじめにサポートする必要はないはずだ。
なぜなら、1つの プロセスしかなく、外のプロセスのことを関知しない というのが VOS のポリシーだから。
signal に関する API は提供する、しかし どこからも シグナルはやって来ない という考えで作る。
コードは、minlib/other/sigfake.c
ところが、例外があった、タイマーである。タイマーは時間がたてば やってくる。
タイマーを登録する API は、UNIX では、alarm か setitimer しかない。 いちおう、タイマーなしでも Ruby のスレッドは動くようなので、 OFF にしてあるが、setitimer は サポートできる。
ただし、VIRTUAL TIMER を指定でき あがるシグナルは、ALRM と VTALRM を ただしく選択できるものの 時間の管理は、REAL である。 理由は、HOST に 負担をかけたくないため。
HOST 側で、
__hos_timer_block __hos_timer_unblock __hos_timer_setの関数を用意すれば、タイマーはサポートできるが、
UNIX API で これを作るのに
setitimer sigaction sigaddset sigdelset sigemptyset sigismember sigprocmaskが結局必要になった。別の OS ではまた違った実装になるかも知れない ということで、できるだけ プリミティブなものを使うという ことにしている。
実は NetBSD で動かすとき はまった。
Linux とか古いものは、
unsigned short sa_family; char sa_data[14];
BSD 系は、
unsigned char sa_len; unsigned char sa_family; char sa_data[14];
という感じになっている。
ndbm の移植ではまったのだが、BSD の bcopy と memcpy はおなじではない。 オーバラップするときちゃんとコピーされるかどうかが違う。
で、Linux の memcpy は定義どおり オーバラップするケースを考慮しない。 しかし、NetBSD とかは、memcpy は、オーバラップするケースを考慮してくれる。 NetBSD とかで動くからといって、Linux でも動くとは限らない。