X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=cd9c68fed1b9596b3bf07e68eb041f09b5aa82df;hp=40c9fadd4c20f23f608497e443a65a2972107868;hb=1fe9dd622fcc8e8c83de032cf8679806cf52359b;hpb=2e715e70ba0df8224ec1bf118ba34a73a21df5c5 diff --git a/ipc.tex b/ipc.tex index 40c9fad..cd9c68f 100644 --- a/ipc.tex +++ b/ipc.tex @@ -101,6 +101,16 @@ processi possano condividere i file descriptor della pipe, e per questo essi devono comunque derivare da uno stesso processo padre che ha aperto la 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 \macro{EPIPE}, e la funzione di scrittura +restituirà un errore di \macro{EPIPE} (al ritorno del manipolatore, o qualora +il segnale sia ignorato o bloccato). + + \subsection{Un esempio dell'uso delle pipe} \label{sec:ipc_pipe_use} @@ -158,8 +168,8 @@ duplicazione dei file descriptor che abbiamo trattato in \secref{sec:file_dup}, in particolare di \func{dup2}. È attraverso queste funzioni infatti che è possibile dirottare gli stream standard dei processi (che abbiamo visto in \secref{sec:file_std_descr} e -\secref{sec:file_std_stream}) sulla pipe. In \figref{fig:ipc_barcode_code} -abbiamo riportato il corpo del programm, il cui codice completo è disponibile +\secref{sec:file_std_stream}) sulla pipe. In \figref{fig:ipc_barcodepage_code} +abbiamo riportato il corpo del programma, il cui codice completo è disponibile nel file \file{BarCodePage.c} che si trova nella directory dei sorgenti. @@ -217,8 +227,9 @@ int main(int argc, char *argv[], char *envp[]) \end{lstlisting} \end{minipage} \normalsize - \caption{Codice del \textit{CGI} \cmd{BarCodePage}.} - \label{fig:ipc_barcode_code} + \caption{Sezione principale del codice del \textit{CGI} + \file{BarCodePage.c}.} + \label{fig:ipc_barcodepage_code} \end{figure} La prima operazione del programma (\texttt{\small 4--12}) è quella di creare @@ -226,10 +237,10 @@ le due pipe che serviranno per la comunicazione fra i due comandi utilizzati per produrre il codice a barre; si ha cura di controllare la riuscita della chiamata, inviando in caso di errore un messaggio invece dell'immagine richiesta.\footnote{la funzione \func{WriteMess}, non è riportata in - \secref{fig:ipc_barcode_code}; essa si incarica semplicemente di formattare - l'uscita alla maniera dei CGI, aggiungendo l'opportuno \textit{mime type}, e - formattando il messaggio in HTML, in modo che quest'ultimo possa essere - visualizzato correttamente da un browser.} + \secref{fig:ipc_barcodepage_code}; essa si incarica semplicemente di + formattare l'uscita alla maniera dei CGI, aggiungendo l'opportuno + \textit{mime type}, e formattando il messaggio in HTML, in modo che + quest'ultimo possa essere visualizzato correttamente da un browser.} Una volta create le pipe, il programma può creare (\texttt{\small 13-17}) il primo processo figlio, che si incaricherà (\texttt{\small 19--25}) di eseguire @@ -240,10 +251,13 @@ corrispondente, e poi scrive il risultato direttamente sullo standard output. Per poter utilizzare queste caratteristiche prima di eseguire \cmd{barcode} si chiude (\texttt{\small 20}) il capo aperto in scrittura della prima pipe, e se ne collega (\texttt{\small 21}) il capo in lettura allo standard input, usando -\func{dup2}. Dato che \cmd{barcode} scrive l'immagine postscript del codice a -barre sullo standard output, per poter effettuare una ulteriore redirezione il -capo in lettura della seconda pipe viene chiuso (\texttt{\small 22}) mentre il -capo in scrittura viene collegato allo standard output (\texttt{\small 23}). +\func{dup2}. Si ricordi che invocando \func{dup2} il secondo file, qualora +risulti aperto, viene, come nel caso corrente, chiuso prima di effettuare la +duplicazione. Allo stesso modo, dato che \cmd{barcode} scrive l'immagine +postscript del codice a barre sullo standard output, per poter effettuare una +ulteriore redirezione il capo in lettura della seconda pipe viene chiuso +(\texttt{\small 22}) mentre il capo in scrittura viene collegato allo standard +output (\texttt{\small 23}). In questo modo all'esecuzione (\texttt{\small 25}) di \cmd{barcode} (cui si passa in \var{size} la dimensione della pagina per l'immagine) quest'ultimo @@ -254,9 +268,9 @@ Al contempo una volta lanciato il primo figlio, il processo padre prima chiude (\texttt{\small 26}) il capo inutilizzato della prima pipe (quello in input) e poi scrive (\texttt{\small 27}) la stringa da convertire sul capo in output, così che \cmd{barcode} possa riceverla dallo standard input. A questo punto -l'uso della prima pipe è finito ed essa può essere definitivamente chiusa -(\texttt{\small 28}), si attende poi (\texttt{\small 29}) che l'esecuzione di -\cmd{barcode} sia completata. +l'uso della prima pipe da parte del padre è finito ed essa può essere +definitivamente chiusa (\texttt{\small 28}), si attende poi (\texttt{\small + 29}) che l'esecuzione di \cmd{barcode} sia completata. Alla conclusione della sua esecuzione \cmd{barcode} avrà inviato l'immagine postscript del codice a barre sul capo in scrittura della seconda pipe; a @@ -276,15 +290,15 @@ provvedendo gli appositi switch che consentono di leggere il file da convertire dallo standard input e di inviare la conversione sullo standard output. -Per completare le operazioni il processo padre chiude \texttt{\small 44}) il +Per completare le operazioni il processo padre chiude (\texttt{\small 44}) il capo in scrittura della seconda pipe, e attende la conclusione del figlio -\texttt{\small 45}); a questo punto può \texttt{\small 46}). Si tenga conto -che l'operazione di chiudere il capo in scrittura della seconda pipe è -necessaria, infatti se non venisse chiusa \cmd{gs}, che legge il suo stardard -input da detta pipe, resterebbe bloccato in attesa di ulteriori dati in -ingresso (l'unico modo che un programma ha per sapere che l'input è terminato -è rilevare che lo standard input è stato chiuso), e la \func{wait} non -ritornerebbe. +(\texttt{\small 45}); a questo punto può (\texttt{\small 46}) uscire. Si tenga +conto che l'operazione di chiudere il capo in scrittura della seconda pipe è +necessaria, infatti, se non venisse chiusa, \cmd{gs}, che legge il suo +stardard input da detta pipe, resterebbe bloccato in attesa di ulteriori dati +in ingresso (l'unico modo che un programma ha per sapere che l'input è +terminato è rilevare che lo standard input è stato chiuso), e la \func{wait} +non ritornerebbe. \subsection{Le funzioni \func{popen} e \func{pclose}} @@ -339,64 +353,63 @@ attendendo la terminazione del processo ad essa associato. (tramite \func{wait4}) la conclusione del processo creato dalla precedente \func{popen}. -Per illustrare l'uso di queste due funzioni riprenderemo il problema -precedente; il programma mostrato in \figref{fig:ipc_barcode_code} per quanto -funzionante, è (volutamente) codificato in maniera piuttosto complesso; +Per illustrare l'uso di queste due funzioni riprendiamo il problema +precedente: il programma mostrato in \figref{fig:ipc_barcodepage_code} per +quanto funzionante, è (volutamente) codificato in maniera piuttosto complessa, inoltre nella pratica sconta un problema di \cmd{gs} che non è in grado\footnote{nella versione GNU Ghostscript 6.53 (2002-02-13).} di -riconoscere correttamente l'encapsulated postscript, per cui tutte le volte -generata una pagina intera, invece che semplice figura delle dimensioni -corrispondenti al codice a barre. - -Se si vuole generare una immagine di dimensioni corrette si deve usare un -approccio diverso; una possibilità sarebbe quella di ricorrere ad ulteriore -programma, \cmd{epstopsf}, per convertire in PDF il file EPS generato da -\cmd{barcode} (utilizzando lo switch \cmd{-E} di quest'ultimo). Utilizzando un -PDF al posto di un EPS \cmd{gs} esegue la conversione rispettando le -dimensioni originarie del codice a barre e produce un JPEG delle dimensioni -adeguate. +riconoscere correttamente l'encapsulated postscript, per cui deve essere usato +il postscript e tutte le volte viene generata una pagina intera, invece che +una immagine delle dimensioni corrispondenti al codice a barre. + +Se si vuole generare una immagine di dimensioni appropriate si deve usare un +approccio diverso. Una possibilità sarebbe quella di ricorrere ad ulteriore +programma, \cmd{epstopsf}, per convertire in PDF un file EPS (che può essere +generato da \cmd{barcode} utilizzando lo switch \cmd{-E}). Utilizzando un PDF +al posto di un EPS \cmd{gs} esegue la conversione rispettando le dimensioni +originarie del codice a barre e produce un JPEG delle dimensioni adeguate. Questo però ci porta a scontrarci con una caratteristica peculiare delle pipe, che a prima vista non è evidente. Per poter effettuare la conversione di un PDF infatti è necessario, per la struttura del formato, dover eseguire delle -\func{lseek} sul file da convertire; una pipe però è rigidamente sequenziale, -ed il tentativo di eseguire detta funzioni su un file descriptor associato ad -una pipe comporta l'immediato fallimento con un errore di \macro{ESPIPE}. - -Per questo motivo si è utilizzata una strada diversa, che prevede la +\func{lseek} sul file da convertire; se si esegue \cmd{gs} su un file normale +non ci sono problemi, ma una pipe però è rigidamente sequenziale, ed il +tentativo di eseguire detta operazione su una pipe comporta l'immediato +fallimento con un errore di \macro{ESPIPE}. Questo ci dice che in generale la +concatenazione di vari programmi funzionerà soltanto quando tutti prevedono +una lettura sequenziale del loro input. + +Per questo motivo si è dovuto utilizzare una strada diversa, che prevede la conversione attraverso \cmd{gs} del PS in un altro formato intermedio, il PPM,\footnote{il \textit{Portable PixMap file format} è un formato usato spesso come formato intermedio per effettuare conversioni, è estremamente inefficiente, ma molto facile da manipolare dato che usa caratteri ASCII per memorizzare le immagini.} dal quale poi si può ottenere un'immagine di dimensioni corrette attraverso vari programmi di manipolazione (\cmd{pnmcrop}, -\cmd{pnmmargin}) che può essere infine trasformata in PNG. +\cmd{pnmmargin}) che può essere infine trasformata in PNG (con \cmd{pnm2png}). -In questo caso però occorre eseguire sequenza ben quattro comandi diversi, +In questo caso però occorre eseguire in sequenza ben quattro comandi diversi, inviando l'output di ciascuno all'input del successivo, per poi ottenere il -risultato finale sullo standard output, il caso più classico dell'uso delle -pipe. - +risultato finale sullo standard output: un caso classico di utilizzazione +delle pipe, in cui l'uso di \func{popen} e \func{pclose} permette di +semplificare notevolmente la stesura del codice. -Dato che questo caso ciascun processo deve scrivere il suo output sullo +Nel nostro caso, dato che ciascun processo deve scrivere il suo output sullo standard input del successivo, occorrerà usare \func{popen} aprendo la pipe in -scrittura. - -Il codice del nuovo programma è riportato in \figref{fig:ipc_barcode2_code}; -come si può notare l'ordine di invocazione dei programmi è l'inverso di quello -in cui ci si aspetta vengano effettivamente eseguiti. Questo non comporta -nessun problema; infatti la lettura su una pipe è bloccante, per cui ciascun -processo, per quanto lanciato per primo, si bloccherà in attesa di ricevere -sullo standard input il risultato dell'elaborazione del precendente, benchè -quest'ultimo venga invocato dopo. +scrittura. Il codice del nuovo programma è riportato in +\figref{fig:ipc_barcode_code}. Come si può notare l'ordine di invocazione dei +programmi è l'inverso di quello in cui ci si aspetta che vengano +effettivamente eseguiti. Questo non comporta nessun problema dato che la +lettura su una pipe è bloccante, per cui ciascun processo, per quanto lanciato +per primo, si bloccherà in attesa di ricevere sullo standard input il +risultato dell'elaborazione del precendente, benchè quest'ultimo venga +invocato dopo. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} \begin{lstlisting}{} int main(int argc, char *argv[], char *envp[]) -{ -int main(int argc, char *argv[], char *envp[]) { FILE *pipe[4]; FILE *pipein; @@ -425,42 +438,35 @@ int main(int argc, char *argv[], char *envp[]) } exit(0); } -/* - * Routine to produce an HTML error message on output - */ -void WriteMess(char *mess) -{ - printf("Content-type: text/html\n\n"); - perror(mess); - printf("
\n"); -} -} - \end{lstlisting} \end{minipage} \normalsize - \caption{Codice del \textit{cgi-bin} \cmd{BarCode2}.} - \label{fig:ipc_barcode2_code} + \caption{Codice completo del \textit{CGI} \file{BarCode.c}.} + \label{fig:ipc_barcode_code} \end{figure} -Nel nostro caso il primo passo (\texttt{\small 12}) è scrivere il mime-type +Nel nostro caso il primo passo (\texttt{\small 14}) è scrivere il mime-type sullo standard output; a questo punto il processo padre non necessita più di eseguire ulteriori operazioni sullo standard output e può tranquillamente -provvedere alla redirezione. Il primo processo figlio ad essere invocato -(\texttt{\small 14}) è necessariamente l'ultimo della sequenza, in quanto è -lui che deve uscire sullo standard output, gli altri saranno tutti rediretti. +provvedere alla redirezione. + +Dato che i vari programmi devono essere lanciati in successione, si è +approntato un ciclo (\texttt{\small 15--19}) che esegue le operazioni in +sequenza: prima crea una pipe (\texttt{\small 17}) per la scrittura eseguendo +il programma con \func{popen}, in modo che essa sia collegata allo standard +input, e poi redirige (\texttt{\small 18}) lo standard output su detta pipe. -Una volta lanciato il processo finale si può iniziare la catena delle -redirezioni; ogni volta (\texttt{\small 16} e \texttt{\small 20}) duplicheremo -il file restituito dalla chiamata precedente a \func{popen} sullo standard -output, in questo modo alla successiva chiamata di \func{popen} il processo -eseguito scriverà il suo standard output sulla pipe collegata allo standard -input del precedente. +In questo modo il primo processo ad essere invocato (che è l'ultimo della +catena) scriverà ancora sullo standard output del processo padre, ma i +successivi, a causa di questa redirezione, scriveranno sulla pipe associata +allo standard input del processo invocato nel ciclo precedente. -Alla fine tutto quello che resta da fare è scrivere (\texttt{\small 22}) la -stringa del codice a barre sulla pipe collegata allo standard input -dell'ultimo processo lanciato, e poi chiudere tutte le pipe create con -\func{pclose}. +Alla fine tutto quello che resta da fare è lanciare (\texttt{\small 21}) il +primo processo della catena, che nel caso è \cmd{barcode}, e scrivere +(\texttt{\small 23}) la stringa del codice a barre sulla pipe, che è collegata +al suo standard input, infine si può eseguire (\texttt{\small 24--27}) un +ciclo, che chiuda, nell'ordine inverso rispetto a quello in cui le si sono +create, tutte le pipe create con \func{pclose}. \subsection{Le \textit{pipe} con nome, o \textit{fifo}} @@ -470,33 +476,47 @@ Come accennato in \secref{sec:ipc_pipes} il problema delle \textit{pipe} esse possono essere utilizzate solo da processi con un progenitore comune o nella relazione padre/figlio; per superare questo problema lo standard POSIX.1 ha definito dei nuovi oggetti, le \textit{fifo}, che hanno le stesse -caratteristiche delle pipe, ma invece che essere struttura interne del kernel -visibili solo attraverso un file descriptor comune, possono essere viste -attraverso un inode che risiede sul filesystem. - -Utilizzando una fifo tutti i dati passeranno attraverso un apposito buffer nel -kernel, senza transitare dal filesystem, l'inode serve solo a fornire un punto -d'appoggio per i vari processi che permetta loro di accedere alla stessa -fifo. - - - - -Abbiamo già visto in \secref{sec:file_mknod} le modalità che permettono di -creare una fifo, attraverso le funzioni \func{mknod} e \func{mkfifo}; per -utilizzarle un processo non avrà che da aprire il relativo file in lettura o -scrittura (a seconda della direzione che si vuole dare ai dati). - -che invece possono risiedere -sul filesystem, e che i processi possono usare per le comunicazioni senza -dovere per forza essere in relazione diretta. - - +caratteristiche delle pipe, ma che invece di essere strutture interne del +kernel, visibili solo attraverso un file descriptor, sono accessibili +attraverso un inode che risiede sul filesystem, così che i processi le possono +usare senza dovere per forza essere in una relazione di \textsl{parentela}. + +Utilizzando una \textit{fifo} tutti i dati passeranno, come per le pipe, +attraverso un apposito buffer nel kernel, senza transitare dal filesystem; +l'inode allocato sul filesystem serve infatti solo a fornire un punto di +riferimento per i processi, che permetta loro di accedere alla stessa fifo; il +comportamento delle funzioni di lettura e scrittura è identico a quello +illustrato per le pipe in \secref{sec:ipc_pipes}. + +Abbiamo già visto in \secref{sec:file_mknod} le funzioni \func{mknod} e +\func{mkfifo} che permettono di creare una fifo; per utilizzarne una un +processo non avrà che da aprire il relativo file speciale o in lettura o +scrittura; nel primo caso sarà collegato al capo di uscita della fifo, e dovrà +leggere, nel secondo al capo di ingresso, e dovrà scrivere. + +Il kernel crea una sigola pipe per ciascuna fifo che sia stata aperta, che può +essere acceduta contemporaneamente da più processi, sia in lettura che in +scrittura. Dato che per funzionare deve essere aperta in entrambe le +direzioni, per una fifo di norma la funzione \func{open} si blocca se viene +eseguita quando l'altro capo non è aperto. + +Le fifo però possono essere anche aperte in modalità \textsl{non-bloccante}, +nel qual caso l'apertura del capo in lettura avrà successo solo quando anche +l'altro capo è aperto, mentre l'apertura del capo in scrittura restituirà +l'errore di \macro{ENXIO} fintanto che non verrà aperto il capo in lettura. + +In Linux\footnote{lo standard POSIX lascia indefinito questo comportamento.} è +possibile aprire le fifo anche in lettura/scrittura, operazione che avrà +sempre successo qualunque sia la modalità di apertura (bloccante e non +bloccante); questo può essere utilizzato per aprire comunque una fifo in +scrittura anche se non ci sono ancora processi il lettura; è possibile anche +usare la fifo all'interno di un solo processo, nel qual caso però occorre +stare molto attenti alla possibili deadlock.\footnote{se si cerca di leggere + da una fifo che non contiene dati si avrà un deadlock immediato, dato che il + processo si blocca e non potrà quindi mai eseguire le funzioni di + scrittura.} -Per poter superare il problema delle \textit{pipe}, illustrato in -\secref{sec:ipc_pipes}, che ne consente l'uso solo fra processi con un -progenitore comune o nella relazione padre/figlio, \section{La comunicazione fra processi di System V} \label{sec:ipc_sysv}