From: Simone Piccardi Date: Mon, 29 Apr 2013 23:13:11 +0000 (+0000) Subject: Inizio lavoro sulle pipe, aggiunta pipe2. X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=c01748fa2f24d5ecfe61d2e3ec94b71dbf966e42 Inizio lavoro sulle pipe, aggiunta pipe2. --- diff --git a/ipc.tex b/ipc.tex index 4a9090e..3ead8bb 100644 --- a/ipc.tex +++ b/ipc.tex @@ -22,9 +22,9 @@ diversi, come quelli tradizionali che coinvolgono \textit{pipe} e Tralasceremo invece tutte le problematiche relative alla comunicazione attraverso la rete (e le relative interfacce) che saranno affrontate in dettaglio in un secondo tempo. Non affronteremo neanche meccanismi più -complessi ed evoluti come le RPC (\textit{Remote Procedure Calls}) e CORBA -(\textit{Common Object Request Brocker Architecture}) che in genere sono -implementati con un ulteriore livello sopra i meccanismi elementari. +complessi ed evoluti come le RPC (\textit{Remote Procedure Calls}) che in +genere sono implementati da un ulteriore livello di librerie sopra i +meccanismi elementari. \section{L'intercomunicazione fra processi tradizionale} @@ -42,37 +42,51 @@ ne gestiscono l'uso e le varie forme in cui si è evoluto. Le \textit{pipe} nascono sostanzialmente con Unix, e sono il primo, e tuttora uno dei più usati, meccanismi di comunicazione fra processi. Si tratta in -sostanza di una coppia di file descriptor\footnote{si tenga presente che - le pipe sono oggetti creati dal kernel e non risiedono su disco.} connessi -fra di loro in modo che se quanto scrive su di uno si può rileggere -dall'altro. Si viene così a costituire un canale di comunicazione tramite i -due file descriptor, nella forma di un \textsl{tubo} (da cui il nome) -attraverso cui fluiscono i dati. - -La funzione che permette di creare questa speciale coppia di file descriptor -associati ad una \textit{pipe} è appunto \funcd{pipe}, ed il suo prototipo è: -\begin{prototype}{unistd.h} -{int pipe(int filedes[2])} - -Crea una coppia di file descriptor associati ad una \textit{pipe}. - - \bodydesc{La funzione restituisce zero in caso di successo e -1 per un - errore, nel qual caso \var{errno} potrà assumere i valori \errval{EMFILE}, - \errval{ENFILE} e \errval{EFAULT}.} -\end{prototype} +sostanza di una coppia di file descriptor connessi fra di loro in modo che +quanto scrive su di uno si può rileggere dall'altro. Si viene così a +costituire un canale di comunicazione realizzato tramite i due file +descriptor, che costituisce appunto una sorta di \textsl{tubo} (che appunto il +significato del termine inglese \textit{pipe}) attraverso cui si possono far +passare i dati. + +In pratica si tratta di un buffer circolare in memoria in cui il kernel +appoggia i dati immessi nel file descriptor su cui si scrive per farli poi +riemergere dal file descriptor da cui si legge. Si tenga ben presente che in +questo passaggio di dati non è previsto nessun tipo di accesso al disco e che +nonostante l'uso dei file descriptor le pipe non han nulla a che fare con i +file di dati di cui si è parlato al cap.~\ref{cha:file_IO_interface}. + +La funzione di sistema che permette di creare questa speciale coppia di file +descriptor associati ad una \textit{pipe} è appunto \funcd{pipe}, ed il suo +prototipo è: + +\begin{funcproto}{ +\fhead{unistd.h} +\fdecl{int pipe(int filedes[2])} +\fdesc{Crea la coppia di file descriptor di una \textit{pipe}.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EFAULT}] \param{filedes} non è un indirizzo valido. + \end{errlist} + ed inoltre \errval{EMFILE} e \errval{ENFILE} nel loro significato generico.} +\end{funcproto} La funzione restituisce la coppia di file descriptor nel vettore -\param{filedes}; il primo è aperto in lettura ed il secondo in scrittura. Come -accennato concetto di funzionamento di una pipe è semplice: quello che si -scrive nel file descriptor aperto in scrittura viene ripresentato tale e quale -nel file descriptor aperto in lettura. I file descriptor infatti non sono -connessi a nessun file reale, ma, come accennato in -sez.~\ref{sec:file_sendfile_splice}, ad un buffer nel kernel, la cui -dimensione è specificata dal parametro di sistema \const{PIPE\_BUF}, (vedi +\param{filedes}, il primo è aperto in lettura ed il secondo in scrittura. Come +accennato concetto di funzionamento di una \textit{pipe} è semplice: quello +che si scrive nel file descriptor aperto in scrittura viene ripresentato tale +e quale nel file descriptor aperto in lettura. + +I file descriptor infatti non sono connessi a nessun file reale, ma, come +accennato, ad un buffer nel kernel la cui dimensione è specificata dal +parametro di sistema \const{PIPE\_BUF}, (vedi sez.~\ref{sec:sys_file_limits}). Lo schema di funzionamento di una pipe è -illustrato in fig.~\ref{fig:ipc_pipe_singular}, in cui sono illustrati i due -capi della pipe, associati a ciascun file descriptor, con le frecce che -indicano la direzione del flusso dei dati. +illustrato in fig.~\ref{fig:ipc_pipe_singular}, in cui sono indicati i due +capi della \textit{pipe}, associati a ciascun file descriptor, con le frecce +che indicano la direzione del flusso dei dati. % TODO: la dimensione è cambiata a 64k (vedi man 7 pipe) e può essere % modificata con F_SETPIPE_SZ dal 2.6.35 (vedi fcntl) @@ -84,12 +98,42 @@ indicano la direzione del flusso dei dati. \label{fig:ipc_pipe_singular} \end{figure} -Chiaramente creare una pipe all'interno di un singolo processo non serve a -niente; se però ricordiamo quanto esposto in sez.~\ref{sec:file_shared_access} -riguardo al comportamento dei file descriptor nei processi figli, è immediato -capire come una pipe possa diventare un meccanismo di intercomunicazione. Un -processo figlio infatti condivide gli stessi file descriptor del padre, -compresi quelli associati ad una pipe (secondo la situazione illustrata in +Della funzione di sistema esiste una seconda versione, \funcd{pipe2}, +introdotta con il kernel 2.6.27 e le \acr{glibc} 2.9 e specifica di Linux +(utilizzabile solo definendo la macro \macro{\_GNU\_SOURCE}), che consente di +impostare atomicamente le caratteristiche dei file descriptor restituiti, il +suo prototipo è: + +\begin{funcproto}{ +\fhead{unistd.h} +\fhead{fcntl.h} +\fdecl{int pipe2(int pipefd[2], int flags)} +\fdesc{Crea la coppia di file descriptor di una \textit{pipe}.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{flags} non valido. + \end{errlist} + e gli altri già visti per \func{pipe} con lo stesso significato.} +\end{funcproto} + +Utilizzando un valore nullo per \param{flags} la funzione è identica a +\func{pipe}, si può però passare come valore l'OR aritmetico di uno qualunque +fra \const{O\_NONBLOCK} o \const{O\_CLOEXEC} che hanno l'effetto di impostare +su entrambi i file descriptor restituiti dalla funzione i relativi flag, già +descritti per \func{open} in tab.~\ref{tab:open_operation_flag}, che attivano +rispettivamente la modalità di accesso \textsl{non-bloccante} ed il +\textit{close-on-exec} \itindex{close-on-exec}. + +Chiaramente creare una \textit{pipe} all'interno di un singolo processo non +serve a niente; se però ricordiamo quanto esposto in +sez.~\ref{sec:file_shared_access} riguardo al comportamento dei file +descriptor nei processi figli, è immediato capire come una \textit{pipe} possa +diventare un meccanismo di intercomunicazione. Un processo figlio infatti +condivide gli stessi file descriptor del padre, compresi quelli associati ad +una pipe (secondo la situazione illustrata in fig.~\ref{fig:ipc_pipe_fork}). In questo modo se uno dei processi scrive su un capo della pipe, l'altro può leggere. @@ -103,27 +147,28 @@ capo della pipe, l'altro può leggere. Tutto ciò ci mostra come sia immediato realizzare un meccanismo di comunicazione fra processi attraverso una pipe, utilizzando le proprietà -ordinarie dei file, ma ci mostra anche qual è il principale\footnote{Stevens - in \cite{APUE} riporta come limite anche il fatto che la comunicazione è - unidirezionale, ma in realtà questo è un limite facilmente superabile usando - una coppia di pipe.} limite nell'uso delle pipe. È necessario infatti che i -processi possano condividere i file descriptor della pipe, e per questo essi -devono comunque essere \textsl{parenti} (dall'inglese \textit{siblings}), cioè -o derivare da uno stesso processo padre in cui è avvenuta la creazione della -pipe, o, più comunemente, essere nella relazione padre/figlio. - -A differenza di quanto avviene con i file normali, la lettura da una pipe può -essere bloccante (qualora non siano presenti dati), inoltre se si legge da una -pipe il cui capo in scrittura è stato chiuso, si avrà la ricezione di un EOF -(vale a dire che la funzione \func{read} ritornerà restituendo 0). Se invece -si esegue una scrittura su una pipe il cui capo in lettura non è aperto il -processo riceverà il segnale \signal{SIGPIPE}, e la funzione di scrittura -restituirà un errore di \errcode{EPIPE} (al ritorno del gestore, o qualora il -segnale sia ignorato o bloccato). - -La dimensione del buffer della pipe (\const{PIPE\_BUF}) ci dà inoltre un'altra -importante informazione riguardo il comportamento delle operazioni di lettura -e scrittura su di una pipe; esse infatti sono atomiche fintanto che la +ordinarie dei file, ma ci mostra anche qual è il principale limite nell'uso +delle pipe.\footnote{Stevens in \cite{APUE} riporta come limite anche il fatto + che la comunicazione è unidirezionale, ma in realtà questo è un limite + superabile usando una coppia di pipe, anche se al costo di una maggiore + complessità di gestione.} È necessario infatti che i processi possano +condividere i file descriptor della pipe, e per questo essi devono comunque +essere \textsl{parenti} (dall'inglese \textit{siblings}), cioè o derivare da +uno stesso processo padre in cui è avvenuta la creazione della \textit{pipe}, +o, più comunemente, essere nella relazione padre/figlio. + +A differenza di quanto avviene con i file normali, la lettura da una +\textit{pipe} può essere bloccante (qualora non siano presenti dati), inoltre +se si legge da una pipe il cui capo in scrittura è stato chiuso, si avrà la +ricezione di un EOF (vale a dire che la funzione \func{read} ritornerà +restituendo 0). Se invece si esegue una scrittura su una pipe il cui capo in +lettura non è aperto il processo riceverà il segnale \signal{SIGPIPE}, e la +funzione di scrittura restituirà un errore di \errcode{EPIPE} (al ritorno del +gestore, o qualora il segnale sia ignorato o bloccato). + +La dimensione del buffer della \textit{pipe} (\const{PIPE\_BUF}) ci dà inoltre +un'altra importante informazione riguardo il comportamento delle operazioni di +lettura e scrittura su di una pipe; esse infatti sono atomiche fintanto che la quantità di dati da scrivere non supera questa dimensione. Qualora ad esempio si effettui una scrittura di una quantità di dati superiore l'operazione verrà effettuata in più riprese, consentendo l'intromissione di scritture effettuate