
パイプ経由で bash コマンドを連鎖すると、何か象徴的なことが起こるのでしょうか、それともすべて compute-pass-compute-pass なのでしょうか?
たとえばhead t.txt -n 5 | tail -n 2
、 では、 がhead t.txt -n 5
計算されてtail -n 2
から実行されます。それとも、最初に何らかの抽象化が行われ、シェルに 3 行目から 5 行目が読み取られることを指示するのでしょうか? この例では違いはないかもしれませんが、他のシナリオでは違いがあると思います。
答え1
シェルはpipe(2)
システム コールを使用して、カーネル内に 2 つのファイル記述子を持つ境界付きバッファーを作成します。1 つはプロセスがバッファーに書き込むためのもので、もう 1 つはプロセスがバッファーから読み取るためのものです。
簡単なケースを考えてみましょう。
$ p1 | p2
この場合、概念的には、シェルは前述のパイプfork()
s を作成し、子プロセスは標準出力ストリームをパイプの書き込み側に接続し、次に子プロセスexec()
s に接続しますp1
。次に、シェルはfork()
再び s を作成し、子プロセスは標準入力ストリームをパイプの読み取り側に接続し、次に子プロセスexec()
sに接続しますp2
。(概念的にシェルは異なる順序で処理を実行する可能性がありますが、考え方は同じです。
その時点で、p1
と はp2
同時に実行されています。 p1
はパイプに書き込み、カーネルは書き込まれたデータをバッファにコピーします。 は p2
パイプから読み取り、カーネルは読み取られたデータをバッファからコピーします。 パイプがいっぱいになると、カーネルはパイプから何かを読み取ってスペースを解放するまで、p1
の呼び出しをブロックします。 パイプが空の場合、カーネルはパイプにさらにデータを書き込むまで、の呼び出しをブロックします。write()
p2
p2
read()
p1
答え2
あなたが提案した 2 つのモデルのうち、compute-pass-compute-pass が最も近いものです。シェルはプロセスを接続するだけです。プロセスが何をしているかは知りません。
を除外する実行順序は未定義です。これらは実質的に同時に実行されます。ただし、左側のコマンドは、右側のコマンドがバイトを入力する前にバイトを出力する必要があります。データは左から右に流れます。データは最初のコマンドから標準出力に流れ、次のプロセスの標準入力に流れ込み、そこで処理され、標準出力から出力され、別のプロセスにパイプされるなど、繰り返し実行されます。
>
リダイレクトや、、などがない場合<
や、ファイルからの読み込みの場合は、次のようになります。
┌───────────┐ ┌───────────┐ ┌─────────────┐
Terminal⇨│Process one│⇨│Process two│⇨│Process Three│⇨Terminal
└───────────┘ └───────────┘ └─────────────┘