Comprensione dei comandi convogliati in Unix / Linux


16

Ho due semplici programmi: Ae B. Averrebbe eseguito per primo, quindi Bottiene lo "stdout" diA e lo userà come "stdin". Supponiamo che sto usando un sistema operativo GNU / Linux e il modo più semplice possibile per farlo sarebbe:

./A | ./B

Se dovessi descrivere questo comando, direi che è un comando che accetta input (ovvero, legge) da un produttore ( A) e scrive a un consumatore ( B). È una descrizione corretta? Mi sto perdendo qualcosa?



Non è un comando, è un oggetto kenerl creato dal processo bash, che viene utilizzato come stdout del processo A e stdin come B. Due processi vengono avviati quasi contemporaneamente.
炸鱼 薯条 德里克

1
@ 炸鱼 Hai ragione - per la pipeline del kernel è un oggetto nel filesystem pipefs, ma per quanto riguarda la shell stessa - tecnicamente si tratta di un comando della pipeline
Sergiy Kolodyazhnyy

Risposte:


26

L'unica cosa della tua domanda che si distingue come sbagliata è che dici

A verrebbe per primo, poi B otterrà lo stdout di A

In effetti, entrambi i programmi sarebbero stati avviati praticamente nello stesso momento. Se non c'è input per Bquando tenta di leggere, si bloccherà fino a quando non ci sarà input da leggere. Allo stesso modo, se non c'è nessuno che legge l'output daA , le sue scritture si bloccheranno fino a quando non viene letto l'output (parte di esso verrà bufferizzato dalla pipe).

L'unica cosa che sincronizza i processi che prendono parte a una pipeline è l'I / O, ovvero la lettura e la scrittura attraverso la pipa. Se non si verifica alcuna scrittura o lettura, i due processi verranno eseguiti in modo totalmente indipendente l'uno dall'altro. Se uno ignora la lettura o la scrittura dell'altro, il processo ignorato si bloccherà e alla fine verrà interrotto da un SIGPIPEsegnale (se in scrittura) o otterrà una condizione di fine file sul suo flusso di input standard (se in lettura) quando l'altro processo termina .

Il modo idiomatico di descrivere A | Bè che è una pipeline contenente due programmi. L'output prodotto sull'output standard dal primo programma è disponibile per essere letto sull'input standard dal secondo ("[l'output di] Aviene convogliato in [input di] B"). La shell esegue l'impianto idraulico necessario per consentire che ciò accada.

Se vuoi usare le parole "consumatore" e "produttore", suppongo che vada bene anche questo.

Il fatto che si tratti di programmi scritti in C non è rilevante. Il fatto che si tratti di Linux, macOS, OpenBSD o AIX non è rilevante.


2
La scrittura su un file temporaneo è stata utilizzata in DOS, poiché non supportava più processi.
CSM

2
@AlexVong Nota però che il tuo esempio con un file temporaneo non è esattamente equivalente. Un programma può scegliere di cercare attraverso il contenuto di un file, ma i dati provenienti da una pipe non sono ricercabili. Un esame migliore sarebbe utilizzare mkfifoper creare una pipe denominata, quindi avviare B in background leggendo dalla pipe e quindi A scrivendoci. Questo è un pignolo, tuttavia, poiché l' effetto sarebbe lo stesso.
Kusalananda

2
@AlexVong Le semplificazioni apportate in quell'articolo lo separano da vere condutture; l'esecuzione parallela è veramente semantica, non un'ottimizzazione. È una spiegazione ragionevole da parte dei bambini della valutazione o della composizione monadica per qualcuno che ha visto pipeline di gusci, ma non è valido nella direzione opposta. La quindicesima versione di Kusalananda è più vicina, ma le parti di propagazione degli errori del modello sono veramente importanti e non replicabili. (tutto ciò che dico come qualcuno che è molto sul treno "I gasdotti della shell sono solo composizione di funzioni")
Michael Homer,

6
@AlexVong No, è completamente fuori strada. Ciò non è in grado di spiegare nemmeno qualcosa di semplice come yes | sed 10q
zio Billy,

1
@UncleBilly Sono d'accordo con il tuo esempio. Ciò dimostra che l'esecuzione parallela è davvero richiesta anche da Michael. Altrimenti, non riceveremo la risoluzione.
Alex Vong,

2

Il termine solitamente usato nella documentazione è "pipeline", che consiste in uno o più comandi, vedi definizione POSIX Quindi tecnicamente parlando, ci sono due comandi che hai lì, due sottoprocessi per la shell (ofork()+exec() comandi esterni o subshells)

Per quanto riguarda la parte produttore-consumatore , la pipeline può essere descritta da quel modello, poiché:

  • Produttore e consumatore condividono buffer di dimensioni fisse e, almeno su Linux e MacOS X, esistono dimensioni fisse per il buffer della pipeline
  • Il produttore e il consumatore sono accoppiati liberamente, i comandi nella pipeline non conoscono l'esistenza reciproca (a meno che non stiano controllando attivamente la /proc/<pid>/fddirectory).
  • I produttori scrivono stdoute i consumatori leggono stdincome se fossero un singolo comando in esecuzione, ovvero possono esistere l'uno senza l'altro .

La differenza che vedo qui è che a differenza del Produttore-Consumatore in altre lingue, i comandi di shell usano il buffering e scrivono stdout una volta riempito il buffer, ma non c'è menzione che il Produttore-Consumatore deve seguire quella regola - attendere solo quando la coda è piena o scartata dati (che è qualcos'altro che la pipeline non fa).

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.