web-dev-qa-db-ja.com

名前付きパイプ、ファイル記述子、およびEOF

Bashプロンプトが表示された、同じユーザーの2つのウィンドウ。ウィンドウ1で次のように入力します。

$ mkfifo f; exec <f

そのため、bashは名前付きパイプfにマップされているファイル記述子0から読み取ろうとしています。ウィンドウ2で次のように入力します。

$ echo ls > f

ここで、window-1はlsを出力し、シェルは終了します。どうして?

次の実験:exec <fでwindow-1をもう一度開きます。ウィンドウ2で次のように入力します。

$ exec 3>f
$ echo ls >&3

上記の最初の行の後、window-1はウェイクアップし、プロンプトを出力します。どうして?上記の2行目以降、window-1はls出力を出力し、シェルは存続します。どうして?実際、現在window-2では、echo ls > fはwindow-1シェルを閉じません。

答えは、名前付きパイプを参照するwindow-2からのファイル記述子3のexistenceに関係する必要がありますか?!

10
Fixee

これは、ファイル記述子のclosingに関係しています。

最初の例では、echoはシェルがfと接続するために開く標準出力ストリームに書き込み、終了すると記述子が閉じられます(シェルによって)。受信側では、標準入力ストリーム(fに接続されている)から入力を読み取るシェルがlsを読み取り、lsを実行してから、エンドオブエンドのために終了します。 -標準入力のファイル条件。

名前付きパイプへのすべてのライター(この例では1つのみ)がパイプの終わりを閉じたため、ファイルの終わり条件が発生します。

2番目の例では、exec 3>ffに書き込むためにファイル記述子3を開き、次にecholsに書き込みます。 echoコマンドではなく、ファイル記述子が開かれたのはシェルです。 exec 3>&-を実行するまで、記述子は開いたままになります。受信側では、標準入力ストリーム(fに接続されている)から入力を読み取るシェルがlsを読み取り、lsを実行して、さらに入力を待機します(ストリームはまだ開いています)。

ストリームへのすべての書き込み(exec 3>fおよびechoを介したシェル)がnotでパイプの終わりを閉じているため、ストリームは開いたままです(exec 3>f isまだ有効です)。


上記のechoについては、まるで外部コマンドであるかのように書いています。ほとんどの場合、シェルに組み込まれています。それでも効果は同じです。

12
Kusalananda

それほど多くはありません:パイプへのライターがない場合、それはリーダーに対して閉じているように見えます。つまり、EOFが返されたときに返され、開かれたときにブロックされます。

Linuxのmanページから( pipe(7) 、参照 fifo(7) ):

パイプの書き込み終了を参照するすべてのファイル記述子が閉じている場合、パイプからread(2)を実行しようとすると、ファイルの終わりが表示されます(read(2)は0を返します)。

書き込み終了を閉じると、暗黙的にecho ls >f、そしてあなたが言うように、他の場合では、ファイル記述子は開いたままです。

6
ilkkachu

@Kusalanandaと@ikkachuの2つの回答を読んだら、理解できたと思います。ウィンドウ1で、シェルはパイプの書き込み側を開いて閉じるための何かを待機しています。書き込み終了が開かれると、ウィンドウ1のシェルはプロンプトを出力します。書き込みが終了すると、シェルはEOFを取得して終了します。

Window-2側では、私の質問で説明した2つの状況があります。echo ls > fの最初の状況では、ファイル記述子3がないため、echoが生成され、そのstdinstdoutは次のようになります。

0 --> tty
1 --> f

その後、echoは終了し、シェルは両方の記述子を閉じます。ファイル記述子1が閉じており、fを参照しているため、fの書き込み側が閉じているため、EOFがwindow-1になります。

2番目の状況では、シェルでexec 3>fを実行し、シェルに次の環境を適用させます。

bash:
0 --> tty
1 --> tty
2 --> tty
3 --> f

ここでecho ls >& 3を実行し、シェルは次のようにechoのファイル記述子を割り当てます。

echo:
0 --> tty
1 --> f     # because 3 points to f
2 --> tty

次に、シェルはfを含む上記の3つの記述子を閉じますが、fにはシェル自体からの参照がまだ残っています。これが重要な違いです。 exec 3>&-で記述子3を閉じると、最後に開いた参照が閉じ、@ Kusalanandaが述べたようにEOFをwindow-1に引き起こします。

2
Fixee