FIFO(名前付きパイプ)は通常のパイプ(|)とどのように異なりますか?私が知る限りウィキペディア通常のパイプとは異なり、FIFO パイプはプロセス終了後も「有効」になり、後で削除できます。
しかし、プロセスがcat x | grep y
パイプ()を含むシェルコマンドに基づいている場合、それを変数またはファイルに保存すると、「プロセス後もアクティブなまま」になりますが、このFIFOではありませんか?
また、通常のパイプにも最初に取得した標準出力は、他のコマンドの標準入力として使用されます。では、先入選出パイプラインではないでしょうか?
答え1
「名前付きパイプ」は実際に非常に正確な名前 - ファイルシステムに名前があることを除いて、通常のパイプと同じです。
パイプ - 使用される名前が指定されていない汎用(「匿名」)パイプはsome-command | grep pattern
特別な種類のファイルです。私は他のファイルと同様に読み書き可能なファイルに言及しています。 Grepは、ターミナル3や通常のファイルではなくパイプから読み取るという事実に全く気にしません。
技術的に、後で起こるのは、stdin、stdout、およびstderrが各コマンド実行に渡される3つのオープンファイル(ファイル記述子)です。ファイル記述子(ファイルの読み取り/書き込みなどのすべてのシステムコールに使用されます)は数字だけです。 stdin、stdout、stderr はファイル記述子 0、1、2 です。したがって、シェルがsome-command | grep
これを設定したら、次の操作を行います。
カーネルから匿名パイプを要求します。名前がないため、通常のファイルのように完了できません。代わりに、
open
2つのファイル記述子を返すorを使用して完了します。 ⁴pipe
pipe2
子プロセスをフォークして(
fork()
親プロセスのコピーを作成します。パイプの両側がここで開きます)、パイプの書き込み端をfd 1(stdout)にコピーします。カーネルにはファイル記述子番号をコピーするためのシステムコールがあります。 yesdup2()
またはdup3()
。次に、読取り側と書込み側の他のレプリカを閉じます。最後にexecve
実行に使用されますsome-command
。パイプがfd 1なので、stdoutsome-command
はパイプです。他のサブプロセスのフォークです。今回は、パイプの読み取り端をfd 0(stdin)にコピーして実行します
grep
。したがって、grepはパイプからstdinに読み込まれます。それから二人の子供が出るのを待ちます。
この時点で、カーネルはパイプがもはや開いていないことを認識し、それをガベージコレクションします。これが実際にパイプラインを壊すことです。
名前付きパイプは単にファイルシステムに入れて、匿名パイプに名前を付けます。だから今どのプロセスは通常のシステムコールを使用して、将来のいつでもパイプのファイル記述子を取得できますopen
。概念的には、unlink
すべてのリーダー/ライターがパイプを閉じてファイルシステムから削除するまで、パイプは破壊されません。 ²
ちなみに、これは通常Unixでファイルが動作する方法です。unlink
(次のシステムコールrm
)は単にファイル名の1つを削除します。すべての名前が削除され、開いているファイルがない場合にのみ実際に削除されます。以下は、これを探求するいくつかの答えです。
- ハードリンクが元のリンクと同じスペースを占めるように見えるのはなぜですか?
- ロガーが削除されたファイルを記録し続けるにはどうすればよいですか?
- ファイルが現在使用中であるため、Windowsで苦情を示すファイルを削除/置換できるように、Linuxにはどのような違いがありますか?
脚注
- 技術的にはこれは本当ではないかもしれません。理解すると、いくつかの最適化が可能になり、実際のgrep実装はしばしば大幅に最適化されます。しかし、概念的には関係ありません(実際にはgrepを直接実装することも同様です)。
- もちろん、カーネルは実際にはすべてのデータ構造をメモリに永久に保持するのではなく、最初のプログラムが名前付きパイプを開くたびにそれを透過的に再生成します(その後、開いている間はその構造を保持します)。だから彼らは長い間周りにいたようです。
- ターミナルは、grepが読む一般的な場所ではありませんが、他のターミナルを指定しないと、デフォルトの標準入力になります。したがって、
grep pattern
シェルに入力するだけでgrep
端末から読み込まれます。私が考えることができる唯一の用途は、端末に何かを貼り付けたい場合です。 - Linuxでは、匿名パイプは実際に特別なファイルシステム(pipefs)に作成されます。バラよりLinuxでパイプがどのように機能するかもっと学ぶ。これはLinuxの内部実装の詳細です。
答え2
私の考えでは、パイプのシェル構文と基本的なUnixシステムプログラミングを混同しているようです。 Pipe / 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を返します)- パイプの読み取り端を閉じます(書き込みfdを開いたままにします)。
stdout
fdを書き込むようにリダイレクトします。dup2(pipefd[1], 1)
- だから
execve("/usr/bin/foo", ...)
- 親(
fork()
ゼロ以外の子PIDを返します)- パイプの書き込み端を閉じます(読み取りfdを開いたままにします)。
stdin
fd読み取りからリダイレクトdup2(pipefd[0], 0)
- だから
execve("/usr/bin/bar", ...)
走ると非常によく似た状況に直面しますfoo > named_pipe & bar < named_pipe
。
名前付きパイプは、プロセス間パイプを設定するためのランデブーポイントです。
この状況は、匿名tmpファイルと名前付きファイルの場合と似ています。open("/path/to/dir", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
一時ファイルを作成できます名前なし(O_TMPFILE
)、"/path/to/dir/tmpfile"
ファイルを開いO_CREAT
て接続を解除し、削除されたファイルのファイル記述子を残すのと同じです。
.linkat
O_TMPFILE
ただし、Linuxでは、特定の名前で作成された後に削除されたファイルを使用してこれを行うことはできません。)