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モジュールを使っている。