Nikdy jsem opravdu nepřemýšlel o tom, jak Shell ve skutečnosti provádí příkazy z potrubí. Vždy mi bylo řečeno, že "stdout jednoho programu dostane piped do stdin jiného", jako způsob přemýšlení o dýmkách. Takže jsem si přirozeně myslel, že v případě řekněme, že A | B
, A
bude spuštěn jako první, pak B
získá stdout A
a použije stdout A
jako jeho vstup.
Všiml jsem si však, že když lidé hledají konkrétní proces v ps
, zahrnuli by na konci příkazu grep -v "grep"
, Aby se ujistili, že grep
neobsahuje objeví se v konečném výstupu.
To znamená, že v příkazu ps aux | grep "bash" | grep -v "grep"
Se předpokládá, že ps
věděl, že grep
běží, a je tedy ve výstupu ps
. Ale pokud ps
skončí běh před tím, než se jeho výstup dostane do grep
, jak to bylo vědět, že grep
běží?
[email protected]: ~$ ps | grep ".*"
PID TTY TIME CMD
3773 pts/0 00:00:00 bash
3784 pts/0 00:00:00 ps
3785 pts/0 00:00:00 grep
Potrubní příkazy běží souběžně. Když spustíte ps | grep …
, je to štěstí losování (nebo záležitost detailů fungování Shell v kombinaci s plánovačem jemného doladění hluboko v útrobách jádra), zda ps
nebo grep
začíná nejprve a v každém případě pokračují v provádění souběžně.
To se velmi běžně používá k tomu, aby druhý program mohl zpracovat data tak, jak vychází z prvního programu, dříve, než první program dokončí svou činnost. Například
grep pattern very-large-file | tr a-z A-Z
začne zobrazovat odpovídající řádky velkými písmeny ještě předtím, než grep
dokončí procházení velkého souboru.
grep pattern very-large-file | head -n 1
zobrazí první odpovídající řádek a může ukončit zpracování dobře dříve, než grep
dokončí čtení vstupního souboru.
Pokud čtete někde, kde jsou programy spuštěny v sekvenci, unikněte tomuto dokumentu. Piped programy běží souběžně a vždy mají.
Pořadí, ve kterém jsou příkazy spouštěny, ve skutečnosti nezáleží a není zaručeno. Ponecháme-li stranou tajemné detaily pipe()
, fork()
, dup()
a execve()
, Shell nejprve vytvoří potrubí, potrubí pro data který bude probíhat mezi procesy a poté vytvoří procesy s konci potrubí, které jsou k nim připojeny. První spuštěný proces může blokovat čekání na vstup z druhého procesu nebo blokovat čekání na druhý proces, aby se začalo číst data z potrubí. Tyto čekání mohou být libovolně dlouhé a na tom nezáleží. Ať už jsou procesy spuštěny, data se nakonec přenesou a vše funguje.
Zdá se, že existuje riziko, že porazí mrtvého koně
A | B
je ekvivalentní
A > dočasný_souborB < dočasný_soubor rm dočasný_soubor
Ale v době, kdy byl Unix vytvořen a děti jezdily do školy dinosaury, byly disky velmi malé a pro poměrně benigní příkaz bylo běžné využívat všechen volný prostor v systému souborů. Pokud B
bylo něco jako grep some_very_obscure_string
, konečný výstup potrubí může být mnohem menší než tento mezilehlý soubor. Trubka proto byla vyvinuta, nikoli jako zkratka pro „run A , a poté spusťte B se vstupem z A výstupního modelu, ale jako způsob pro B
provádět souběžně s A
a eliminujte potřebu ukládání přechodného souboru na disk.
Obvykle to spustíte pod bash. proces pracuje a začíná souběžně, ale běží Shell paralelně. Jak je to možné?
systém nezaručuje, jak bude provedeno rychlé spuštění a spustí zadaný příkaz. je nezávislý na Shell, ale na systému. To je proto, že:
ps auxww| grep ps | cat
jednou zobrazí příkaz grep
a/nebo ps
a další nyní. Závisí to na tom, jak rychlé jádro skutečně spouští procesy pomocí funkce spuštění systému.