In quale ordine vengono eseguiti i comandi piped?


89

Non ho mai veramente pensato a come la shell esegua effettivamente i comandi piped. Mi è sempre stato detto che lo "stdout di un programma viene convogliato nello stdin di un altro", come un modo di pensare alle pipe. Quindi, naturalmente, ho pensato che, nel caso di dire, A | B, A verrebbe eseguito per primo, quindi B ottiene lo stdout di A e utilizza lo stdout di A come input.

Ma ho notato che quando le persone cercano un particolare processo in ps, includerebbero grep -v "grep" alla fine del comando per assicurarsi che grep non compaia nell'output finale. Ciò significa che nel comando ps aux | grep "bash" | grep -v "grep", il che significa che ps sapeva che grep era in esecuzione e quindi è nell'output di ps. Ma se ps termina l'esecuzione prima che il suo output venga convogliato su grep, come fa a sapere che grep era in esecuzione?

flamingtoast@FTOAST-UBUNTU: ~$ 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

perché non accettare una risposta?
törzsmókus,

Risposte:


64

I comandi con pipe vengono eseguiti contemporaneamente. Quando corri ps | grep …, è la fortuna del sorteggio (o una questione di dettagli del funzionamento della shell combinato con lo scheduler che affina le viscere in profondità nelle viscere del kernel) se pso grepinizia prima, e in ogni caso continuano a eseguire contemporaneamente.

Questo è molto comunemente usato per consentire al secondo programma di elaborare i dati quando escono dal primo programma, prima che il primo programma abbia completato il suo funzionamento. Per esempio

grep pattern very-large-file | tr a-z A-Z

inizia a visualizzare le righe corrispondenti in maiuscolo anche prima di grepaver attraversato il file di grandi dimensioni.

grep pattern very-large-file | head -n 1

visualizza la prima riga corrispondente e potrebbe interrompere l'elaborazione molto prima che grepabbia finito di leggere il suo file di input.

Se leggi da qualche parte che i programmi in pipe vengono eseguiti in sequenza, fuggi da questo documento. I programmi piped vengono eseguiti contemporaneamente e lo sono sempre.


7
E la cosa bella di questo esempio è che quando head ottiene l'unica riga di cui ha bisogno, termina e quando grep lo nota, termina anche senza fare un sacco di lavoro ulteriore per niente.
Joe,

Immagino che ci sia una sorta di buffer I / O relativo alla pipe ... come faccio a sapere che è una dimensione in byte? Cosa voglio leggere per saperne di più? :)
n611x007

3
@naxa In realtà ci sono due buffer. C'è il buffer stdio all'interno del grepprogramma e c'è un buffer gestito dal kernel nella pipe stessa. Per quest'ultimo, vedi Quanto è grande il buffer del tubo?
Gilles,

49

L'ordine in cui vengono eseguiti i comandi non ha importanza e non è garantito. Tralasciando i dettagli oscuri di pipe(), fork(), dup()e execve(), il guscio prima crea il tubo, il condotto per i dati che fluirà tra i processi, e quindi crea i processi con le estremità del tubo collegato ad essi. Il primo processo eseguito può bloccare l'attesa dell'input dal secondo processo o bloccare l'attesa del secondo processo per avviare la lettura dei dati dalla pipe. Queste attese possono essere arbitrariamente lunghe e non contano. Indipendentemente dall'ordine in cui vengono eseguiti i processi, i dati vengono infine trasferiti e tutto funziona.


5
Bella risposta, ma l'OP sembra pensare che i processi vengano eseguiti in sequenza. Potresti chiarire qui che i processi vengono eseguiti contemporaneamente e che il tubo è come ... un tubo tra i secchi, dove l'acqua scorre attraverso tutto allo stesso tempo (circa).
Keith,

Grazie per il chiarimento. Le fonti che ho letto mi hanno fatto sembrare che i programmi in pipe funzionassero in sequenza, piuttosto che contemporaneamente.
action_potato

Per vedere l'esperienza dei processi che iniziano in modo indeterminato prova a eseguire questo 1000 volte: echo -na> & 2 | echo b> & 2
Ole Tange,

28

A rischio di battere un cavallo morto, l'idea sbagliata sembra essere quella

    A | B

è equivalente a

    A > temporary_file 
    B < temporary_file 
    rm temporary_file

Ma, quando fu creato Unix e i bambini cavalcavano i dinosauri a scuola, i dischi erano molto piccoli, ed era comune per un comando piuttosto benigno consumare tutto lo spazio libero in un file system. Se Bfosse qualcosa del genere , l'output finale della pipeline potrebbe essere molto più piccolo di quel file intermedio. Pertanto, il tubo è stato sviluppato, non come abbreviazione di “run A prima, e quindi eseguire B con il contributo di A s’ uscita”modello, ma come un modo per eseguire contemporaneamente con ed eliminare la necessità di memorizzare il file intermedio su disco.grep some_very_obscure_stringBA


2
Questo risponde perché e quindi ottiene il mio voto.
Piccola foresta antica Kami

1

In genere si esegue questo sotto bash. processo in corso e avvio simultaneo, ma in esecuzione dalla shell in parallelo. Come è possibile?

  1. se non è l'ultimo comando in pipe, crea pipe senza nome con coppia di socket
  2. forchetta
  3. in child riassegnare stdin / stdout ai socket se è necessario (per il primo processo in pipe stdin non viene riassegnato, lo stesso per l'ultimo processo e il suo stdout)
  4. nel figlio EXEC ha specificato il comando con argomenti che spazzano via il codice shell originale, ma lascia tutto aperto dai socket. L'ID del processo figlio non verrà modificato poiché si tratta dello stesso processo figlio
  5. in concomitanza con figlio ma parallelo nella shell principale vai al passaggio 1.

il sistema non garantisce la velocità con cui verrà eseguito exec e verrà avviato il comando specificato. è indipendente dalla shell, ma dal sistema. Questo è perché:

ps auxww| grep ps | cat

una volta mostra grepe / o pscomando, e il prossimo ora. Dipende dalla velocità con cui il kernel avvia realmente i processi usando la funzione exec di sistema.


1
Esecuzione simultanea significa che due o più processi vengono eseguiti nello stesso lasso di tempo, di solito con una sorta di dipendenza tra di loro. L' esecuzione parallela significa che due o più processi vengono eseguiti contemporaneamente (ad es. Su core CPU separati contemporaneamente). Il parallelismo non è rilevante per la domanda, né "quanto velocemente" exec()viene eseguito, ma come exec()vengono intercalate le chiamate e l'esecuzione dei programmi in una pipe .
Thomas Nyman,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.