From: Simone Piccardi Date: Fri, 21 Jun 2002 22:24:10 +0000 (+0000) Subject: varie su read/pread/dup/dup2 ecc. X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=a29d9eebcd859ca662351848cc856e69f50c3ece varie su read/pread/dup/dup2 ecc. --- diff --git a/fileunix.tex b/fileunix.tex index 8428e10..7eddb14 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -538,44 +538,43 @@ come valore di ritorno. 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}. @@ -583,10 +582,15 @@ Cerca di leggere \var{count} byte dal file \var{fd}, a partire dalla posizione 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. @@ -881,7 +885,8 @@ interscambiati nell'uso. Per capire meglio il funzionamento della funzione si 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} @@ -889,18 +894,30 @@ nella \textit{file table}. \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}. @@ -915,11 +932,12 @@ specificare esplicitamente il nuovo file descriptor; il suo prototipo 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 @@ -928,12 +946,6 @@ se 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} diff --git a/ipc.tex b/ipc.tex index 21fae71..0b7818c 100644 --- a/ipc.tex +++ b/ipc.tex @@ -1,4 +1,4 @@ -f\chapter{La comunicazione fra processi} +\chapter{La comunicazione fra processi} \label{cha:IPC} @@ -110,9 +110,9 @@ 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{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 @@ -136,10 +136,10 @@ intermedio su un file temporaneo. Questo per \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 @@ -167,41 +167,39 @@ int main(int argc, char *argv[], char *envp[]) 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} @@ -212,28 +210,34 @@ int main(int argc, char *argv[], char *envp[]) \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 diff --git a/signal.tex b/signal.tex index 519cf44..4a81828 100644 --- a/signal.tex +++ b/signal.tex @@ -750,7 +750,7 @@ processo alla loro occorrenza. \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 diff --git a/sources/BarCode.c b/sources/BarCode.c index 138e23a..7852dae 100644 --- a/sources/BarCode.c +++ b/sources/BarCode.c @@ -29,7 +29,7 @@ * 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 $ * ****************************************************************/ /* @@ -57,15 +57,12 @@ int main(int argc, char *argv[], char *envp[]) /* * 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 */ @@ -88,7 +85,7 @@ int main(int argc, char *argv[], char *envp[]) 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 */ @@ -100,14 +97,14 @@ int main(int argc, char *argv[], char *envp[]) /* * 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) { @@ -115,16 +112,13 @@ int main(int argc, char *argv[], char *envp[]) 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); } /*