X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=4415602bcce799513de037ee02f7867f09dfc093;hp=f29a8efe9e54bef4465268860912445ac8f37487;hb=c48d3a056c974898ca18c27a918f2296c18e2c6e;hpb=5eaf11a5e3e4daaee677554b68f4ba3c92c67b84 diff --git a/fileadv.tex b/fileadv.tex index f29a8ef..4415602 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -1,6 +1,6 @@ %% fileadv.tex %% -%% Copyright (C) 2000-2002 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2003 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free %% Documentation License, Version 1.1 or any later version published by the %% Free Software Foundation; with the Invariant Sections being "Prefazione", @@ -70,15 +70,15 @@ I/O non bloccante. \label{sec:file_multiplexing} Per superare il problema di dover usare il \textit{polling}\index{polling} per -controllare la possibilità di effettuare operazioni su un file aperto in -modalità non bloccante, sia BSD che System V hanno introdotto delle nuove -funzioni in grado di sospendere l'esecuzione di un processo in attesa che -l'accesso diventi possibile. Il primo ad introdurre questa modalità di -operazione, chiamata usualmente \textit{I/O multiplexing}, è stato -BSD,\footnote{la funzione è apparsa in BSD4.2 e standardizzata in BSD4.4, ma è - stata portata su tutti i sistemi che supportano i - \textit{socket}\index{socket}, compreso le varianti di System V.} con la -funzione \funcd{select}, il cui prototipo è: +controllare la possibilità di effettuare operazioni su un gruppo di file +aperti in modalità non bloccante, sia BSD che System V hanno introdotto delle +nuove funzioni in grado di sospendere l'esecuzione di un processo fin quando +l'accesso ad un dato insieme di file diventi possibile. Il primo ad +introdurre questa modalità di operazione, chiamata usualmente \textit{I/O + multiplexing}, è stato BSD,\footnote{la funzione è apparsa in BSD4.2 e + standardizzata in BSD4.4, ma è stata portata su tutti i sistemi che + supportano i \textit{socket}\index{socket}, compreso le varianti di System + V.} con la funzione \funcd{select}, il cui prototipo è: \begin{functions} \headdecl{sys/time.h} \headdecl{sys/types.h} @@ -135,10 +135,14 @@ opportune macro di preprocessore: In genere un \textit{file descriptor set} può contenere fino ad un massimo di \const{FD\_SETSIZE} file descriptor. Questo valore in origine corrispondeva al limite per il numero massimo di file aperti\footnote{ad esempio in Linux, - fino alla serie 2.0.x, c'era un limite di 256 file per processo.}, ma + fino alla serie 2.0.x, c'era un limite di 256 file per processo.}, ma da quando, come nelle versioni più recenti del kernel, non c'è più un limite massimo, esso indica le dimensioni massime dei numeri usati nei \textit{file - descriptor set}. + descriptor set}.\footnote{il suo valore, secondo lo standard POSIX + 1003.1-2001, è definito in \file{sys/select.h}, ed è pari a 1024.} Si tenga +presente che i \textit{file descriptor set} devono sempre essere inizializzati +con \macro{FD\_ZERO}; passare a \func{select} un valore non inizializzato può +dar luogo a comportamenti non prevedibili. La funzione richiede di specificare tre insiemi distinti di file descriptor; il primo, \param{readfds}, verrà osservato per rilevare la disponibilità di @@ -147,17 +151,24 @@ possibilit verificare l'esistenza di condizioni eccezionali (come i messaggi urgenti su un \textit{socket}\index{socket}, vedi \secref{sec:xxx_urgent}). -La funzione inoltre richiede anche di specificare, tramite l'argomento -\param{n}, un valore massimo del numero dei file descriptor usati -nell'insieme; si può usare il già citato \const{FD\_SETSIZE}, oppure il numero -più alto dei file descriptor usati nei tre insiemi, aumentato di uno. +Dato che in genere non si tengono mai sotto controllo fino a +\const{FD\_SETSIZE} file contemporaneamente la funzione richiede di +specificare qual'è il numero massimo dei file descriptor indicati nei tre +insiemi precedenti. Questo viene fatto per efficienza, per evitare di passare +e far controllare al kernel una quantità di memoria superiore a quella +necessaria. Questo limite viene indicato tramite l'argomento \param{n}, che +deve corrispondere al valore massimo aumentato di uno.\footnote{i file + descriptor infatti sono contati a partire da zero, ed il valore indica il + numero di quelli da tenere sotto controllo; dimenticarsi di aumentare di uno + il valore di \param{n} è un errore comune.} Infine l'argomento \param{timeout}, specifica un tempo massimo di -attesa\footnote{il tempo è valutato come \textit{elapsed time}.} prima che la -funzione ritorni; se impostato a \val{NULL} la funzione attende -indefinitamente. Si può specificare anche un tempo nullo (cioè una struttura -\struct{timeval} con i campi impostati a zero), qualora si voglia -semplicemente controllare lo stato corrente dei file descriptor. +attesa\footnote{il tempo è valutato come \textit{clock time} (vedi + \secref{sec:sys_unix_time}).} prima che la funzione ritorni; se impostato a +\val{NULL} la funzione attende indefinitamente. Si può specificare anche un +tempo nullo (cioè una struttura \struct{timeval} con i campi impostati a +zero), qualora si voglia semplicemente controllare lo stato corrente dei file +descriptor. La funzione restituisce il totale dei file descriptor pronti nei tre insiemi, il valore zero indica sempre che si è raggiunto un timeout. Ciascuno dei tre @@ -167,35 +178,35 @@ operazioni ad esso relative, in modo da poterlo controllare con la macro non vengono toccati. In Linux \func{select} modifica anche il valore di \param{timeout}, -impostandolo al tempo restante; questo è utile quando la funzione viene -interrotta da un segnale, in tal caso infatti si ha un errore di -\errcode{EINTR}, ed occorre rilanciare la funzione; in questo modo non è -necessario ricalcolare tutte le volte il tempo rimanente.\footnote{questo può - causare problemi di portabilità sia quando si trasporta codice scritto su - Linux che legge questo valore, sia quando si usano programmi scritti per - altri sistemi che non dispongono di questa caratteristica e ricalcolano - \param{timeout} tutte le volte. In genere la caratteristica è disponibile - nei sistemi che derivano da System V e non disponibile per quelli che - derivano da BSD.} - -Come accennato l'interfaccia di \func{select} è una estensione di BSD; anche -System V ha introdotto una sua interfaccia per gestire l'\textit{I/O - multiplexing}, basata sulla funzione \funcd{poll},\footnote{la funzione è - prevista dallo standard XPG4, ed è stata introdotta in Linux come system - call a partire dal kernel 2.1.23 e dalle \acr{libc} 5.4.28.} il cui +impostandolo al tempo restante in caso di interruzione prematura; questo è +utile quando la funzione viene interrotta da un segnale, in tal caso infatti +si ha un errore di \errcode{EINTR}, ed occorre rilanciare la funzione; in +questo modo non è necessario ricalcolare tutte le volte il tempo +rimanente.\footnote{questo può causare problemi di portabilità sia quando si + trasporta codice scritto su Linux che legge questo valore, sia quando si + usano programmi scritti per altri sistemi che non dispongono di questa + caratteristica e ricalcolano \param{timeout} tutte le volte. In genere la + caratteristica è disponibile nei sistemi che derivano da System V e non + disponibile per quelli che derivano da BSD.} + +Come accennato l'interfaccia di \func{select} è una estensione creata nello +sviluppo di BSD; anche System V ha introdotto una sua interfaccia per gestire +l'\textit{I/O multiplexing}, basata sulla funzione \funcd{poll},\footnote{la + funzione è prevista dallo standard XPG4, ed è stata introdotta in Linux come + system call a partire dal kernel 2.1.23 e dalle \acr{libc} 5.4.28.} il cui prototipo è: \begin{prototype}{sys/poll.h} {int poll(struct pollfd *ufds, unsigned int nfds, int timeout)} - -La funzione attente un cambiamento di stato per uno dei file descriptor -specificati da \param{ufds}. + + La funzione attende un cambiamento di stato per uno dei file descriptor + specificati da \param{ufds}. \bodydesc{La funzione restituisce il numero di file descriptor con attività in caso di successo, o 0 se c'è stato un timeout; in caso di errore viene restituito -1 ed \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno - degli insiemi. + degli insiemi. \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. \end{errlist} ed inoltre \errval{EFAULT} e \errval{ENOMEM}.} @@ -211,17 +222,11 @@ negativo indica un'attesa indefinita). \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} -struct pollfd { - int fd; /* file descriptor */ - short events; /* requested events */ - short revents; /* returned events */ -}; - \end{lstlisting} + \includestruct{listati/pollfd.h} \end{minipage} \normalsize - \caption{La struttura \struct{pollfd}, utilizzata per specificare le modalità - di controllo di un file descriptor alla funzione \func{poll}.} + \caption{La struttura \structd{pollfd}, utilizzata per specificare le + modalità di controllo di un file descriptor alla funzione \func{poll}.} \label{fig:file_pollfd} \end{figure} @@ -254,7 +259,8 @@ vengono utilizzati solo per \var{revents} come valori in uscita). \const{POLLWRNORM}& 0x100 & È possibile la scrittura di dati normali. \\ \const{POLLWRBAND}& 0x200 & È possibile la scrittura di dati ad alta priorità. \\ - \const{POLLMSG} & 0x400 & Estensione propria di Linux.\\ + \const{POLLMSG} & 0x400 & Un segnale \const{SIGPOLL} è arrivato alla + cima dello stream.\\ \hline \end{tabular} \caption{Costanti per l'identificazione dei vari bit dei campi @@ -321,6 +327,15 @@ segnale che non sarebbe rilevato; la race condition\index{race condition} diventa superabile disabilitando il segnale prima del test e riabilitandolo poi grazie all'uso di \param{sigmask}. +Dato che l'I/O multiplexing serve a risolvere il problema di dover attendere +la disponibilità di accesso ad un insieme di file, esso viene utilizzato +prevalentemente per programmi in cui l'accesso ad un file descriptor può +essere bloccante. Abbiamo già accennato come questo non avvenga mai per i +normali file su disco; l'uso più comune di queste funzioni infatti è nei +server di rete, in cui esse vengono utilizzate per tenere sotto controllo vari +socket; pertanto ritorneremo su di esse con maggiori dettagli e con qualche +esempio in \secref{sec:TCP_sock_multiplexing}. + \subsection{L'I/O asincrono} @@ -405,9 +420,10 @@ di thread. Al momento\footnote{fino ai kernel della serie 2.4.x, nella serie di I/O, che prevede anche l'introduzione di un nuovo layer per l'I/O asincrono (effettuato a partire dal 2.5.32).} esiste una sola versione stabile di questa interfaccia, quella delle \acr{glibc}, che è realizzata -completamente in user space. Esistono comunque vari progetti sperimentali -(come il KAIO della SGI, o i patch di Benjamin La Haise) che prevedono un -supporto diretto da parte del kernel. +completamente in user space, ed accessibile linkando i programmi con la +libreria \file{librt}. Esistono comunque vari progetti sperimentali (come il +KAIO della SGI, o i patch di Benjamin La Haise) che prevedono un supporto +diretto da parte del kernel. Lo standard prevede che tutte le operazioni di I/O asincrono siano controllate attraverso l'uso di una apposita struttura \struct{aiocb} (il cui nome sta per @@ -420,21 +436,10 @@ disponibilit \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} -struct aiocb -{ - int aio_fildes; /* File descriptor. */ - off_t aio_offset; /* File offset */ - int aio_lio_opcode; /* Operation to be performed. */ - int aio_reqprio; /* Request priority offset. */ - volatile void *aio_buf; /* Location of buffer. */ - size_t aio_nbytes; /* Length of transfer. */ - struct sigevent aio_sigevent; /* Signal number and value. */ -}; - \end{lstlisting} + \includestruct{listati/aiocb.h} \end{minipage} \normalsize - \caption{La struttura \struct{aiocb}, usata per il controllo dell'I/O + \caption{La struttura \structd{aiocb}, usata per il controllo dell'I/O asincrono.} \label{fig:file_aiocb} \end{figure} @@ -470,20 +475,11 @@ esse. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} -struct sigevent -{ - sigval_t sigev_value; - int sigev_signo; - int sigev_notify; - sigev_notify_function; - sigev_notify_attributes; -}; - \end{lstlisting} + \includestruct{listati/sigevent.h} \end{minipage} \normalsize - \caption{La struttura \struct{sigevent}, usata per specificare le modalità di - notifica degli eventi relativi alle operazioni di I/O asincrono.} + \caption{La struttura \structd{sigevent}, usata per specificare le modalità + di notifica degli eventi relativi alle operazioni di I/O asincrono.} \label{fig:file_sigevent} \end{figure} @@ -493,15 +489,17 @@ notifica del completamento delle operazioni richieste. La struttura riportata in \secref{fig:file_sigevent}; il campo \var{sigev\_notify} è quello che indica le modalità della notifica, esso può assumere i tre valori: \begin{basedescript}{\desclabelwidth{3.0cm}} -\item[\const{SIGEV\_NONE}] Non viene inviata nessuna notifica. +\item[\const{SIGEV\_NONE}] Non viene inviata nessuna notifica. \item[\const{SIGEV\_SIGNAL}] La notifica viene effettuata inviando al processo - chiamante il segnale specificato nel campo \var{sigev\_signo}, se il - gestore è installato con \const{SA\_SIGINFO}, il gli verrà restituito - il valore di \var{sigev\_value} in come valore del campo \var{si\_value} per + chiamante il segnale specificato da \var{sigev\_signo}; se il gestore di + questo è stato installato con \const{SA\_SIGINFO} gli verrà restituito il + valore di \var{sigev\_value} (la cui definizione è in + \figref{fig:sig_sigval}) come valore del campo \var{si\_value} di \struct{siginfo\_t}. \item[\const{SIGEV\_THREAD}] La notifica viene effettuata creando un nuovo - thread che esegue la funzione specificata da \var{sigev\_notify\_function}, - con gli attributi specificati da \var{sigev\_notify\_attribute}. + thread che esegue la funzione specificata da \var{sigev\_notify\_function} + con argomento \var{sigev\_value}, e con gli attributi specificati da + \var{sigev\_notify\_attribute}. \end{basedescript} Le due funzioni base dell'interfaccia per l'I/O asincrono sono @@ -815,15 +813,10 @@ il secondo, \var{iov\_len}, la dimensione dello stesso. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} -struct iovec { - __ptr_t iov_base; /* Starting address */ - size_t iov_len; /* Length in bytes */ -}; - \end{lstlisting} + \includestruct{listati/iovec.h} \end{minipage} \normalsize - \caption{La struttura \struct{iovec}, usata dalle operazioni di I/O + \caption{La struttura \structd{iovec}, usata dalle operazioni di I/O vettorizzato.} \label{fig:file_iovec} \end{figure} @@ -1130,14 +1123,14 @@ Pertanto se si modifica un file con l'interfaccia standard queste modifiche potranno essere visibili o meno a seconda del momento in cui la memoria virtuale trasporterà dal disco in memoria quella sezione del file, perciò è del tutto imprevedibile il risultato della modifica di un file nei confronti -del contenuto della memoria mappata su cui è mappato. - -Per quanto appena visto, è sempre sconsigliabile eseguire scritture su file -attraverso l'interfaccia standard, quando lo si è mappato in memoria, è invece -possibile usare l'interfaccia standard per leggere un file mappato in memoria, -purché si abbia una certa cura; infatti l'interfaccia dell'I/O mappato in -memoria mette a disposizione la funzione \funcd{msync} per sincronizzare il -contenuto della memoria mappata con il file su disco; il suo prototipo è: +del contenuto della memoria su cui è mappato. + +Per questo, è sempre sconsigliabile eseguire scritture su file attraverso +l'interfaccia standard, quando lo si è mappato in memoria, è invece possibile +usare l'interfaccia standard per leggere un file mappato in memoria, purché si +abbia una certa cura; infatti l'interfaccia dell'I/O mappato in memoria mette +a disposizione la funzione \funcd{msync} per sincronizzare il contenuto della +memoria mappata con il file su disco; il suo prototipo è: \begin{functions} \headdecl{unistd.h} \headdecl{sys/mman.h} @@ -1509,18 +1502,10 @@ regione bloccata. \begin{figure}[!bht] \footnotesize \centering \begin{minipage}[c]{15cm} - \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} -struct flock { - short int l_type; /* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK. */ - short int l_whence; /* Where `l_start' is relative to (like `lseek'). */ - off_t l_start; /* Offset where the lock begins. */ - off_t l_len; /* Size of the locked area; zero means until EOF. */ - pid_t l_pid; /* Process holding the lock. */ -}; - \end{lstlisting} + \includestruct{listati/flock.h} \end{minipage} \normalsize - \caption{La struttura \struct{flock}, usata da \func{fcntl} per il file + \caption{La struttura \structd{flock}, usata da \func{fcntl} per il file locking.} \label{fig:struct_flock} \end{figure} @@ -1714,60 +1699,7 @@ necessario a soddisfare l'operazione richiesta. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \begin{lstlisting}{} -int main(int argc, char *argv[]) -{ - int type = F_UNLCK; /* lock type: default to unlock (invalid) */ - off_t start = 0; /* start of the locked region: default to 0 */ - off_t len = 0; /* length of the locked region: default to 0 */ - int fd, res, i; /* internal variables */ - int bsd = 0; /* semantic type: default to POSIX */ - int cmd = F_SETLK; /* lock command: default to non-blocking */ - struct flock lock; /* file lock structure */ - ... - if ((argc - optind) != 1) { /* There must be remaing parameters */ - printf("Wrong number of arguments %d\n", argc - optind); - usage(); - } - if (type == F_UNLCK) { /* There must be a -w or -r option set */ - printf("You should set a read or a write lock\n"); - usage(); - } - fd = open(argv[optind], O_RDWR); /* open the file to be locked */ - if (fd < 0) { /* on error exit */ - perror("Wrong filename"); - exit(1); - } - /* do lock */ - if (bsd) { /* if BSD locking */ - /* rewrite cmd for suitables flock operation values */ - if (cmd == F_SETLKW) { /* if no-blocking */ - cmd = LOCK_NB; /* set the value for flock operation */ - } else { /* else */ - cmd = 0; /* default is null */ - } - if (type == F_RDLCK) cmd |= LOCK_SH; /* set for shared lock */ - if (type == F_WRLCK) cmd |= LOCK_EX; /* set for exclusive lock */ - res = flock(fd, cmd); /* esecute lock */ - } else { /* if POSIX locking */ - /* setting flock structure */ - lock.l_type = type; /* set type: read or write */ - lock.l_whence = SEEK_SET; /* start from the beginning of the file */ - lock.l_start = start; /* set the start of the locked region */ - lock.l_len = len; /* set the length of the locked region */ - res = fcntl(fd, cmd, &lock); /* do lock */ - } - /* check lock results */ - if (res) { /* on error exit */ - perror("Failed lock"); - exit(1); - } else { /* else write message */ - printf("Lock acquired\n"); - } - pause(); /* stop the process, use a signal to exit */ - return 0; -} - \end{lstlisting} + \includecodesample{listati/Flock.c} \end{minipage} \normalsize \caption{Sezione principale del codice del programma \file{Flock.c}.}