Infernoでファイルサーバ
ファイルサーバとしてサービスを実装していくのはPlan9と一緒で、bind/mountやsrvデバイス(#s)等の仕組みも同じ。srvデバイスが変換する通信プロトコルが9PかStyxかの違いはあるが(といってもほとんど違いはないみたいだが)。ファイルサーバの実装という視点から見ると、Plan9の場合は「ファイルサーバの雛形」に書いたが、ファイルに対するopen-close-read-writeのコールバック関数を実装していくことになる。一方、Infernoの場合はこれまたチャネルを活用する。
srvデバイスとファイルサーバのやり取りをラップしてくれているのが、Sys->file2chan関数である。引数にsrvデバイスがmountされたディレクトリとファイル名を取り、FileIO ADTへのリファレンスを返す。FileIOはread、writeという二つのチャネルからなる。
include "sys.m";
sys := load Sys Sys->PATH;Rread: type chan of (array of byte, string);
Rwrite: type chan of (int, string);
FileIO: adt
{
read: chan of (int, int, int, Rread);
write: chan of (int, array of byte, int, Rwrite);
};file2chan: fn(dir, file: string): ref FileIO;
使い方はこんな感じ。read、writeの戻り値はそれぞれ4つの要素からなるタプルになる。Limboではタプルを使って、多値の戻り値を実現している。
sys->bind("#s", "/usr/oraccha", sys->MBEFORE);
channel := sys->file2chan("/usr/oraccha", "test.file");
for(;;) alt {
(offset, count, fid, rc) := <- channel.read =>
end := offset + count;
rc <- (buf[offset:end], nil);
(offset, data, fid, wc) := <- channel.write =>
buf[offset:] = data;
wc <- (count, nil);
}アプリケーションプロセスが"/usr/oraccha/test.file"をreadした場合はchannel.readが、writeした場合はchannel.writeがタプルを返す。rcとwcはsrvデバイスとのチャネルでデータはarray of byteでやり取りされる。タプルの2番目の要素はエラー文字列で、エラーがないときはnilを渡す。
追記:なんて書いたけど、Styxのメッセージをもっとダイレクトにハンドリングする手段もあるみたい。ramfileとmemfsという似たようなコマンドがあるけど、前者はfile2chanを使っていて、後者はstyxlibモジュールを使っている。