Python

為什麼“BrokenPipeError”取決於管道流的大小?

  • February 5, 2020

以下腳本BrokenPipeError: [Errno 32] Broken pipe在通過管道傳遞到類似命令時會引發head(除非要開頭的行數超過 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取決於管道的大小?

這適用於 Linux 5.4.15-arch1-1 上的 Python 3.8.1。

為什麼發生BrokenPipeError 取決於管道的大小?

因為寫更多的東西需要更多的時間,而且在你的 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 試圖寫的東西比它在管道中的容量多,它最終會不可避免地得到一個EPIPEor 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 正在使用完全緩衝;它不是逐行或逐字節寫入,而是按一定大小的塊寫入:

>>> 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 +++

引用自:https://unix.stackexchange.com/questions/565744