`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

내 이해 (from이 답변그리고이 질문에 대한 대답)은 Python 프로세스가 쓰기를 완료하기 전에 파이프가 닫히면 오류가 발생한다는 것입니다.

그러나 반복 범위를 1에서 16385로 줄이면 오류가 발생하지 않습니다(이 임계값이 모든 시스템에서 동일한지 확실하지 않으므로 재현하려면 더 높거나 낮은 숫자를 시도해 보십시오). 나는 처음에 이것이 파이프 버퍼 크기와 관련이 있다고 생각했지만 나에게는 64K였습니다( M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999.so에 따르면 그것은 원인이 아닌 것 같습니다).

BrokenPipeError왜 파이프 크기에 따라 상황이 달라지나요 ?

이는 Linux 5.4.15-arch1-1의 Python 3.8.1입니다.

답변1

파이프 크기에 따라 BrokenPipeError가 발생하는 이유는 무엇입니까?

더 많은 내용을 작성하는 데 더 많은 시간이 걸리고 Python 작성이 완료되기 전에 파이프의 오른쪽이 죽을 수 있기 때문입니다. 또한, 파이썬이 파이프 버퍼가 보유할 수 있는 것보다 더 많이 쓰려고 하면 차단되어 head -1종료할 수 있는 충분한 시간을 줍니다.

살고 죽는 데는 시간이 걸리기 때문에 head -1Python은 그 시간을 사용하여 모든 것을 쓰고(파이프 버퍼에 맞는 경우) 성공적으로 종료할 수 있습니다. 커널은 파이프라인의 양쪽을 어떤 순서로든 자유롭게 예약할 수 있고 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입니다 ... 그래서 그것이 원인이 아닌 것 같습니다.

파이썬이 사용하고 있다는 것을 기억하십시오완전히 버퍼링됨출력이 터미널이 아닌 경우 한 줄씩 또는 바이트별로 기록되지 않고 청크로 기록됩니다.일부크기:

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

관련 정보