Waarom is `BrokenPipeError` afhankelijk van de grootte van de doorgesluisde stroom?
Geplaatst op februari 15, 2021 door admin Het volgende script roept BrokenPipeError: [Errno 32] Broken pipe
op, wanneer doorgesluisd naar een commando zoals head
(tenzij het aantal regels naar kop groter is dan het aantal regels dat door het Python-script wordt afgedrukt).
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
Mijn begrip (van dit antwoord en de antwoorden op deze vraag ) is dat er een fout optreedt als een pipe wordt gesloten voordat het Python-proces is voltooid schrijven.
Als ik echter het herhaalde bereik met één verlaag tot 16385, wordt de fout niet verhoogd (niet zeker of deze drempel hetzelfde is op alle machines, dus probeer gewoon een hoog en laag nummer te reproduceren). Ik dacht aanvankelijk dat dit te maken had met de grootte van de pijpbuffer, maar dat is 64K voor mij (volgens M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999
, dus dat lijkt niet de reden te zijn.
Waarom is het voorkomen BrokenPipeError
afhankelijk van de grootte van wat er wordt doorgesluisd?
Dit is met Python 3.8.1 op Linux 5.4.15-arch1 -1.
Opmerkingen
Answer
Waarom is het voorkomenBrokenPipeError afhankelijk van de grootte van wat wordt doorgesluisd?
Omdat het schrijven van meer dingen meer tijd kost, en de rechterkant van de pijplijn kan sterven voordat je python klaar is met schrijven. Als de python probeert meer te schrijven dan er in de pijpbuffer past, zal hij blokkeren en de head -1
ruimschoots de tijd geven om af te sluiten.
Aangezien de head -1
duurt enige tijd om te leven en te sterven, de python kan die tijd gebruiken om al zijn spullen te schrijven – als het in de pijpbuffer past – en succesvol afsluiten. Dat is moeilijk te voorspellen, aangezien de kernel vrij is om beide kanten van de pijplijn in willekeurige volgorde te plannen, en het starten van de head -1
kan vertragen zolang het nodig acht, of het kan stop het op elk moment.
>>> 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!
Maar als Python probeert meer dingen te schrijven dan het in de pijp past, krijgt het onverbiddelijk een EPIPE
of SIGPIPE
uiteindelijk, ongeacht hoeveel tijd het heeft:
>>> 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
maar dat is 64K voor mij … dus dat lijkt “niet de reden te zijn.
Onthoud dat Python volledige buffering gebruikt wanneer de uitvoer geen terminal is; het schrijft niet “regel voor regel of byte voor byte, maar met brokken van een aantal grootte:
>>> 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 +++
:
commando te gebruiken in plaats van hethead -1
commando.python3 -c "for i in range(10000): print(i)" | : </dev/tcp/unix.stackexchange.com/https
. Als je verdubbelt / verdrievoudigt het bereik, de python met mislukt met Broken Pipe.