next up previous contents index return to home!
Next: Un Esempio Up: Comunicazione fra processi Previous: Comunicazione fra processi   Indice   Indice analitico

Comunicazione tramite pipe

La forma di IPC più semplice che Unix mette a disposizione è quella ottenibile tramite pipe. Un pipe è un canale monodirezionale di comunicazione asincrona, accessibile tramite le primitive di I/O standard messe a disposizione da Unix. Ad ogni pipe sono associati due descrittori di file, uno per l'input ed uno per l'output: i dati scritti sul descrittore di input (tramite la syscall write()) vengono bufferizzati all'interno del pipe e possono essere letti dal descrittore di uscita (tramite la read()).

Poichè le primitive di output (write()) applicate ad un pipe sono non bloccanti (la comunicazione è asincrona), un pipe deve contenere un buffer in cui depositare i dati scritti sulla pipe. Tale buffer non è però infinito, ma ha dimensione PIPE_BUF (che è una costante definita in <unistd.h>): quando tale buffer è pieno (dopo che PIPE_BUF byte sono stati scritti sulla pipe e nessuno è stato letto) la write() diventa bloccante.

Un pipe può essere creato tramite la syscall pipe(), quindi viene acceduto come se fosse una coppia di normali file (uno aperto in sola lettura ed uno aperto in sola scrittura). Il prototipo della syscall pipe() è il seguente:



#include<unistd.h>

int pipe(int fd[2]);


Argomenti in ingresso :

fd
- Array di due interi in cui la primitiva ritorna gli identificatori dei due estremi del pipe.

Valore restituito :

0
- In caso di successo
-1
- In caso di errore.

La syscall crea un pipe e alloca due descrittori di file nella tabella dei file aperti dal processo chiamante, ritornando nell'array fd i loro identificatori. In particolare, fd[0] contiene il descrittore dell'uscita del pipe, mentre fd[1] contiene il descrittore dell'ingresso. Da questo si capisce come solo il processo che crea il pipe ed i suoi figli abbiano i due estremi della pipe fra le proprie risorse private. Per questo motivo, un pipe può essere utilizzato o per svolgere lavoro inutile (far comunicare un processo con se stesso) o per far comunicare un processo con un suo figlio, o per far comunicare processi discendenti dal processo che ha creato il pipe.

Un utilizzo del pipe può allora essere il seguente:

Esempio

Sebbene un pipe possa essere usato da più di un lettore e da più di uno scrittore contemporaneamente (consentendo comunicazioni del tipo molti/molti, molti/uno, uno/molti e uno/uno), generalmente un solo processo vi accede in lettura ed un solo processo vi accede in scrittura (secondo lo schema di comunicazione uno/uno). In una configurazione di questo tipo, il processo scrittore può chiudere il descrittore di uscita del pipe, mentre il processo lettore può chiudere il processo di ingresso, secondo il seguente schema:

Esempio

Spesso, il pipe è collegato allo standard input o allo standard output del processo utilizzando la syscall dup(), che duplica un descrittore di file (usando il primo descrittore libero):

Esempio

Tutto ciò può essere fatto in modo più semplice usando la syscall dup2(). È da notare che Unix fornisce la syscall popen() che esegue la sequenza pipe()/fork()/dup()/exec(), e pclose(), che elimina il pipe ed effettua una wait() sul processo figlio. Queste due syscall sono utilizzate dalle shell per implementare catene di processi, a loro volta dette pipe (utilizzando la sintassi ``<prog1> | <prog2>'' i due programmi <prog1> e <prog2> vengono lanciati in due processi in parallelo, collegando l'uscita del primo con l'ingresso del secondo.

Abbiamo detto che un pipe è monodirezionale, e tipicamente viene usato per comunicazioni uno/uno, quindi l'ingresso del pipe è identificato da un solo descrittore, così come pure l'uscita. Vediamo ora invece cosa succede se tutti i descrittori collegati ad un estremo vengono chiusi.

Se un processo effettua una read() su un pipe il cui ingresso non è identificato da nessun descrittore e che ha il buffer vuoto, la read() ritorna 0 per indicare la fine del file (End Of File, EOF). Se invece cerca di scrivere (tramite una write()) su una pipe la cui uscita non è identificata da nessun descrittore, è generato un segnale SIGPIPE. Il processo può gestire tale segnale tramite un apposito handler (specificato tramite sigaction()) o ignorarlo: in ogni caso la write() verrà interrotta dal segnale, e ritornerà un errore EPIPE.


next up previous contents index
Next: Un Esempio Up: Comunicazione fra processi Previous: Comunicazione fra processi   Indice   Indice analitico
Giuseppe Lipari 2002-10-27