私はシェルが実際にパイプされたコマンドをどのように実行するのか考えたことがありません。私はいつも「プログラムの標準出力は管路パイプについて考える方法で他の人のstdin"に入れます。したがって、当然このケースが最初に実行され、次のA | B
stdoutを取得し、stdoutを入力として使用すると思いました。 A
B
A
A
しかし、私は人々が特定のプロセスを検索したときにそのプロセスが最終出力に表示されないようにするために、コマンドの最後にプロセスを含めることをps
発見しました。これは、命令が実行中であり、したがって出力に含まれるという暗黙の知識が命令にあることを意味します。しかし、出力がパイプに接続される前に完了したら実行されているかどうかはどうすればわかりますか?grep -v "grep"
grep
ps aux | grep "bash" | grep -v "grep"
ps
grep
ps
ps
grep
grep
flamingtoast@FTOAST-UBUNTU: ~$ ps | grep ".*"
PID TTY TIME CMD
3773 pts/0 00:00:00 bash
3784 pts/0 00:00:00 ps
3785 pts/0 00:00:00 grep
答え1
パイプラインコマンドが同時に実行されます。実行時に最初に開始するかどうかは、ps | grep …
運の問題(またはシェル操作の詳細とカーネル内部スケジューラの微調整の問題)によって異なり、いずれにせよ開始されます。同時に実行されます。ps
grep
これは、最初のプログラムがタスクを完了する前に、2番目のプログラムが最初のプログラムのデータを処理できるようにするために非常に一般的です。例えば
grep pattern very-large-file | tr a-z A-Z
grep
大容量ファイルの検索が完了する前に、一致する行を大文字で表示し始めます。
grep pattern very-large-file | head -n 1
grep
一致する最初の行を表示し、入力ファイルの読み取りが完了する前に処理を停止できます。
パイプラインプログラムが順次実行されることを読んだ場合は、この記事を離れてください。パイプラインプログラムは常に同時に実行されます。
答え2
コマンドが実行される順序は実際には重要ではなく、保証されません。pipe()
、、、fork()
およびdup()
の難解な詳細は取り除き、execve()
シェルは最初にプロセス間でデータが流れるパイプであるパイプを作成し、パイプの各端がプロセスに関連付けられたプロセスを作成します。実行中の最初のプロセスは、2番目のプロセスの入力を待つことをブロックするか、2番目のプロセスがパイプからデータの読み取りを開始するのを待つことをブロックできます。これらの待ち時間はランダムに長くなる可能性がありますが、問題ではありません。プロセスがどの順序で実行されても、最終的にデータは送信され、すべてがうまく機能します。
答え3
死んだ馬の危険にさらされた誤解は次のとおりです。
ㅏ|第二
等しい
ㅏ>一時ファイル 第二<一時ファイル RM一時ファイル
しかし、Unixが作成されたとき、子供たちは恐竜に乗って学校に行っていましたが、ディスクが小さすぎてかなりまともなコマンドがファイルシステムの空き容量を使い果たすことが多かったです。その場合、B
パイプラインの最終出力は次のようになります。grep some_very_obscure_string
たくさん中間ファイルより小さい。したがって、パイプライン開発は「実行」を減らして使用しません。ㅏ最初に実行してから第二入力は以下で提供されます。ㅏこれは「出力」モデルですが、B
実行を並列化しA
て中間ファイルをディスクに保存する必要がないようにする方法です。
答え4
あなたは注文について質問しました。それはランダムではありません(Gilesが彼の答えで話そうとしていたように)。
ps -ef
次にパイプされるコマンドは次のとおりですgrep
。
$ ps -ef | grep .
...
alexis 37188 55443 0 20:17 pts/4 00:00:00 ps -ef
alexis 37189 55443 0 20:17 pts/4 00:00:00 grep --color=auto .
...
注:問題に重要ではない他のすべてのプロセスを出力から削除しました。
ご覧のとおり、出力ps -ef
にはaとaがあります。grep --color=auto .
今質問に答えることができますか?
はい。このps
コマンドのPIDは37,188で、grep
このコマンドのPIDは37,189です。明らかに左から右に作成され、どのシェルもこの操作を異なる方法で実行しないでください。
技術的には、Cでは次のようにパイプを作成します。pipe(2)
この関数は2つのファイル記述子を提供します。 1つstdout
はで使用され、もう1つはでps
使用されます。を始める前にファイル記述子を予約するのは簡単です。stdin
grep
stdin
ps
また、次のシステム構成を見てください。
$ getconf -a | grep PIPE_BUF
PIPE_BUF 4096
_POSIX_PIPE_BUF 4096
これら2つのパラメータは、パイプの最小保証サイズ(バイト単位)を定義します。 Linux 2.6以降、デフォルトのサイズは64Kbです。また、絶対最大バイト数は次のように定義されます。
$ cat /proc/sys/fs/pipe-max-size
1048576
これが1Mbであることがわかります。パイプがいっぱいになると、出力器(ps
最初の例では)はパイプの反対側のプロセスがデータ(grep
最初の例では)を読み取るまでブロックされます。
つまり、出力はps
パイプサイズよりはるかに小さいため、次のようになります。
$ ps -ef | wc
1132 10819 121435
(つまり、現在のマイコンピュータの出力は約120Kbです...)
配管がまったく詰まらない。
1Mbを超えるストリーミングデータの場合、ある時点でブロックされます。grep
すぐに起動しないと、最初のコマンドの呼び出しがブロックされるため、絶対に開始されませんwrite()
。
したがって、プロセスは非常に迅速に連続的に開始されますが、ほとんどの場合並列に実行されます(または単一のプロセッサがある場合は同時に)。つまり、ps
コマンドが最初に終了します。これはパイプを「完了」(EOF
データを読み取るときに信号を受け取る)としてマークし、これが次のツールがパイプが完了したことを知る方法であり、受信した最後の数バイトを処理するとパイプも死にます。
逆に、パイプの右側のプロセスが早く終了すると(左側のプロセスがパイプへの書き込みを完了する前に)、左側のプロセスはパイプに書き込もうとするとすぐにシグナルを受け取りSIGPIPE
ます。これは、パイプラインのプロセスが終了したときにパイプラインもすばやく終了するようにするために行われます。