Richard Stevensが書いた「Unixネットワークプログラミング」という本を勉強している間、クライアントとサーバー間のFIFOの使用について説明する次の行が見つかりました。
クライアントプロセスが開始され、書き込みのためにFIFOを開き、要求を作成してから終了します。クライアントプロセスが終了するたびに、読み取り操作はデーモンに0を返します。その後、デーモンはFIFOを再度開いて(読み取り専用)、クライアントプロセスが書き込みを行うためにFIFOを開くまで待つ必要があります。
最後の行がわかりません。サーバープロセスがFIFOを再度開く必要があるのはなぜですか?クライアントプロセスが書き込んだ後にもう一度読んでください。そうですか?
ベストアンサー1
サーバープロセスがFIFOを再度開く必要があるのはなぜですか?クライアントプロセスが書き込んだ後にもう一度読んでください。そうですか?
面白いですね。あなたの提案を試してみましょう。次の結果は、Linux 4.9.0-6-amd64(Ubuntu Linuxカーネル)で作成されました。
$ mkfifo t
$ (cat; cat) < t & # run a "server" as a background job
[1] 4856
$ echo 1 > t
1
[1]+ Done ( cat; cat ) < t
私たちが望むように動作しませんでした。最初はcat
期待どおりにEOFを読み取り、終了します。問題は2番目です。cat
返品すぐにEOFを読むと、「サーバー」が完了します。それは言わない待つ新しいクライアントの場合(read()を繰り返し呼び出してCPU時間を無駄にする必要はありません)。
シェルでファイル記述子(FD)を操作する方法がわかっている場合は、確認に役立つ別の方法があります。
$ echo 1 > t &
$ exec 3 < t # open "t" for reading, as FD 3.
$ cat <&3
1
[1]+ Done echo 1 > t
$ cat <&3
$
$ echo 2 > t &
[1] 5102
$ cat <&3
2
[1]+ Done echo 2 > t
$ cat <&3
$
答えは、open()
FIFOをリセットする目的は、他の人が書くためにFIFOを開くのを防ぐことです。このステップがないと、read()
fifoへのすべての後続の呼び出しはすぐに0(EOF)を返します。
これを見つけたとき、私はsystemd-initctl
それがどのように機能するのか疑問に思いました。このプログラムはsystemdの下の古いfifoをエミュレートします/dev/initctl
。 (免責事項:テストするのは簡単ではありません。実行方法を文書化しません。)答えは、systemd-initctlが読み取りと書き込みのためにFIFOを開くことです。 (技術的には、systemdはsystemd-initctl.socketで指定されているようにfifoを開き、それをsystemd-initctlに渡します。)同時読み取りと書き込みのためにfifoを開くことはLinux専用の機能です。しかし、これを行うことで、systemdはStevensが次に述べたのと同じトリックを実装します。
これを防ぐための有用な技術は、デーモンがFIFOを2回(読み取り用に1回、書き込み用に1回)開くことです。読み取り用に返されたファイル記述子は読み取りクライアント要求に使用されますが、書き込みに使用されるファイル記述子は決して使用されません。書き込みのために常にFIFOを開いたままにすると(デーモンが存在する限り)、読み取りはEOFを返さずに次のクライアント要求を待ちます。