Varför är ”BrokenPipeError” beroende av storleken på den rörledda strömmen?
On februari 15, 2021 by admin Följande skript höjer BrokenPipeError: [Errno 32] Broken pipe
, när det skickas till ett kommando som head
(såvida inte antalet rader till huvudet överstiger antalet rader som skrivs ut av Python-skriptet.
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
Min förståelse (från detta svar och svarar på denna fråga ) är att ett fel uppstår om ett rör stängs innan Python-processen är klar skriver till det.
Men om jag minskar det itererade intervallet med en till 16385 höjs inte felet (inte säker på om denna tröskel är densamma på alla maskiner så kanske bara prova ett högt och lågt nummer att reproducera). Jag trodde inledningsvis att detta kan relateras till rörbuffertstorleken, men det är 64K för mig (enligt M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999
, så det verkar inte vara anledningen.
Varför är förekomsten BrokenPipeError
beroende av storleken på vad som pipas?
Detta är med Python 3.8.1 på Linux 5.4.15-arch1 -1.
Kommentarer
Svar
Varför är förekomstenBrokenPipeError beroende av storleken på det som rörs?
Eftersom att skriva fler saker tar mer tid och att den högra sidan av rörledningen kan dö innan din python har skrivit det. Om pythonen försöker skriva mer än den passar i rörbufferten kommer den att blockera och ge head -1
tillräckligt med tid att avsluta.
Eftersom head -1
tar lite tid att leva och dö, pythonen kan använda den tiden för att skriva alla sina grejer – om den passar i rörbufferten – och avsluta framgångsrikt. Det är svårt att förutsäga, eftersom kärnan är fri att schemalägga båda sidor av rörledningen i valfri ordning, och kan fördröja start av head -1
så länge den finner lämpligt, eller så kan det stoppa det när som helst.
>>> 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!
Men om python försöker skriva fler saker än det passar i röret, kommer det oförlåtligt att få en EPIPE
eller SIGPIPE
i slutändan, oavsett hur mycket tid det har:
>>> 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
men det är 64K för mig … så det verkar inte vara orsaken.
Tänk på att python använder full buffring när utdata inte är en terminal; det skriver inte rad för rad eller byte för byte, utan av bitar av någon storlek:
>>> 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 +++
:
istället förhead -1
-kommandot.python3 -c "for i in range(10000): print(i)" | : </dev/tcp/unix.stackexchange.com/https
. Om du dubblar / tredubblar intervallet, pythonen med fail med Broken Pipe.