From: Simone Piccardi Date: Sun, 23 Jun 2002 22:03:28 +0000 (+0000) Subject: Altra roba connessa con le pipe X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=commitdiff_plain;h=9790ea16d447835b894169fa997314fd5fb0ebd5;p=gapil.git Altra roba connessa con le pipe --- diff --git a/filedir.tex b/filedir.tex index f7ce340..66b1f99 100644 --- a/filedir.tex +++ b/filedir.tex @@ -13,6 +13,7 @@ dei file \section{La gestione di file e directory} +\label{sec:file_dir} Come già accennato in \secref{sec:file_filesystem} in un sistema unix-like la gestione dei file ha delle caratteristiche specifiche che derivano @@ -507,8 +508,8 @@ nuovi file nella directory. Finora abbiamo parlato esclusivamente di file, directory e link simbolici; in \secref{sec:file_file_types} abbiamo visto però che il sistema prevede pure -degli altri tipi di file, come i file di dispositivo e le fifo (i socket sono -un caso a parte, che vedremo in \secref{cha:socket_intro}). +degli altri tipi di file speciali, come i file di dispositivo e le fifo (i +socket sono un caso a parte, che vedremo in \capref{cha:socket_intro}). La manipolazione delle caratteristiche di questi file e la loro cancellazione può essere effettuata con le stesse funzioni che operano sui file normali; ma diff --git a/fileunix.tex b/fileunix.tex index 7eddb14..40d0228 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -562,16 +562,15 @@ allora ritorna immediatamente con un errore \macro{EAGAIN}\footnote{sotto BSD Linux, con le glibc, questa è sinonima di \macro{EAGAIN}.} che nel caso indica soltanto che occorrerà provare a ripetere la lettura. -La funzione \func{read} è una delle system call esistenti fin dagli abori di -Unix, ma nella seconda versione delle \textit{Single Unix +La funzione \func{read} è una delle system call fondamentali, esistenti fin +dagli albori di Unix, ma nella seconda versione delle \textit{Single Unix Specification}\footnote{questa funzione, e l'analoga \func{pwrite} sono state aggiunte nel kernel 2.1.60, il supporto nelle \acr{glibc}, compresa l'emulazione per i vecchi kernel che non hanno la system call, è stato aggiunto con la versione 2.1, in versioni precedenti sia del kernel che delle librerie la funzione non è disponibile.} (quello che viene chiamato normalmente Unix98, vedi \secref{sec:intro_opengroup}) è stata introdotta la -definizione di un'altra funzione di lettura, \func{pread}, il cui prototipo di -questa funzione è: +definizione di un'altra funzione di lettura, \func{pread}, il cui prototipo è: \begin{prototype}{unistd.h} {ssize\_t pread(int fd, void * buf, size\_t count, off\_t offset)} @@ -932,8 +931,9 @@ prototipo descriptor aperti. \end{errlist}} \end{prototype} -\noindent e qualora il file descriptor \param{newfd} sia già aperto esso -sarà chiuso e poi duplicato. +\noindent e qualora il file descriptor \param{newfd} sia già aperto (come +avviene ad esempio nel caso della duplicazione di uno dei file standard) esso +sarà prima chiuso e poi duplicato. La duplicazione dei file descriptor può essere effettuata anche usando la funzione di controllo dei file \func{fnctl} (che esamineremo in diff --git a/ipc.tex b/ipc.tex index 0b7818c..898305f 100644 --- a/ipc.tex +++ b/ipc.tex @@ -94,8 +94,8 @@ capo della pipe, l'altro pu 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, @@ -110,11 +110,11 @@ 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-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: @@ -133,8 +133,8 @@ effettuare la conversione della stessa immagine in formato JPEG. 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à.} @@ -198,7 +198,7 @@ int main(int argc, char *argv[], char *envp[]) execlp("gs", "gs", "-q", "-sDEVICE=jpeg", "-sOutputFile=-", "-", NULL); } /* still parent */ - close(pipeout[1]); + close(pipeout[1]); waitpid(pid, NULL, 0); exit(0); } @@ -209,8 +209,7 @@ int main(int argc, char *argv[], char *envp[]) \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 @@ -220,38 +219,60 @@ richiesta.\footnote{la funzione \func{WriteMess}, non 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}} @@ -260,19 +281,61 @@ pipe; a questo punto 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. @@ -281,15 +344,27 @@ file EPS generato da \cmd{barcode}, che invece viene trattato correttamente. \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} diff --git a/sources/BarCode.c b/sources/BarCode.c index 7852dae..44a08da 100644 --- a/sources/BarCode.c +++ b/sources/BarCode.c @@ -18,7 +18,7 @@ */ /**************************************************************** * - * Program echod + * Program barcode * CGI for barcode generation * * Author: Simone Piccardi @@ -29,7 +29,7 @@ * 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 $ * ****************************************************************/ /* @@ -57,75 +57,23 @@ int main(int argc, char *argv[], char *envp[]) /* * 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); } /*