web-dev-qa-db-ja.com

`BrokenPipeError`がパイプされたストリームのサイズに依存するのはなぜですか?

次のスクリプトは、headのようなコマンドにパイプすると、_BrokenPipeError: [Errno 32] Broken pipe_を発生させます(先頭の行数がPythonスクリプトによって出力される行数を超えない限り) 。

_for i in range(16386):
    print("")
_
_$ python test-pipe.py | head -1

Traceback (most recent call last):
  File "test-pipe.py", line 2, in <module>
    print("")
BrokenPipeError: [Errno 32] Broken pipe
_

私の理解( この答え および この質問への答え から)は、Pythonの前にパイプが閉じられるとエラーが発生するということですプロセスはそれに書き込みを終了しました。

ただし、反復範囲を1から16385でデクリメントすると、エラーは発生しません(このしきい値がすべてのマシンで同じかどうかわからないため、再現するために高い数値と低い数値を試してみてください)。最初はこれがパイプバッファーサイズに関連している可能性があると思いましたが、私にとっては64Kです(M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999によると、それは理由ではないようです。

オカレンスBrokenPipeErrorがパイプされるもののサイズに依存するのはなぜですか?

これは、Python 3.8.1 on Linux 5.4.15-Arch1-1で)です。

1
joelostblom

オカレンスブロークンパイプエラーは、パイプされるもののサイズに依存するのはなぜですか?

より多くのものを書き込むには時間がかかり、パイプラインの右側がpythonが書き込みを完了する前に停止する可能性があります。また、pythonパイプバッファーに収まらないほど書き込むと、ブロックされ、head -1が終了するのに十分な時間が与えられます。

head -1は、生存して死ぬまでに時間がかかるため、pythonは、パイプバッファーに収まる場合、その時間を使用してすべてのものを書き込み、正常に終了します。カーネルはパイプラインの両側を任意の順序で自由にスケジュールでき、適切と思われる限りhead -1の開始を遅らせたり、いつでも停止したりする可能性があるため、これは予測が困難です。

>>> python3 -c 'for i in range(50000): print("")' | sleep .01
Traceback (most recent call last):
  File "<string>", line 1, in <module>
BrokenPipeError: [Errno 32] Broken pipe

>>> python3 -c 'for i in range(50000): print("")' | sleep .1

# OK!

しかし、pythonがパイプに収まらないほど多くのものを書き込もうとした場合、最終的にEPIPEまたはSIGPIPEを容赦なく取得しますそれが持っている時間:

>>> python3 -c 'for i in range(100000): print("")' | sleep 20
Traceback (most recent call last):
  File "<string>", line 1, in <module>
BrokenPipeError: [Errno 32] Broken pipe

しかし、それは私にとって64Kです...それが理由ではないようです。

出力が端末でない場合、pythonはフルバッファリングを使用しています)。行単位またはバイト単位ではなく、チャンク単位で書き込みます。 someサイズ:

>>> strace -s3 -e trace=write python3 -c 'for i in range(50000): print("")' | sleep .3
write(1, "\n\n\n"..., 8193)             = 8193
write(1, "\n\n\n"..., 8193)             = 8193
write(1, "\n\n\n"..., 8193)             = 8193
write(1, "\n\n\n"..., 8193)             = 8193
write(1, "\n\n\n"..., 8193)             = 8193
write(1, "\n\n\n"..., 8193)             = 8193
write(1, "\n\n\n"..., 842)              = 842
+++ exited with 0 +++
1
pizdelect