From: Simone Piccardi Date: Fri, 28 Jun 2002 23:07:38 +0000 (+0000) Subject: Messo nuovo esempio e figura corretta. X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=2e715e70ba0df8224ec1bf118ba34a73a21df5c5;ds=sidebyside Messo nuovo esempio e figura corretta. --- diff --git a/img/pipeuse.dia b/img/pipeuse.dia index dcd771a..9f4d609 100644 Binary files a/img/pipeuse.dia and b/img/pipeuse.dia differ diff --git a/ipc.tex b/ipc.tex index e563153..40c9fad 100644 --- a/ipc.tex +++ b/ipc.tex @@ -86,8 +86,8 @@ capo della pipe, l'altro pu \begin{figure}[htb] \centering \includegraphics[height=5cm]{img/pipefork} - \caption{Schema dell'uso di una pipe come mezzo di comunicazione fra - processo attraverso una \func{fork}.} + \caption{Schema dei collegamenti ad una pipe, condivisi fra processo padre e + figlio dopo l'esecuzione \func{fork}.} \label{fig:ipc_pipe_fork} \end{figure} @@ -109,45 +109,58 @@ Per capire meglio il funzionamento di una pipe faremo un esempio di quello che è il loro uso più comune, analogo a quello effettuato della shell, e che consiste nell'inviare l'output di un processo (lo standard output) sull'input di un'altro. Realizzaremo il programma nella forma di un -\textit{CGI}\footnote{Un CGI (\textit{Common Gateway Interface} è un programma +\textit{CGI}\footnote{Un CGI (\textit{Common Gateway Interface}) è un programma che permette la creazione dinamica di un oggetto da inserire all'interno di una pagina HTML.} per apache, che genera una immagine JPEG di un codice a barre, specificato come parametro di input. -Un programma che deve essere eseguito come \textit{cgi} per apache deve -rispondere a delle caratteristiche specifiche, esso infatti non viene lanciato -da una shell, ma dallo stesso web server, alla richiesta di una specifica URL -che di solito ha la forma: +Un programma che deve essere eseguito come \textit{CGI} deve rispondere a +delle caratteristiche specifiche, esso infatti non viene lanciato da una +shell, ma dallo stesso web server, alla richiesta di una specifica URL, che di +solito ha la forma: \begin{verbatim} -http://www.sito.it/cgi-bin/programma?parametro + http://www.sito.it/cgi-bin/programma?parametro \end{verbatim} ed il risultato dell'elaborazione deve essere presentato (con una intestazione -che ne descrive il mime-type) sullo standard output, in modo che apache possa -reinviarlo al browser che ha effettuato la richiesta. +che ne descrive il mime-type) sullo standard output, in modo che il web-server +possa reinviarlo al browser che ha effettuato la richiesta, che in questo modo +è in grado di visualizzarlo opportunamente. Per fare questo useremo in sequenza i programmi \cmd{barcode} e \cmd{gs}, il primo infatti è in grado di generare immagini postscript di codici a barre corrispondenti ad una qualunque stringa, mentre il secondo serve per poter -effettuare la conversione della stessa immagine in formato JPEG. +effettuare la conversione della stessa immagine in formato JPEG. Usando una +pipe potremo inviare l'output del primo sull'input del secondo, secondo lo +schema mostrato in \figref{fig:ipc_pipe_use}, in cui la direzione del flusso +dei dati è data dalle frecce continue. + +\begin{figure}[htb] + \centering + \includegraphics[height=5cm]{img/pipeuse} + \caption{Schema dell'uso di una pipe come mezzo di comunicazione fra + due processi attraverso attraverso l'esecuzione una \func{fork} e la + chiusura dei capi non utilizzati.} + \label{fig:ipc_pipe_use} +\end{figure} Si potrebbe obiettare che sarebbe molto più semplice salvare il risultato -intermedio su un file temporaneo. Questo però non tiene conto del fatto che il -\textit{cgi} deve poter gestire più richieste in concorrenza, e si avrebbe una +intermedio su un file temporaneo. Questo però non tiene conto del fatto che un +\textit{CGI} deve poter gestire più richieste in concorrenza, e si avrebbe una evidente race condition in caso di accesso simultaneo a detto -file.\footnote{la questione potrebbe essere evitata creando prima dei file - temporanei, da comunicare poi ai vari sotto-processi, da cancellare alla - fine dell'esecuzione; ma a questo punto avremmo perso tutta la semplicità.} -L'uso di una pipe invece permette di risolvere il problema in maniera semplice -ed elegante. - -Il programma ci servirà anche come esempio dell'uso di alcune delle funzioni -di manipolazione dei file descriptor, come \func{dup} e \func{dup2}, viste in -\secref{sec:file_dup}; è attraverso queste funzioni 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. Le -sezioni significative del programma è riportato in -\figref{fig:ipc_barcode_code}, il codice è disponibile nel file -\file{BarCode.c} nella directory dei sorgenti. +file.\footnote{il problema potrebbe essere superato determinando in anticipo + un nome appropiato per il file temporaneo, che verrebbe utilizzato dai vari + sotto-processi, e cancellato alla fine della loro esecuzione; ma a questo le + cose non sarebbero più tanto semplici.} L'uso di una pipe invece permette +di risolvere il problema in maniera semplice ed elegante. + +Il programma ci servirà anche come esempio dell'uso delle funzioni di +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 +nel file \file{BarCodePage.c} che si trova nella directory dei sorgenti. \begin{figure}[!htb] @@ -204,47 +217,46 @@ int main(int argc, char *argv[], char *envp[]) \end{lstlisting} \end{minipage} \normalsize - \caption{Codice del \textit{cgi-bin} \cmd{BarCode}.} + \caption{Codice del \textit{CGI} \cmd{BarCodePage}.} \label{fig:ipc_barcode_code} \end{figure} -Il primo passo (\texttt{\small 4--12}) è quello di creare le due pipe che -servono per la comunicazione fra i due programmi che verranno utilizzati per -produrre il codice a barre; si ha cura di controllare la riuscita della +La prima operazione del programma (\texttt{\small 4--12}) è quella di creare +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 - \ref{fig:ipc_barcode_code}, ma si incarica semplicemente di formattare - l'uscita, aggiungendo un \textit{mime type}, in modo che possa essere - interpretata direttamente da un browser.} + \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.} -Una volta create le pipe il programma può creare (\texttt{\small 13-17}) il +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 -il programma \cmd{barcode}: quest'ultimo funziona ricevendo dallo standard -input la stringa da convertire nell'immagine postscript del codice a barre, -che sarà scritta sullo standard output. - -Per utilizzare queste caratteristiche il primo figlio chiude (\texttt{\small - 20}) il capo aperto in scrittura della prima pipe, dato che userà il capo -aperto in lettura per ricevere dal padre la stringa da codificare; per far -questo collega (\texttt{\small 21}) il capo in lettura della pipe 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}). +\cmd{barcode}. Quest'ultimo legge dallo standard input una stringa di +caratteri, la converte nell'immagine postscript del codice a barre ad essa +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}). In questo modo all'esecuzione (\texttt{\small 25}) di \cmd{barcode} (cui si -passa in \var{size} la dimensione per l'immagine) quest'ultimo leggerà la -stringa da codificare che gli viene inviata dal padre dalla prima pipe e -scriverà l'immagine postscript del codice a barre sulla seconda. - -Dall'altra parte il processo padre prima chiude (\texttt{\small 26}) il capo -inutilizzato della prima pipe (quello in input), 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. +passa in \var{size} la dimensione della pagina per l'immagine) quest'ultimo +leggerà dalla prima pipe la stringa da codificare che gli sarà inviata dal +padre, e scriverà l'immagine postscript del codice a barre sulla seconda. + +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. Alla conclusione della sua esecuzione \cmd{barcode} avrà inviato l'immagine postscript del codice a barre sul capo in scrittura della seconda pipe; a @@ -254,24 +266,25 @@ processo figlio, che poi (\texttt{\small 35--42}) eseguir leggendo l'immagine postscript creata da \cmd{barcode} dallo standard input, per convertirla in JPEG. -Per fare tutto ciò il secondo figlio anzitutto chiude (\texttt{\small 37}) il -capo in scrittura della seconda pipe, e ne collega (\texttt{\small 38}) il -capo in lettura allo standard input. Per poter formattare l'output del -programma in maniera utilizzabile da un browser, si provvede anche -\texttt{\small 40}) alla scrittura dell'apposita stringa di mime-type in testa -allo standard output. A questo punto si può invocare \texttt{\small 41}) il -programma \cmd{gs}, provvedendo gli appositi switch che consentono di leggere -il file da convertire dallo standard input, ed inviare la conversione sullo -standard output. - -Per concludere le operazioni il processo padre chiude \texttt{\small 44}) il +Per fare tutto ciò anzitutto si chiude (\texttt{\small 37}) il capo in +scrittura della seconda pipe, e se ne collega (\texttt{\small 38}) il capo in +lettura allo standard input. Per poter formattare l'output del programma in +maniera utilizzabile da un browser, si provvede anche \texttt{\small 40}) alla +scrittura dell'apposita stringa di identificazione del mime-type in testa allo +standard output. A questo punto si può invocare \texttt{\small 41}) \cmd{gs}, +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 capo in scrittura della seconda pipe, e attende la conclusione del figlio -\texttt{\small 45}), per poi uscire \texttt{\small 46}). Si tenga conto che, -l'operazione di chiudere il capo in scrittura della seconda pipe è necessaria, -infatti non chiudendola \cmd{gs}, che legge il suo stardard input da detta -pipe, resterebbe bloccato in attesa di ulteriore input (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}). 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}} @@ -294,13 +307,12 @@ stream restituito come valore di ritorno. potrà assumere i valori relativi alle sottostanti invocazioni di \func{pipe} e \func{fork} o \macro{EINVAL} se \param{type} non è valido.} \end{prototype} -\noindent e serve per semplificare l'uso di \func{pipe}. La funzione crea una pipe, esegue una \func{fork}, ed invoca il programma \param{command} attraverso la shell (in sostanza esegue \file{/bin/sh} con il -flag \code{-c}); l'argomento \param{type} deve essere una stringa \verb|"w"| o -\verb|"r"|, per indicare se la pipe sarà collegata allo standard input o allo -standard output del comando invocato. +flag \code{-c}); l'argomento \param{type} deve essere una delle due stringhe +\verb|"w"| o \verb|"r"|, per indicare se la pipe sarà collegata allo standard +input o allo standard output del comando invocato. La funzione restituisce il puntatore allo stream associato alla pipe creata, che sarà aperto in sola lettura (e quindi associato allo standard output del @@ -308,10 +320,11 @@ programma indicato) in caso si sia indicato \code{"r"}, o in sola scrittura (e quindi associato allo standard input) in caso di \code{"w"}. Lo stream restituito da \func{popen} è identico a tutti gli effetti ai file -standard visti in \secref{cha:files_std_interface}, e viene sempre aperto in -modalità \textit{fully-buffered} (vedi \secref{sec:file_buffering}); l'unica -differenza è che deve essere chiuso dalla seconda delle due funzioni, -\func{pclose}, il cui prototipo è: +stream visti in \secref{cha:files_std_interface}, anche se è collegato ad una +pipe e non ad un inode, e viene sempre aperto in modalità +\textit{fully-buffered} (vedi \secref{sec:file_buffering}); l'unica differenza +con gli usuali stream è che dovrà essere chiuso dalla seconda delle due nuove +funzioni, \func{pclose}, il cui prototipo è: \begin{prototype}{stdio.h} {int pclose(FILE *stream)} @@ -322,23 +335,49 @@ attendendo la terminazione del processo ad essa associato. errore; nel quel caso il valore di \func{errno} deriva dalle sottostanti chiamate.} \end{prototype} -\noindent che si incarica anche di attendere la conclusione del processo -creato dalla precedente \func{popen}. - -Per illustrare l'uso di queste due funzioni riprendiamo l'esempio in -\figref{fig:ipc_barcode_code}: esso per quanto funzionante, è piuttosto -complesso; inoltre nella pratica sconta un problema di \cmd{gs} che non è in +\noindent che oltre alla chiusura dello stream si incarica anche di attendere +(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; +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 una semplice figura. Se si vuole -generare una immagine di dimensioni corrette si deve allora ricorrere ad -ulteriore programma, \cmd{epstopsf}, per convertire in PDF il file EPS -generato da \cmd{barcode}. Utilizzando un file in PDF invece, \cmd{gs} esegue -la conversione rispettando le dimensioni originarie del codice a barre. - -Ci si trova dunque davanti al classico caso dell'uso delle pipe in cui si -devono eseguire più processi in fila, inviando l'output di ciascuno all'input -del successivo, per poi ottenere il risultato finale sullo standard output. +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. + +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 +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. + +In questo caso però occorre eseguire 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. + + Dato che questo caso ciascun processo deve scrivere il suo output sullo standard input del successivo, occorrerà usare \func{popen} aprendo la pipe in scrittura. @@ -357,6 +396,44 @@ quest'ultimo venga invocato dopo. \begin{lstlisting}{} int main(int argc, char *argv[], char *envp[]) { +int main(int argc, char *argv[], char *envp[]) +{ + FILE *pipe[4]; + FILE *pipein; + char *cmd_string[4]={ + "pnmtopng", + "pnmmargin -white 10", + "pnmcrop", + "gs -sDEVICE=ppmraw -sOutputFile=- -sNOPAUSE -q - -c showpage -c quit" + }; + char content[]="Content-type: image/png\n\n"; + int i; + /* write mime-type to stout */ + write(STDOUT_FILENO, content, strlen(content)); + /* execute chain of command */ + for (i=0; i<4; i++) { + pipe[i] = popen(cmd_string[i], "w"); + dup2(fileno(pipe[i]), STDOUT_FILENO); + } + /* create barcode (in PS) */ + pipein = popen("barcode", "w"); + /* send barcode string to barcode program */ + write(fileno(pipein), argv[1], strlen(argv[1])); + /* close all pipes (in reverse order) */ + for (i=4; i==0; i--) { + pclose((pipe[i])); + } + 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}