it-swarm-eu.dev

V jakém pořadí se spouští piped příkazy?

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
97
action_potato

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.

52
Kyle Jones

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.

32
Scott

Obvykle to spustíte pod bash. proces pracuje a začíná souběžně, ale běží Shell paralelně. Jak je to možné?

  1. pokud to není poslední příkaz v potrubí, vytvořte nejmenovaný kanál s párem soketů
  2. fork
  3. v dítěti přiřaďte stdin/stdout do soketů, pokud je to potřeba (pro první proces v potrubí není stdin znovu přiřazen, totéž pro poslední proces a jeho stdout)
  4. v podřízeném příkazu EXEC s argumenty, které vymetou původní kód Shell, ale ponechají všechny otevřené jimi sokety. ID podřízeného procesu se nezmění, protože se jedná o stejný podřízený proces
  5. souběžně s dítětem, ale paralelně pod hlavním Shellem, přejděte ke kroku 1.

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.

1
Znik