FIFO(名前付きパイプ)は通常のパイプ(|)とどのように異なりますか? Wikipedia から理解できるように、通常のパイプとは異なり、FIFOプロセスが終了した後、パイプは「存続」し、後で削除できます。
しかし、プロセスがパイプ(cat x | grep y
)を含むシェルコマンドに基づいている場合、変数またはファイルに格納すると、「プロセスの後でそれを維持する」ことができます。 ?
また、通常のパイプには別のコマンドのstdinとして取得する最初のstdoutもあるので、これも一種の先入れ先出しパイプではありませんか?
「名前付きパイプ」は、実際にはveryという正確な名前です。名前が(ファイルシステム)。
パイプ— _some-command | grep pattern
_で使用される通常の名前のない(「匿名」)パイプは、特別な種類のファイルです。そして、私はファイルを意味します。他のすべてのファイルを実行するのと同じように、ファイルを読み書きします。 Grepは、端末や通常のファイルではなく、パイプから読み取ることを実際には気にしません。
技術的には、舞台裏で起こっていることは、stdin、stdout、およびstderrが、すべてのコマンド実行に渡される3つのオープンファイル(ファイル記述子)であるということです。ファイル記述子(読み取り/書き込み/ etc。ファイルへのすべてのsyscallで使用される)は単なる数値です。 stdin、stdout、およびstderrはファイル記述子0、1、および2です。したがって、シェルが_some-command | grep
_を設定すると、次のようになります。
カーネルに匿名パイプを要求します。名前がないため、これは通常のファイルのようにopen
では実行できません。代わりに、2つのファイル記述子を返すpipe
または_pipe2
_で実行されます。⁴
子プロセスをフォークして(fork()
は親プロセスのコピーを作成します。ここではパイプの両側が開いています)、パイプの書き込み側をfd 1(stdout)にコピーします。カーネルには、ファイル記述子番号をコピーするためのsyscallがあります。 dup2()
またはdup3()
です。次に、読み取り側と書き込み側の他のコピーを閉じます。最後に、execve
を使用して_some-command
_を実行します。パイプはfd 1なので、_some-command
_のstdoutがパイプです。
別の子プロセスのフォーク。今回は、パイプの読み取り側をfd 0(stdin)に複製し、grep
を実行します。そのため、grepはパイプからstdinとして読み取ります。
次に、それらの子の両方が終了するのを待ちます。
この時点で、カーネルはパイプが開いていないことに気づき、ガーベッジがそれを収集します。それが実際にパイプを破壊するものです。
名前付きパイプは、ファイルシステムに配置することで、匿名パイプに名前を付けます。そのため、anyプロセスは、将来の任意の時点で、通常のopen
syscallを使用してパイプのファイル記述子を取得できます。概念的には、すべてのリーダー/ライターがパイプを閉じ、ファイルシステムからunlink
edされるまで、パイプは破棄されません。²
ちなみに、これは一般にUnixでファイルが機能する方法です。 unlink
(rm
の背後にあるsyscall)は、ファイルの名前の1つを削除するだけです。すべての名前が削除され、ファイルが開かれていない場合にのみ、実際に削除されます。ここでいくつかの答えがこれを探ります:
脚注
grep pattern
_と入力すると、grep
がターミナルから読み取られます。これが頭に浮かぶのは、ターミナルに何かを貼り付ける場合だけです。パイプラインのシェル構文と基礎となるUnixシステムプログラミングの間で混乱していると思います。パイプ/ FIFOは、ディスクに保存されないタイプのファイルですが、代わりにカーネルのバッファーを介してライターからリーダーにデータを渡します。
パイプ/ FIFOは、ライターとリーダーがopen("/path/to/named_pipe", O_WRONLY);
のようなシステムコールを実行するか、または pipe(2)
を使用して接続され、新しい匿名パイプを作成するかどうかに関係なく同じように機能します。開いているファイル記述子を読み取り側と書き込み側の両方に返します。
fstat(2)
パイプファイル記述子で、いずれかの方法でsb.st_mode & S_IFMT == S_IFIFO
を提供します。
foo | bar
を実行すると:
pipe(2)
システムコールを実行して、匿名パイプの入力と出力の2つのファイル記述子を取得します。fork()
は0を返しました)stdout
をdup2(pipefd[1], 1)
で書き込みfdにリダイレクトしますexecve("/usr/bin/foo", ...)
を実行しますfork()
は0以外の子PIDを返しました)stdin
をdup2(pipefd[0], 0)
で読み取りfdからリダイレクトしますexecve("/usr/bin/bar", ...)
を実行しますfoo > named_pipe & bar < named_pipe
を実行すると、非常によく似た状況になります。
名前付きパイプは、プロセス間でパイプを確立するためのランデブーです。
状況は、匿名のtmpファイルと名前の付いたファイルに似ています。 O_TMPFILE
で"/path/to/dir/tmpfile"
を開いてリンクを解除し、リンクを解除した場合と同じように、open("/path/to/dir", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
を使用して名前のない一時ファイルを作成できます(O_CREAT
)。削除されたファイルへのファイル記述子。
O_TMPFILE
で作成された場合、linkat
を使用して、その匿名ファイルをファイルシステムにリンクし、名前を付けることもできます。 ( ただし、名前を付けて作成したファイルをLinuxで削除してから削除することはできません。 )