Tutto ciò ci mostra come sia immediato realizzare un meccanismo di
comunicazione fra processi attraverso una pipe, utilizzando le ordinarie
proprietà dei file, ma ci mostra anche qual'è il principale\footnote{Stevens
- riporta in APUE come limite anche il fatto che la comunicazione è
- unidirezionale, in realtà questo è un limite facilmente risolvibile usando
+ in \cite{APUE} riporta come limite anche il fatto che la comunicazione è
+ unidirezionale, 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 derivare da uno stesso processo padre che ha aperto la pipe,
è 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-bin}\footnote{NdA, inserire una breve descrizione di cosa è un
- cgi-bin.} per apache, che genera una immagine JPEG di un codice a barre,
-specificato come parametro di input.
+\textit{cgi}\footnote{NdA, inserire una breve descrizione di cosa è un cgi.}
+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-bin} per apache deve
+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:
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-bin} deve poter gestire più richieste in concorrenza, e si avrebbe
-una evidente race condition in caso di accesso simultaneo a detto
+\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à.}
execlp("gs", "gs", "-q", "-sDEVICE=jpeg", "-sOutputFile=-", "-", NULL);
}
/* still parent */
- close(pipeout[1]);
+ close(pipeout[1]);
waitpid(pid, NULL, 0);
exit(0);
}
\label{fig:ipc_barcode_code}
\end{figure}
-
-Il primo passo (\texttt{\small 4-12}) è quello di creare le due pipe che
+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
chiamata, inviando in caso di errore un messaggio invece dell'immagine
interpretata direttamente 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
+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
- 21}) il capo aperto in scrittura della prima pipe, dato che userà il capo
+ 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 22}) il capo in lettura allo standard input
-usando \func{dup2};
-
-
-
-Analogamente il capo in lettura della seconda pipe sarà chiuso mentre il capo
-in scrittura viene collegato allo standard output (\texttt{\small 23-24}. In
-questo modo all'esecuzione (\texttt{\small 25}) di \cmd{barcode} quest'ultimo
-leggerà la stringa da codificare dalla prima pipe e scriverà l'immagine
-postscript nella seconda.
-
-Dall'altra parte il processo padre prima chiude (\texttt{\small 28-29}) i due
-capi inutilizzati delle pipe (input della prima ed output della seconda), poi
-scrive (\texttt{\small 30}) la stringa da convertire sull'output della prima
-pipe 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 31}), si attenderà poi (\texttt{\small 32}) che
-l'esecuzione di \cmd{barcode} venga completata.
+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}).
+
+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.
Alla conclusione della sua esecuzione \cmd{barcode} avrà effettuato inviato
l'immagine postscript del codice a barre sul capo in scrittura della seconda
-pipe; a questo punto
-
-
+pipe; a questo punto si può eseguire la seconda conversione, da PS a JPEG,
+usando il programma \cmd{gs}. Per questo si crea (\texttt{\small 30--34}) un
+secondo processo figlio, che poi (\texttt{\small 35--42}) eseguirà questo
+programma leggendo l'immagine postscript creata da \cmd{barcode} sullo
+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 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
+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.
\subsection{Le funzioni \func{popen} e \func{pclose}}
Come si è visto la modalità più comune di utilizzo di una pipe è quella di
utilizzarla per fare da tramite fra output ed input di due programmi invocati
in sequenza; per questo motivo lo standard POSIX.2 ha introdotto due funzioni
-che permettono di sintetizzare queste operazioni comuni in una sola
-chiamata. La prima di esse si chiama \func{popen} ed il suo prototipo è:
-
-
-L'esempio in \figref{fig:ipc_barcode_code} per quanto perfettamente
-funzionante, è 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}, che invece viene trattato correttamente.
+che permettono di sintetizzare queste operazioni. La prima di esse si chiama
+\func{popen} ed il suo prototipo è:
+\begin{prototype}{stdio.h}
+{FILE *popen(const char *command, const char *type)}
+
+Esegue il programma \param{command}, di cui, a seconda di \param{type},
+restituisce, lo standard input o lo standard output nella pipe collegata allo
+stream restituito come valore di ritorno.
+
+\bodydesc{La funzione restituisce l'indirizzo dello stream associato alla pipe
+ in caso di successo e \macro{NULL} per un errore, nel qual caso \var{errno}
+ 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.
+
+La funzione restituisce il puntatore allo stream associato alla pipe creata,
+che sarà aperto in sola lettura (e quindi associato allo standard output del
+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 è:
+\begin{prototype}{stdio.h}
+{int pclose(FILE *stream)}
+
+Chiude il file \param{stream}, restituito da una prededente \func{popen}
+attendendo la terminazione del processo ad essa associato.
+
+\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
+ 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
+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.
\subsection{Le \textit{pipe} con nome, o \textit{fifo}}
\label{sec:ipc_named_pipe}
-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, lo standard POSIX.1
-definisce dei nuovi oggetti, le \textit{fifo}, che invece possono risiedere
+Come accennato in \secref{sec:ipc_pipes} il problema delle \textit{pipe} è che
+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.
+
+
+Abbiamo già accennato in \secref{sec:file_mknod}
+
+che invece possono risiedere
sul filesystem, e che i processi possono usare per le comunicazioni senza
dovere per forza essere in relazione diretta.
+
+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}
*/
/****************************************************************
*
- * Program echod
+ * Program barcode
* CGI for barcode generation
*
* Author: Simone Piccardi
* http://localhost/cgi-bin/barcode?string
* where string is the code to be converted
*
- * $Id: BarCode.c,v 1.2 2002/06/21 22:24:10 piccardi Exp $
+ * $Id: BarCode.c,v 1.3 2002/06/23 22:03:28 piccardi Exp $
*
****************************************************************/
/*
/*
* Variables definition
*/
- pid_t pid;
- int retval;
- int pipein[2];
- int pipeout[2];
+ FILE *file1, *file2, *file3;
char content[]="Content-type: image/jpeg\n\n";
- char size[]="-pA9";
/*
* Begin
*/
- /* create two pipes to handle process communication */
- if ( (retval = pipe(pipein)) ) {
- WriteMess("input pipe creation error");
- exit(0);
- }
- if ( (retval = pipe(pipeout)) ) {
- WriteMess("output pipe creation error");
- exit(0);
- }
- /* fork child to run barcode program */
- pid = fork();
- if (pid == -1) {
- WriteMess("child creation error");
- exit(0);
- }
- /* if child */
- if (pid == 0) {
- /*
- * Child exec barcode program, that take input (string to encode)
- * from pipein, remapped to stdin, and write the output (a PS
- * image) to stdout, remapped to pipeout
- */
- close(pipein[1]); /* close output side of input pipe */
- dup2(pipein[0], STDIN_FILENO); /* remap stdin in pipe input */
- close(pipeout[0]);
- dup2(pipeout[1], STDOUT_FILENO); /* remap stdout in pipe output */
- execlp("barcode", "barcode", size, NULL); //"-o", "-", NULL);
- }
- /*
- * Parent write string to pipe input and close it,
- * then wait child execution and results form pipeout,
- * then fork to convert PS to JPEG using gs
- */
- close(pipein[0]); /* close input side of input pipe */
- write(pipein[1], argv[1], strlen(argv[1]));
- close(pipein[1]);
- waitpid(pid, NULL, 0);
- /*
- * refork to use gs
- */
- pid = fork();
- if (pid == -1) {
- WriteMess("child creation error");
- exit(0);
- }
- /*
- * second child, convert PS to JPEG
- */
- if (pid == 0) {
- /* send mime type */
- close(pipeout[1]);
- dup2(pipeout[0], STDIN_FILENO);
- write(STDOUT_FILENO, content, strlen(content));
- execlp("gs", "gs", "-q", "-sDEVICE=jpeg", "-sOutputFile=-", "-", NULL);
- }
- /*
- * still parent
- */
- close(pipeout[1]);
- waitpid(pid, NULL, 0);
+ /* write mime-type to stout */
+ write(STDOUT_FILENO, content, strlen(content));
+ /* convert PDF to JPEG */
+ file1 = popen("gs -q -sDEVICE=jpeg -sOutputFile=- -", "w");
+ /* convert EPS to PDF*/
+ dup2(fileno(file1), STDOUT_FILENO); /* set epstopdf stdout to file1 */
+ file2 = popen("epstopdf --filter", "w");
+// file2 = popen("eps2eps", "w");
+ /* create barcode */
+ dup2(fileno(file2), STDOUT_FILENO); /* set barcode stdout to file2 */
+ file3 = popen("barcode -E", "w");
+ write(fileno(file3), argv[1], strlen(argv[1]));
exit(0);
}
/*