为什么 `BrokenPipeError` 依赖于管道流的大小?

为什么 `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。

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

相关内容