Con i \textsl{file regolari} questa è l'unica situazione in cui si può avere
un numero di byte letti inferiore a quello richiesto, ma questo non è vero
quando si legge da un terminale, da una fifo o da una pipe. In tal caso
-infatti, se non ci sono dati in ingresso, la \func{read} si blocca e ritorna
-solo quando ne arrivano; se il numero di byte richiesti eccede quelli
-disponibili la funzione ritorna comunque, ma con un numero di byte inferiore a
-quelli richiesti.
+infatti, se non ci sono dati in ingresso, la \func{read} si blocca (a meno di
+non aver selezionato la modalità non bloccante, vedi
+\secref{sec:file_noblocking}) e ritorna solo quando ne arrivano; se il numero
+di byte richiesti eccede quelli disponibili la funzione ritorna comunque, ma
+con un numero di byte inferiore a quelli richiesti.
Lo stesso comportamento avviene caso di lettura dalla rete (cioè su un socket,
-come vedremo in \secref{sec:sock_io_behav}), o per certi dispositivi, come le
-unità a nastro, che restituiscono un singolo blocco di dati alla volta.
+come vedremo in \secref{sec:sock_io_behav}), o per la lettura da certi file di
+dispositivo, come le unità a nastro, che restituiscono sempre i dati ad un
+singolo blocco alla volta.
In realtà anche le due condizioni segnalate dagli errori \macro{EINTR} e
\macro{EAGAIN} non sono errori. La prima si verifica quando la \func{read} è
bloccata in attesa di dati in ingresso e viene interrotta da un segnale; in
-tal caso l'azione da prendere è quella di rieseguire la funzione. Torneremo
-sull'argomento in \secref{sec:sig_gen_beha}.
-
-La seconda si verifica quando il file è in modalità non bloccante e non ci
-sono dati in ingresso: la funzione allora ritorna immediatamente con un errore
-\macro{EAGAIN}\footnote{sotto BSD questo per questo errore viene usata la
- costante \macro{EWOULDBLOCK}, in GNU/Linux questa è sinonima di
- \macro{EAGAIN}.} che nel caso indica soltanto che occorrerà provare a
-ripetere la lettura.
-
-
-Nella seconda versione delle \textit{Single Unix
+tal caso l'azione da prendere è quella di rieseguire la funzione. Torneremo in
+dettaglio sull'argomento in \secref{sec:sig_gen_beha}.
+
+La seconda si verifica quando il file è in modalità non bloccante (vedi
+\secref{sec:file_noblocking}) e non ci sono dati in ingresso: la funzione
+allora ritorna immediatamente con un errore \macro{EAGAIN}\footnote{sotto BSD
+ questo per questo errore viene usata la costante \macro{EWOULDBLOCK}, in
+ 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
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.} (quello che viene chiamato normalmente Unix98,
-vedi \secref{sec:intro_opengroup}) è stata introdotta la definizione di
-un'altra funzione di lettura, \func{pread}, che diventa accessibile con la
-definizione:
-\begin{verbatim}
- #define _XOPEN_SOURCE 500
-\end{verbatim}
-il prototipo di questa funzione è:
+ 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 è:
\begin{prototype}{unistd.h}
{ssize\_t pread(int fd, void * buf, size\_t count, off\_t offset)}
-
+
Cerca di leggere \var{count} byte dal file \var{fd}, a partire dalla posizione
\var{offset}, nel buffer \var{buf}.
in caso di errore, nel qual caso \var{errno} viene settata secondo i valori
già visti per \func{read} e \func{lseek}.}
\end{prototype}
+\noindent che però diventa accessibile solo con la definizione della macro:
+\begin{verbatim}
+ #define _XOPEN_SOURCE 500
+\end{verbatim}
Questa funzione serve quando si vogliono leggere dati dal file senza
modificarne la posizione corrente. È equivalente alla esecuzione di una
-\func{read} e una \func{lseek}, ma permette di eseguire l'operazione
+\func{read} seguita da una \func{lseek} che riporti al valore precedente la
+posizione corrente sul file, ma permette di eseguire l'operazione
atomicamente. Questo può essere importante quando la posizione sul file viene
condivisa da processi diversi (vedi \secref{sec:file_sharing}). Il valore di
\var{offset} fa sempre riferimento all'inizio del file.
può fare riferimento a \figref{fig:file_dup}: l'effetto della funzione è
semplicemente quello di copiare il valore nella struttura \var{file\_struct},
cosicché anche il nuovo file descriptor fa riferimento alla stessa voce
-nella \textit{file table}.
+nella \textit{file table}; per questo si dice che il nuovo file descriptor è
+\textsl{duplicato}, da cui il nome della funzione.
\begin{figure}[htb]
\centering \includegraphics[width=13cm]{img/filedup}
\label{fig:file_dup}
\end{figure}
-In questo modo entrambi i file condivideranno eventuali lock, \textit{file
- status flag}, e posizione corrente: se ad esempio \func{lseek} modifica la
-posizione su uno dei due file descriptor essa sarà modificata anche sull'altro
-(al solito viene modificato lo stesso campo nella voce della \textit{file
- table} a cui entrambi fanno riferimento).
-
-L'unica differenza fra i due file descriptor è che ciascuno avrà il suo
-\textit{file descriptor flag}: nel caso di \func{dup} il flag di \textit{close
- on exec} viene sempre cancellato nella copia.
-
-Una diversa versione della funzione, \func{dup2} viene utilizzata per
-specificare esplicitamente il nuovo file descriptor; il suo prototipo è:
+Si noti che per quanto illustrato in\figref{fig:file_dup} i file descriptor
+duplicati condivideranno eventuali lock, \textit{file status flag}, e
+posizione corrente. Se ad esempio si esegue una \func{lseek} per modificare la
+posizione su uno dei due file descriptor, essa risulterà modificata anche
+sull'altro (dato che quello che viene modificato è lo stesso campo nella voce
+della \textit{file table} a cui entrambi fanno riferimento). L'unica
+differenza fra due file descriptor duplicati è che ciascuno avrà il suo
+\textit{file descriptor flag}; a questo proposito va specificato che nel caso
+di \func{dup} il flag di \textit{close on exec} viene sempre cancellato nella
+copia.
+
+L'uso principale di questa funzione è per la redirezione dell'input e
+dell'output fra l'esecuzione di una \func{fork} e la successiva \func{exec};
+diventa così possibile associare un file (o una pipe) allo standard input o
+allo standard output (torneremo sull'argomento in \secref{sec:ipc_pipe_use},
+quando tratteremo le pipe). Per fare questo in genere occorre prima chiudere
+il file che si vuole sostituire, cossicché il suo file descriptor possa esser
+restituito alla chiamata di \func{dup}, come primo file descriptor
+disponibile.
+
+Dato che questa è l'operazione più comune, è prevista una diversa versione
+della funzione, \func{dup2}, che permette di specificare esplicitamente qual'è
+il valore di file descriptor che si vuole avere come duplicato; il suo
+prototipo è:
\begin{prototype}{unistd.h}{int dup2(int oldfd, int newfd)}
Rende \param{newfd} una copia del file descriptor \param{oldfd}.
descriptor aperti.
\end{errlist}}
\end{prototype}
-\noindent la funzione chiude il file descriptor \param{newfd} se è aperto.
+\noindent e qualora il file descriptor \param{newfd} sia già aperto esso
+sarà 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
-\secref{sec:file_fcntl}) con il parametro \macro{F\_DUPFD}.
+\secref{sec:file_fcntl}) con il parametro \macro{F\_DUPFD}.
L'operazione ha la sintassi \code{fnctl(oldfd, F\_DUPFD, newfd)} e se si usa 0
come valore per \param{newfd} diventa equivalente a \func{dup}. La sola
superiore, per cui per poterla usare come \func{dup2} occorrerebbe prima
effettuare una \func{close}, perdendo l'atomicità dell'operazione.
-L'uso principale di queste funzioni è per la redirezione dell'input e
-dell'output fra l'esecuzione di una \func{fork} e la successiva \func{exec};
-diventa così possibile associare un file (o una pipe) allo standard input o
-allo standard output, torneremo su questo uso in \secref{sec:ipc_pipe_use}
-quando tratteremo le pipe.
-
\subsection{La funzione \func{fcntl}}
\label{sec:file_fcntl}
-f\chapter{La comunicazione fra processi}
+\chapter{La comunicazione fra processi}
\label{cha:IPC}
è 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{breve descrizione, da fare, di cosa è un cgi-bin.}
-per apache, che genera una immagine JPEG di un codice a barre, specificato
-come parametro di input.
+\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.
Un programma che deve essere eseguito come \textit{cgi-bin} per apache deve
rispondere a delle caratteristiche specifiche, esso infatti non viene lanciato
\textit{cgi-bin} 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 processi, e 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.
+ 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
WriteMess("output pipe creation error");
exit(0);
}
- /* fork child to run barcode program */
- pid = fork();
- if (pid == -1) {
+ /* First fork: use child to run barcode program */
+ if ( (pid = fork()) == -1) { /* on error exit */
WriteMess("child creation error");
exit(0);
}
/* if child */
if (pid == 0) {
- close(pipein[1]); /* close output side of input pipe */
- dup2(0, pipein[0]); /* remap stdin in pipe input */
- close(pipeout[0]); /* close input side of output pipe */
- dup2(1, pipeout[1]); /* remap stdout in pipe output */
- execlp("barcode", "barcode", "-E", NULL);
- } else {
- /* first set the pipe */
- close(pipein[0]); /* close input side of input pipe */
- close(pipeout[1]); /* close output side of output pipe */
- write(pipein[1], argv[1], strlen(argv[1]));
- close(pipein[1]);
- waitpid(pid, NULL, 0);
- pid = fork();
- if (pid == -1) {
- WriteMess("child creation error")
- exit(0);
- }
- if (pid == 0) {
- /* send mime type */
- write(0,content, strlen(content));
- dup2(0, pipeout[0]);
- execlp("gs", "gs", "-sDEVICE=jpeg", "-sOutputFile=-", "-", NULL);
- } else {
- close(pipeout[0]);
- waitpid(pid, NULL, 0);
- }
+ close(pipein[1]); /* close pipe write end */
+ dup2(pipein[0], STDIN_FILENO); /* remap stdin to pipe read end */
+ close(pipeout[0]);
+ dup2(pipeout[1], STDOUT_FILENO); /* remap stdout in pipe output */
+ execlp("barcode", "barcode", size, NULL); //"-o", "-", NULL);
+ }
+ close(pipein[0]); /* close input side of input pipe */
+ write(pipein[1], argv[1], strlen(argv[1])); /* write parameter to pipe */
+ close(pipein[1]); /* closing write end */
+ waitpid(pid, NULL, 0); /* wait child completion */
+ /* Second fork: use child to run ghostscript */
+ if ( (pid = fork()) == -1) { /* on error exit */
+ WriteMess("child creation error");
+ exit(0);
+ }
+ /* second child, convert PS to JPEG */
+ if (pid == 0) {
+ close(pipeout[1]); /* close write end */
+ dup2(pipeout[0], STDIN_FILENO); /* remap read end to stdin */
+ /* send mime type */
+ write(STDOUT_FILENO, content, strlen(content));
+ execlp("gs", "gs", "-q", "-sDEVICE=jpeg", "-sOutputFile=-", "-", NULL);
}
+ /* still parent */
+ close(pipeout[1]);
+ waitpid(pid, NULL, 0);
exit(0);
}
\end{lstlisting}
\end{figure}
-Il primo passo (\texttt{\small 4-12}) è quello di creare le due pipe, una per
-l'input e l'altra per l'output, che servono per la comunicazione con i due
-programmi che verranno utilizzati, inviando in caso di errore (attraverso una
-apposita funzione \func{WriteMess}, non riportata in
-\ref{fig:ipc_barcode_code}, che si incarica di formattare l'output in HTML
-perché sia interpretabile da un browser) un messaggio invece dell'immagine
-richiesta.
+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
+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.}
-Una volta create le pipe il programma può creare (\texttt{\small 13-18}) 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
-\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 questo il processo figlio prima chiude (\texttt{\small 21}) il capo aperto
-in scrittura della prima pipe (che sarà usato dal padre per trasmettergli la
-stringa da codificare), e poi collega (\texttt{\small 22}) il capo il 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.
+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
+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
\subsection{Il comportamento generale del sistema.}
- \label{sec:sig_gen_beha}
+\label{sec:sig_gen_beha}
Abbiamo già trattato in \secref{sec:sig_intro} le modalità con cui il sistema
gestisce l'interazione fra segnali e processi, ci resta da esaminare però il
* http://localhost/cgi-bin/barcode?string
* where string is the code to be converted
*
- * $Id: BarCode.c,v 1.1 2002/06/18 22:11:06 piccardi Exp $
+ * $Id: BarCode.c,v 1.2 2002/06/21 22:24:10 piccardi Exp $
*
****************************************************************/
/*
/*
* Variables definition
*/
- char buffer[8192];
pid_t pid;
- size_t n;
int retval;
int pipein[2];
int pipeout[2];
- char size[]="-pA9";
- char psize[]="-sPAPERSIZE=a9";
char content[]="Content-type: image/jpeg\n\n";
+ char size[]="-pA9";
/*
* Begin
*/
if (pid == 0) {
/*
* Child exec barcode program, that take input (string to encode)
- * from pipein, remapped to stdin, and write the output (a ppm
+ * 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 */
/*
* Parent write string to pipe input and close it,
* then wait child execution and results form pipeout,
- * then fork to convert ppm to gif using ppmtogif
+ * then fork to convert PS to JPEG using gs
*/
close(pipein[0]); /* close input side of input pipe */
- n=write(pipein[1], argv[1], strlen(argv[1]));
+ write(pipein[1], argv[1], strlen(argv[1]));
close(pipein[1]);
waitpid(pid, NULL, 0);
/*
- * refork to use ppmtogif
+ * refork to use gs
*/
pid = fork();
if (pid == -1) {
exit(0);
}
/*
- * second child, convert ppm to gif
+ * 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));
- n=read(pipeout[0], buffer, sizeof(buffer));
- printf("Letti %n di %n, %s\n", n, sizeof(buffer), buffer);
- exit(0);
execlp("gs", "gs", "-q", "-sDEVICE=jpeg", "-sOutputFile=-", "-", NULL);
}
/*