Named pipe, descrittori di file ed EOF


10

Due finestre, stesso utente, con istruzioni bash. Nella finestra-1 digitare:

$ mkfifo f; exec <f

Quindi ora bash sta tentando di leggere dal descrittore di file 0, che è mappato sulla pipe denominata f. Nella finestra-2 digitare:

$ echo ls > f

Ora window-1 stampa un s e poi il guscio muore. Perché?

Esperimento successivo: apri di nuovo window-1 con exec <f. Nella finestra-2 digitare:

$ exec 3>f
$ echo ls >&3

Dopo la prima riga sopra, window-1 si sveglia e stampa un prompt. Perché? Dopo la seconda riga sopra, window-1 stampa l' lsoutput e la shell rimane in vita. Perché? Infatti, ora in window-2, echo ls > fnon si chiude la shell window-1.

La risposta deve avere a che fare con l' esistenza del descrittore di file 3 da window-2 che fa riferimento alla named pipe ?!


1
Dopo exec <f, bashnon sta tentando di leggere da f, sta prima tentando di aprirlo . Il open()comando non ritorna fino a quando non viene eseguito un processo in un'altra modalità di scrittura sulla pipe (a quel punto la pipe verrà istanziata e la shell leggerà l'input da essa).
Stéphane Chazelas,

Ottimo punto, @ StéphaneChazelas. Questo deve essere il motivo per cui, una volta exec 3>feseguita, la prima shell fornisce un prompt. (Punto minore, volevi dire "in modalità di scrittura " nel tuo commento?)
Fixee,

1
sì scusa. Modificato subito prima della scadenza dei 5 minuti
Stéphane Chazelas,

Risposte:


12

Ha a che fare con la chiusura del descrittore di file.

Nel tuo primo esempio, echoscrive nel suo flusso di output standard che la shell apre per collegarlo fe, quando termina, il suo descrittore viene chiuso (dalla shell). All'estremità ricevente, la shell, che legge l'input dal suo flusso di input standard (collegato a f) legge ls, viene eseguita lse quindi termina a causa della condizione di fine del file sull'input standard.

La condizione di fine del file si verifica perché tutti gli autori della pipe denominata (solo una in questo esempio) hanno chiuso la loro estremità della pipe.

Nel tuo secondo esempio, exec 3>fapre il descrittore di file 3 per la scrittura f, quindi echoscrive lssu di esso. È la shell che ora ha il descrittore di file aperto, non il echocomando. Il descrittore rimane aperto fino a quando non lo fai exec 3>&-. All'estremità ricevente, la shell, che legge l'input dal suo flusso di input standard (collegato a f) legge ls, viene eseguita lse quindi attende ulteriori input (poiché lo stream è ancora aperto).

Il flusso rimane aperto perché tutti gli scrittori (la shell, via exec 3>fe echo) non hanno chiuso la loro estremità della pipe ( exec 3>fè ancora in vigore).


Ho scritto echosopra come se fosse un comando esterno. È molto probabile che sia incorporato nella shell. L'effetto è lo stesso comunque.


6

Non c'è molto da fare: quando non ci sono scrittori nella pipe, sembra chiuso ai lettori, cioè restituisce EOF quando viene letto e si blocca quando viene aperto.

Dalla pagina man di Linux ( pipe(7), ma vedi anche fifo(7)):

Se tutti i descrittori di file che si riferiscono all'estremità di scrittura di una pipe sono stati chiusi, un tentativo read(2)dalla pipe vedrà la fine del file ( read(2)restituirà 0).

La chiusura della fine di scrittura è ciò che accade implicitamente alla fine di the echo ls >f, e come dici tu, nell'altro caso, il descrittore di file è tenuto aperto.


Sembra analogo ai conteggi di riferimento in Java (e in altri linguaggi OO)! Ha senso però.
Fixee,

2

Dopo aver letto le due risposte di @Kusalananda e @ikkachu, penso di aver capito. In window-1, la shell è in attesa di qualcosa per aprire sia la fine di scrittura della pipe che per chiuderla. Una volta aperta la fine della scrittura, la shell in window-1 stampa un prompt. Una volta chiusa la fine della scrittura, la shell ottiene EOF e muore.

Sul lato finestra-2 abbiamo le due situazioni descritte nella mia domanda: nella prima situazione con echo ls > f, non esiste un descrittore di file 3, quindi abbiamo echospawning, ed è stdine stdoutassomiglia a questo:

0 --> tty
1 --> f

Quindi echotermina e la shell chiude entrambi i descrittori. Poiché il descrittore di file 1 è chiuso e riferimenti f, la fine di scrittura di fè chiusa e ciò provoca un EOF a window-1.

Nella seconda situazione, corriamo exec 3>fnella nostra shell, facendo sì che la shell prenda questo ambiente:

bash:
0 --> tty
1 --> tty
2 --> tty
3 --> f

Ora eseguiamo echo ls >& 3e la shell alloca i descrittori di file echocome segue:

echo:
0 --> tty
1 --> f     # because 3 points to f
2 --> tty

Quindi la shell chiude i tre descrittori sopra, incluso f, ma fha ancora un riferimento ad essa dalla shell stessa. Questa è la differenza importante. La chiusura del descrittore 3 con exec 3>&-chiuderebbe l'ultimo riferimento aperto e causerebbe un EOF a window-1, come notato da @Kusalananda.


Questo è un buon esempio del motivo per cui dovresti lasciare da solo i primi tre descrittori di file a meno che non ci siano buoni motivi di progettazione per modificarli. Quando hai usato il descrittore (1) che è finito per essere il descrittore di input (0) nell'altra shell, non solo hai chiuso la pipe (e quello che stavi facendo con quel particolare flusso di dati), ma hai anche chiuso l'input per il secondo shell che ha causato la sua chiusura. Questo va bene, ma solo se lo stai facendo apposta. L'uso di descrittori di file con numero più elevato evita effetti collaterali come questo perché nulla si aspetta che siano in uno stato particolare o addirittura definiti.
Joe,

Ad essere sincero, non sono sicuro di quello che stavo cercando di dire in quel commento, lo cancellerò.
Stéphane Chazelas,
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.