ネットワークのファイルへの抽象化
Plan9というオペレーティングシステムでは,Plan9はソケットインタフェースを採用せず,ネットワークに対しても,通常のファイルに対するインタフェース,つまりopen-read-write-closeを使って操作すると言った.
UNIXには/devディレクトリ以下にデバイスファイルが存在するように,Plan9では/net以下がネットワークに対する名前空間(ファイルシステムのツリーを想像してもらえればよいが,Plan9の場合,プロセスごとに名前空間を持つことができる)になる.UNIXのソケットは,プログラムからファイル記述子を介して操作できるが,専用のAPIを使う必要があり,ファイルシステム上のファイルとして見えるわけではない.つまり,ローカルホスト上の資源とリモートホスト上の資源を扱う場合に,異なる作法に乗っ取ったプログラミングが必要になる.Plan9では,ネットワーク透過に資源を扱うために,ネットワークに対するファイルへの抽象化を拡張している.
では,Plan9からLinux上で動作するエコーサーバにアクセスする例によって,具体的に見ていこう.なお,Plan9側ではCでプログラムを書かずに,シェルスクリプトだけで通信してみる.
Linux側のエコーサーバ
今時のLinuxだと,エコーサーバなんか動いていないだろうから,Perlで次のようなプログラムを書いてみた.LinuxマシンのIPアドレスは12.34.56.78とする.
#!/usr/bin/env perl use IO::Socket; $s = new IO::Socket::INET(Listen => 5, LocalAddr => '12.34.56.78', LocalPort => 9000, Proto => 'tcp', Reuse => 1); die "IO::Socket : $!" unless $s; while (1) { $sock = $s->accept(); print "accept!\n"; while (<$sock>) { print $sock $_; } close($sock) } close($s); exit;
rcでエコークライアント
Plan9のシェルはrcである.
まず,/net/tcp/cloneというファイルをcatして,その出力を覚えておこう.
cpu% cat /net/tcp/clone 5
ここからが本番で,/net/tcp/cloneに"connect Linuxマシンのアドレス!ポート番号"を書き込む.このcatプロセスが死ぬとコネクションが切れてしまうので,注意.
cpu% cat > /net/tcp/clone connect 12.34.56.78!9000
そして,別のターミナルから次のように実行してみよう."5"は最初に実行した"cat /net/tcp/clone"の出力結果と読み替えてほしい.
cpu% echo hoge > /net/tcp/5/data cpu% cat /net/tcp/5/data hoge
echoとcatだけでネットワークプログラミング(?)ができてしまった.
これはUNIXには真似できない芸当だよね.
後始末(シグナルではなくてノート)
このままだとcatは入力を待ち続けて,終了しない.ctl-Dでも終了しない.なぜだろう?
ということでcatをkillする.killもUNIXとはちょっと違っている.killの引数はコマンド名で(pkillに近いね),しかも,killコマンドが本当にプロセスを殺すのではなく,"echo..."という出力を返す.
cpu% kill cat echo kill>/proc/68725/note
この出力をパイプでrcに食わせることでプロセスを殺すことができる.
cpu% kill cat | rc
つまり,/proc/
ちょっと裏側を解説
閑話休題.ネットワークの話に戻ろう.基本的には,/net/tcp/*/ctlがコントロール用のファイル,/net/tcp/*/dataがデータ通信用のファイルになる./net/tcp/*以下を見ると,ctlとdata以外にも次のようなファイルが存在することがわかる.
%cpu ls /net/tcp/5 /net/tcp/5/ctl /net/tcp/5/data /net/tcp/5/err /net/tcp/5/listen /net/tcp/5/local /net/tcp/5/remote /net/tcp/5/status
そして,新たにコネクションを張るために,cloneファイルと呼ばれるファイルが存在する*1.TCPの場合は,/net/tcp/cloneがそれである./net/tcp/cloneをopenすると,新たなインタフェースが割り当てられ,そのctlファイルのファイル記述子が返される.つまり,先の"cat > /net/tcp/clone"は,/net/tcp/*/ctlへリダイレクトされる.そして,connectメッセージにより,コネクションが確立し,/net/tcp/*/dataを介して,データのやり取りが可能になる.