私はさまざまな記事やSO=の質問に出くわしましたが、私が日常的に使用しているものについてはまだ混乱していますが、それがどれほど混乱するか理解していません。パイプ(名前付き)を実験しています。 Linuxで。
1st試してみるのは簡単でした:パイプバッファーがどのように機能しているかを理解します
#1
mkfifo /tmp/mypipe
#2
echo "Hello World" >/tmp/mypipe
ctrl+c
#3
cat /tmp/mypipe
Observation:echo
がデータを読み取る前にcat
を強制終了すると、パイプには何も書き込まれませんでした(cat
は実行を続けますが、パイプから何も読み取られませんでした)。 producent >named_pipe
と入力してproducent
を終了すると、パイプバッファーのサイズに一致するデータの一部がnamed_pipe
に書き込まれ、それが読み取られるまでここに残ると想定していましたconsument
(これがどのように機能するかではないことがわかりました)。それで私が次にしたことは:
2ndconsument
をパイプの他端に接続しようとしました:
#1
mkfifo /tmp/mypipe
#2
echo "Hello World" >/tmp/mypipe
#3
cat /tmp/mypipe
観測:cat
コマンドは"Hello World"
メッセージを表示し、両方のプロセスが終了します。ここでの興味深い発見は、#2ステップの間、ps -elf
がecho
コマンドを表示しないことでした。 echo
は、誰かがパイプから読み取るまで待機しているようです。これが、最初の試行でパイプに何も出力されなかった理由の説明です。
rd「永久に」実行され、常にパイプに書き込み、何が起こるかを確認するコマンドをパイプすることを試みました。
#1
mkfifo /tmp/mypipe
#2
yes >/tmp/mypipe
#3
cat /tmp/mypipe
観測:これは期待どおりに機能し、cat
はyes
がパイプに転送したものを出力しました。ただし、cat
をtail -f
に置き換えようとしました。私がこれを行ったとき、tail
はyes
コマンドが強制終了されるまで何も出力しませんでした。
4番目試してみることは大きな謎です:
# 1#
mkfifo /tmp/mypipe
# 2#
for i in $(seq 1 10000); do echo -n $i"|"> /tmp/mypipe; done
# 3#
for i in $(seq 1 10); do echo "${i}# Read:"; cat /tmp/mypipe && echo ""; done
この後、3#コマンドは次のような入力を開始します。
1# Read:
1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|
2# Read:
109|
3# Read:
110|
4# Read:
111|
5# Read:
112|
6# Read:
113|114|115|
7# Read:
116|
8# Read:
117|
9# Read:
118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255|256|257|258|259|260|261|262|263|264|265|266|267|268|269|270|271|272|273|274|275|276|277|278|279|280|281|282|283|284|285|286|287|288|289|290|291|292|293|294|295|
10# Read:
296|297|298|299|300|301|302|303|304|305|306|307|308|309|310|311|312|313|314|315|316|317|318|319|320|321|322|323|324|325|326|327|328|329|
質問:
1回目と2回目の試行:
|
パイプと同等ですか。この特定のケースではbashから?Linuxは、コンシューマーがパイプに接続されていること、つまり通信がいつ発生するかをどのように知るのでしょうか。 lsof named_pipe
を試しましたが、何も表示されません。この情報はどこに保存されていますか?私もフォローしてみましたが、結果はcat
がパイプから読み取れませんでした。
#1
mkfifo /tmp/mypipe
#2
echo 1 >/tmp/mypipe
#3
rm /tmp/mypipe
#4
mkfifo /tmp/mypipe
#5
cat /tmp/mypipe
入力中:producent >/tmp/mypipe
入力と同じcommand |
あるコマンドを別のコマンドにパイプしたいが、パイプの後に別のコマンドを入力し忘れた場合(ps
この場合も最初に表示しないcommand
)?
回目の試行:
cat
とtail -f
の違いは何ですか?4回目の試行:
ここで何が起こっているのですか?読み取りデータのチャンクが正確なサイズではないのはなぜですか?私は出力を次のように期待していました:
1#読み取り:1 | 2#読み取り:2 | 3#読み取り:3 |
PS:また、コマンドを開始する順序(最初に読み取るものと後で書き込むもの)を変えてみましたが、結果は同じでした。
PPS:これが明確であることを願っていますが、プロデューサー=パイプに書き込むプロセスです。コンシューマー=パイプから読み取るプロセス。
これは、ほとんどCでスクリプトの知識を持っている人に説明できますか?どうもありがとうございました。
返信への返信:Joe Sewell
どちらも並行して実行されること、つまり次の2つは同じではないことを理解しています。
find | less
対
find > /tmp/file && less /tmp/file
さらに観察したところ、次のコマンドを実行すると、HDDが機能していないことがわかりました。これは、less
コマンドに表示するのに十分なデータが含まれるまで停止しているようです。
find | less
shifg+g
を押すと(less
のファイルの終わりに移動します)、HDDがすぐに動作を開始し、データの出力が始まります。これは、less
コマンドに表示するのに十分なデータがある場合、それ以上のデータを生成しないようにfind
に何らかの形で指示することを意味しますか?これが同期の意味ですか?また、パイプへのデータ書き込み量はそのバッファサイズに対応していますか? find
でps aux
を押した後、less
が状態(S+ to D+
-stat列)をshift+g
から変更することにも気付きました
S interruptible sleep (waiting for an event to complete)
D uninterruptible sleep (usually IO)
+ is in the foreground process group.
┌─[wakatana@~] [63 files, 178Mb]
└──> ps aux | egrep -w 'less|find'
wakatana 6071 0.0 0.0 12736 1088 pts/5 S+ 23:15 0:00 find
wakatana 6072 0.0 0.0 7940 928 pts/5 S+ 23:15 0:00 less
wakatana 6183 0.0 0.0 7832 892 pts/6 S+ 23:20 0:00 egrep --color=auto -w less|find
┌─[wakatana@~] [63 files, 178Mb]
└──> ps aux | egrep -w 'less|find'
wakatana 6071 0.0 0.0 12808 1304 pts/5 D+ 23:15 0:00 find
wakatana 6072 0.0 0.0 9556 2508 pts/5 S+ 23:15 0:00 less
wakatana 6193 0.0 0.0 7832 892 pts/6 S+ 23:21 0:00 egrep --color=auto -w less|find
誰がこの信号を送信しますか?はいの場合、消費者は彼がすでに生産物を持っているパイプに接続されていることをどのように知っていますか(たとえば、rmパイプの私の例)?
OKクリア
OKクリア
新しいラインは私を混乱させるケースではないと思います。私の以前の観察に基づいています(そしてあなたは「はい、両端はお互いを待っています」と確認しました)。私はこれを期待していました:
I. 1番目のループの最初の反復はパイプに書き込みます。誰も読み取っていないため、ここで待機します。
II。 2番目のループが発行されると、1回目の反復で1番目のループによって書き込まれたデータが読み取られます。ここにはこれ以上何も書き込まれないため、これ以上読み取ることはできません。
III。 2番目のループは、次のデータが1番目のループによって書き込まれるのを待つか、(順序に関係なく)1番目のループは、書き込まれたデータが2番目のループによって読み取られるまで待機します。
このため、1回の書き込みが1回の読み取りに対応することを期待していました。また、ループが実行されていないかどうかを確認していたので、少し元のコマンドを変更して、コンシューマーが読み取らない場合でも何かがSTDOUTに出力されるかどうかを確認しましたが、何も出力されませんでした。
for i in $(seq 1 10000); do
if [ $(( $i % 5 )) -eq 0 ]; then
echo $i;
else
echo -n $i"|"> /tmp/mypipe;
fi;
done
「書き込みプロセスでは改行が送信されないので、読者は「十分だ」と言われるまで読むだけです。
「最初のケースでは、おそらくFIFOのバッファがいっぱいになったためです。」
「したがって、読者にフラッシュされました。」
「通信を非同期にする方法はありますが...」
質問リストに番号で回答するには:
名前付きパイプ、別名fifosは、基本的に、シェルによって生成された名前なしパイプと同等です。大きな違いは、シェルバージョンでは両端間の同期は直感的ですが、名前付きパイプを使用しているように見えるため、シェルが何をしているかについて少し知識が必要です。
はい、両端はお互いを待ちます。シェルパイプのようなFIFOの目的は、あるプロセスからの出力を別のプロセスの入力に渡すことです。それらは一時ファイルではありません。あなたが混乱しているのはここだと思います。 cat somefile.txt | less
のようなシェルコマンドの場合、bothコマンドはフォークされたプロセスとして同時に実行され、パイプは2つを同期する役割を果たします。これは、メモリが機能する場合、Cで変更できますが、シェルコマンドでは簡単には変更できません。
プロセスは、パイプのもう一方の端が接続されたときにシグナルを受信できますが、多くの場合、上記のように、2つのプロセスの同期を維持することが目的です。ライターは何かを送信し、書き込み操作が完了したときにそれを続行できることを認識しています。
bash
とtcsh
は、パイプを「忘却の中に」入れさせません。コマンドは実行されません。
tail -f
は、EOFを取得するまで、この場合はstdin
でストリーム全体を読み取ってから、何かを表示する必要があります。あなたの実験では、終わりは決して現れませんでした。一方、cat
は、入力の処理をすぐに開始できます。
書き込みプロセスは改行を送信しないため、リーダーは「十分」であると通知されるまで単に読み取ります。最初のケースでは、おそらくFIFOのバッファーがいっぱいになり、リーダーにフラッシュされたことが原因です。後続の出力はおそらく似ており、システムのタイミングに基づいて異なる可能性があります。
ここでもう少し混乱を取り上げましょう。シェルは、コマンドが実行される前にリダイレクトを処理します。つまり、プロセスのリストにcat
が表示されないのは、bash
がFIFOのもう一方の端が接続するのを待ってスタックしているためですbeforecat
または実行中のライターを実行します。同様に、ライターが接続されるまで、読み取りコマンドは実行されません。
ここでの最大の誤解は、名前付きパイプが一時ファイルではないことです。名前のないパイプもありません。通信を非同期にする方法はいくつかありますが、doしない限り、/tmp
の下に実際の一時ファイルを作成する方がよいようです。両方のプロセスを同時に実行したい。