為什麼“BrokenPipeError”取決於管道流的大小?
以下腳本
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 試圖寫的東西比它在管道中的容量多,它最終會不可避免地得到一個
EPIPE
orSIGPIPE
,不管它有多少時間:>>> 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 +++