Come funzionano le pipe in Linux


25

Ho letto come vengono implementate le pipe nel kernel di Linux e ho voluto confermare la mia comprensione. Se non sono corretto, verrà selezionata la risposta con la spiegazione corretta.

  • Linux ha un VFS chiamato pipefs che è montato nel kernel (non nello spazio utente)
  • pipefs ha un singolo super blocco ed è montato sulla sua radice ( pipe:), a fianco/
  • pipefs non può essere visualizzato direttamente a differenza della maggior parte dei file system
  • L'accesso a pipefs avviene tramite pipe(2)syscall
  • Il pipe(2)syscall utilizzato dalle shell per |eseguire il piping con l' operatore (o manualmente da qualsiasi altro processo) crea un nuovo file in pipefs che si comporta praticamente come un normale file
  • Il file sul lato sinistro dell'operatore pipe viene stdoutreindirizzato al file temporaneo creato in pipefs
  • Il file sul lato destro dell'operatore pipe è stdinimpostato sul file su pipefs
  • pipefs è archiviato in memoria e attraverso qualche magia del kernel, non dovrebbe essere impaginato

Questa spiegazione di come funzionano i tubi (ad es. ls -la | less) È praticamente corretta?

Una cosa che non capisco è come qualcosa come bash imposterà un processo ' stdino stdoutil descrittore di file restituito da pipe(2). Non sono ancora riuscito a trovare nulla al riguardo.


Nota che stai parlando di due strati di cose considerevolmente diversi con lo stesso nome. La pipe()chiamata del kernel insieme al macchinario che lo supporta ( pipefs, ecc.) È di livello molto inferiore rispetto |all'operatore offerto nella shell. Il secondo è di solito implementato usando il primo, ma non è necessario.
Greg Hewgill il

Sì, mi riferisco in particolare alle operazioni di livello inferiore, supponendo che l' |operatore stia semplicemente chiamando pipe(2)un processo come fa bash.
Brandon Wamboldt,

Risposte:


19

La tua analisi finora è generalmente corretta. Il modo in cui una shell potrebbe impostare lo stdin di un processo su un descrittore pipe potrebbe essere (pseudocodice):

pipe(p) // create a new pipe with two handles p[0] and p[1]
fork() // spawn a child process
    close(p[0]) // close the write end of the pipe in the child
    dup2(p[1], 0) // duplicate the pipe descriptor on top of fd 0 (stdin)
    close(p[1]) // close the other pipe descriptor
    exec() // run a new process with the new descriptors in place

Grazie! dup2Sei solo curioso di sapere perché la chiamata è necessaria e non puoi semplicemente assegnare direttamente il descrittore di pipe a stdin?
Brandon Wamboldt,

3
Il chiamante non può scegliere quale sia il valore numerico del descrittore di file quando viene creato pipe(). La dup2()chiamata consente al chiamante di copiare il descrittore di file su un valore numerico specifico (necessario perché 0, 1, 2 sono stdin, stdout, stderr). Questo è l'equivalente del kernel di "assegnare direttamente a stdin". Notare che la variabile globale della libreria runtime C stdinè a FILE *, che non è correlata al kernel (sebbene sia inizializzata per essere connessa al descrittore 0).
Greg Hewgill,

Bella risposta! Sono un po 'perso nei dettagli. Ti stai solo chiedendo perché chiudi (p [1]) prima di eseguire exec ()? Quando dup2 ritorna, p [1] punta a fd 0? Quindi close (p [1]) chiude il descrittore di file 0. Quindi come possiamo leggere dallo stdin del processo figlio?
user1559897

@ user1559897: la dup2chiamata non cambia p[1]. Invece, crea i due handle p[1]e 0punta allo stesso oggetto kernel (la pipe). Dal momento che il processo figlio non ha bisogno di due handle stdin (e non saprebbe quale sia l'handle numerato che p[1]è comunque), p[1]viene chiuso prima exec.
Greg Hewgill,

@GregHewgill Gotchu. Grazie!
user1559897
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.