X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=7a47057864ed8a3403948cf6bebbc495e5c9be4d;hp=d744a758905558b00fe24189ce811a6b41c9a064;hb=5c88e6587e9e392c26ba943c719be9caf7fab0d9;hpb=38dff25efc0d3ae24d82724cd196c583c2362737 diff --git a/fileadv.tex b/fileadv.tex index d744a75..7a47057 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -31,12 +31,11 @@ disponibili sul descrittore su cui si sta operando. Questo comportamento causa uno dei problemi più comuni che ci si trova ad affrontare nelle operazioni di I/O, che è quello che si verifica quando si devono eseguire operazioni che possono bloccarsi su più file descriptor: -mentre si è bloccati su uno di questi file su di un'altro potrebbero essere -presenti dei dati, così che nel migliore dei casi si avrebbe una lettura -ritardata inutilmente, e nel peggiore si potrebbe addirittura arrivare ad un -deadlock. +mentre si è bloccati su uno di essi su di un'altro potrebbero essere presenti +dei dati; così che nel migliore dei casi si avrebbe una lettura ritardata +inutilmente, e nel peggiore si potrebbe addirittura arrivare ad un deadlock. -Abbiamo già accennato in \secref{sec:file_open} che però è possibile prevenire +Abbiamo già accennato in \secref{sec:file_open} che è possibile prevenire questo tipo di comportamento aprendo un file in modalità \textsl{non-bloccante}, attraverso l'uso del flag \macro{O\_NONBLOCK} nella chiamata di \func{open}. In questo caso le funzioni di input/output che @@ -92,7 +91,7 @@ con la funzione \func{select}, il cui prototipo La funzione mette il processo in stato di \textit{sleep} (vedi \tabref{tab:proc_proc_states}) fintanto che almeno uno dei file descriptor -degli insiemo specificati (\param{readfds}, \param{writefds} e +degli insiemi specificati (\param{readfds}, \param{writefds} e \param{exceptfds}), non diventa attivo, per un tempo massimo specificato da \param{timeout}. @@ -169,7 +168,7 @@ Come accennato l'interfaccia di \func{select} System V ha introdotto una sua interfaccia per gestire l'\textit{I/O multiplexing}, basata sulla funzione \func{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 libc 5.4.28.} il cui prototipo è: + 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)} @@ -192,7 +191,7 @@ specificati attraverso un vettore di puntatori a strutture di tipo \type{pollfd}, la cui definizione è riportata in \figref{fig:file_pollfd}. Come \func{select} anche \func{poll} permette di interrompere l'attesa dopo un certo tempo, che va specificato attraverso \param{timeout} in numero di -millesecondi (un valore negativo indica un'attesa indefinita). +millisecondi (un valore negativo indica un'attesa indefinita). \begin{figure}[!htb] \footnotesize \centering @@ -231,7 +230,7 @@ vengono utilizzati solo per \var{revents} come valori in uscita). \macro{POLLOUT} & 0x004 & È possibile la scrittura immediata.\\ \hline \macro{POLLERR} & 0x008 & C'è una condizione di errore.\\ - \macro{POLLHUP} & 0x010 & Si è vericato un hung-up.\\ + \macro{POLLHUP} & 0x010 & Si è verificato un hung-up.\\ \macro{POLLNVAL} & 0x020 & Il file descriptor non è aperto.\\ \hline \macro{POLLRDNORM}& 0x040 & Sono disponibili in lettura dati normali.\\ @@ -263,7 +262,7 @@ ad esso relative vengano dichiarate nell'header \file{sys/select.h}, che sostituisce i precedenti, ed aggiunge a \func{select} una nuova funzione \func{pselect},\footnote{il supporto per lo standard POSIX 1003.1-2001, ed l'header \file{sys/select.h}, compaiono in Linux a partire dalle \acr{glibc} - 2.0. Le \acr{libc4} e \acr{libc5} non contengono questo header, le + 2.1. Le \acr{libc4} e \acr{libc5} non contengono questo header, le \acr{glibc} 2.0 contengono una definizione sbagliata di \func{psignal}, senza l'argomento \param{sigmask}, la definizione corretta è presente dalle \acr{glibc} 2.1-2.2.1 se si è definito \macro{\_GNU\_SOURCE} e nelle @@ -325,7 +324,7 @@ del flag \macro{O\_ASYNC},\footnote{l'uso del flag di \macro{O\_ASYNC} e dei di Linux e BSD.} aprire un file in modalità asincrona, così come è possibile attivare in un secondo tempo questa modalità settando questo flag attraverso l'uso di \func{fcntl} con il comando \macro{F\_SETFL} (vedi -\secref{sec:file_fcntl}). +\secref{sec:file_fcntl}). In realtà in questo caso non si tratta di I/O asincrono vero e proprio, quanto di un meccanismo asincrono di notifica delle variazione dello stato del file @@ -333,42 +332,54 @@ descriptor; quello che succede \macro{SIGIO}, ma è possibile usarne altri) tutte le volte che diventa possibile leggere o scrivere dal file descriptor che si è posto in questa modalità. Si può inoltre selezionare, con il comando \macro{F\_SETOWN} di -\func{fcntl}, quale processo (o gruppo di processi) riceverà il segnale. - -Uno dei problemi che si presenta con l'implementazione usuale di questa -modalità di I/O è che essa può essere usata in maniera immediata aprendo in -modalità asincrona un solo file per processo, altrimenti ad ogni segnale si -dovrebbe provvedere ad effettuare un controllo (utilizzando di nuovo -\func{select}) su tutti i file tenuti in modalità asincrona per distinguere -quelli cui è dovuta l'emissione del segnale. - -Linux però supporta una estensione che permette di evitare tutto questo -facendo ricorso alle informazioni aggiuntive restituite attraverso la -struttura \type{siginfo\_t} quando il manipolatore del segnale viene -installato come \macro{SA\_SIGINFO} (si riveda quanto illustrato in +\func{fcntl}, quale processo (o gruppo di processi) riceverà il segnale. + +In questo modo si può evitare l'uso delle funzioni \func{poll} o \func{select} +che, quando vengono usate con un numero molto grande di file descriptor, non +hanno buone prestazioni. In tal caso infatti la maggior parte del loro tempo +di esecuzione è impegnato ad eseguire una scansione su tutti i file descriptor +tenuti sotto controllo per determinare quali di essi (in genere una piccola +percentuale) sono diventati attivi. + +Tuttavia con l'implementazione classica dei segnali questa modalità di I/O +presenta notevoli problemi, dato che non è possibile determinare, quando sono +più di uno, qual'è il file descriptor responsabile dell'emissione del segnale. +Linux però supporta le estensioni POSIX.1b dei segnali che permettono di +superare il problema facendo ricorso alle informazioni aggiuntive restituite +attraverso la struttura \type{siginfo\_t}, utilizzando la forma estesa +\var{sa\_sigaction} del manipolatore (si riveda quanto illustrato in \secref{sec:sig_sigaction}). -Per attivare questa caratteristica occorre settare esplicitamente il segnale -da inviare in caso di I/O asincrono (di norma sempre \macro{SIGIO}) con il -comando \macro{F\_SETSIG} di \func{fcntl}. In questo caso il manipolatore -tutte le volte che riceverà \macro{SI\_SIGIO} come valore del campo -\var{si\_code}\footnote{il valore resta \macro{SI\_SIGIO} qualunque sia il - segnale che si è associato all'I/O asincrono, ed indica appunto che il +Per far questo però occorre utilizzare le funzionalità dei segnali real-time +(vedi \secref{sec:sig_real_time}) settando esplicitamente con il comando +\macro{F\_SETSIG} di \func{fcntl} un segnale real-time da inviare in caso di +I/O asincrono (il segnale di default è \macro{SIGIO}). In questo caso il +manipolatore tutte le volte che riceverà \macro{SI\_SIGIO} come valore del +campo \var{si\_code}\footnote{il valore resta \macro{SI\_SIGIO} qualunque sia + il segnale che si è associato all'I/O asincrono, ed indica appunto che il segnale è stato generato a causa di attività nell'I/O asincrono.} di \type{siginfo\_t}, troverà nel campo \var{si\_fd} il valore del file -descriptor che ha generato il segnale. In questo modo è possibile identificare -immediatamente il file evitando completamente l'uso di funzioni come -\func{poll} o \func{select}. Inoltre, a differenza degli altri segnali, il -sistema mantiene una coda per \macro{SIGIO}, in modo che arrivi un segnale per -ogni file attivo. +descriptor che ha generato il segnale. + +Un secondo vantaggio dell'uso dei segnali real-time è che essendo dotati di +una coda di consegna ogni segnale sarà associato ad uno solo file descriptor; +inoltre sarà possibile stabilire delle priorità nella risposta a seconda del +segnale usato. In questo modo si può identificare immediatamente un file su +cui l'accesso è diventato possibile evitando completamente l'uso di funzioni +come \func{poll} e \func{select}, almeno fintanto che non si satura la coda; +si eccedono le dimensioni di quest'ultima; in tal caso infatti il kernel, non +potendo più assicurare il comportamento corretto per un segnale real-time, +invierà al suo posto un \var{SIGIO}, su cui si accumuleranno tutti i segnali +in eccesso, e si dovrà determinare al solito modo quali sono i file diventati +attivi. Benché la modalità di apertura asincrona di un file possa risultare utile in varie occasioni (in particolar modo con i socket e gli altri file per i quali le funzioni di I/O sono system call lente), essa è comunque limitata alla notifica della disponibilità del file descriptor per le operazioni di I/O, e non ad uno svolgimento asincrono delle medesime. Lo standard POSIX.1b -definisce invece una interfaccia apposita per l'I/O asincrono, che prevede un -insieme di funzioni dedicate, completamente separato rispetto a quelle usate +definisce anche una interfaccia apposita per l'I/O asincrono, che prevede un +insieme di funzioni dedicate, completamente separate rispetto a quelle usate normalmente. In generale questa interfaccia è completamente astratta e può essere @@ -376,29 +387,518 @@ implementata sia direttamente nel kernel, che in user space attraverso l'uso di thread. Al momento\footnote{fino ai kernel della serie 2.4.x, nella serie 2.5.x è però iniziato un lavoro completo di riscrittura di tutto il sistema di I/O, che prevede anche l'introduzione di un nuovo layer per l'I/O - asincrono.} esiste una sola versione stabile, quella delle \acr{glibc}, che -è realizzata completamente in user space; esistono comunque vari progetti -(come il KAIO della SGI, o i patch di Benjamin La Haise) che prevedono un -supporto diretto all'interno del kernel. + asincrono.} 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. + +Lo standard prevede che tutte le operazioni di I/O asincrono siano controllate +attraverso l'uso di una apposita struttura \type{aiocb} (il cui nome sta per +\textit{asyncronous I/O control block}), che viene passata come argomento a +tutte le funzioni dell'interfaccia. La sua definizione, come effettuata in +\file{aio.h}, è riportata in \figref{fig:file_aiocb}. Nello steso file è +definita la macro \macro{\_POSIX\_ASYNCHRONOUS\_IO}, che dichiara la +disponibilità dell'interfaccia per l'I/O asincrono. +\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} + \end{minipage} + \normalsize + \caption{La struttura \type{aiocb}, usata per il controllo dell'I/O + asincrono.} + \label{fig:file_aiocb} +\end{figure} + +Le operazioni di I/O asincrono possono essere effettuate solo su un file già +aperto; il file deve inoltre supportare la funzione \func{lseek}, +pertanto terminali e pipe sono esclusi. Non c'è limite al numero di operazioni +contemporanee effettuabili su un singolo file. + +Ogni operazione deve inizializzare opportunamente un \textit{control block}. +Il file descriptor su cui operare deve essere specificato tramite il campo +\var{aio\_fildes}; dato che più operazioni possono essere eseguita in maniera +asincrona, il concetto di posizione corrente sul file viene a mancare; +pertanto si deve sempre specificare nel campo \var{aio\_offset} la posizione +sul file da cui i dati saranno letti o scritti. Nel campo \var{aio\_buf} deve +essere specificato l'indirizzo del buffer usato per l'I/O, ed in +\var{aio\_nbytes} la lunghezza del blocco di dati da trasferire. + +Il campo \var{aio\_reqprio} permette di settare la priorità delle operazioni +di I/O.\footnote{in generale perché ciò sia possibile occorre che la + piattaforma supporti questa caratteristica, questo viene indicato definendo + le macro \macro{\_POSIX\_PRIORITIZED\_IO}, e + \macro{\_POSIX\_PRIORITY\_SCHEDULING}.} La priorità viene settata a partire +da quella del processo chiamante (vedi \secref{sec:proc_priority}), cui viene +sottratto il valore di questo campo. + +Il campo \var{aio\_lio\_opcode} è usato soltanto dalla funzione +\func{lio\_listio}, che, come vedremo più avanti, permette di eseguire con una +sola chiamanta una serie di operazioni, usando un vettore di \textit{control + block}. Tramite questo campo si specifica quale è la natura di ciascuna di +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} + \end{minipage} + \normalsize + \caption{La struttura \type{sigevent}, usata per specificare le modailtà di + notifica degli eventi relativi alle operazioni di I/O asincrono.} + \label{fig:file_sigevent} +\end{figure} +Infine il campo \var{aio\_sigevent} è una struttura di tipo \type{sigevent} +che serve a specificare il modo in cui si vuole che venga effettuata la +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[\macro{SIGEV\_NONE}] Non viene inviata nessuna notifica. +\item[\macro{SIGEV\_SIGNAL}] La notifica viene effettuata inviando al processo + chiamante il segnale specificato nel campo \var{sigev\_signo}, se il + manipolatore è installato con \macro{SA\_SIGINFO}, il gli verrà restituito + il valore di \var{sigev\_value} in come valore del campo \var{si\_value} per + \type{siginfo\_t}. +\item[\macro{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}. +\end{basedescript} + +Le due funzioni base dell'interfaccia per l'I/O asincrono sono +\func{aio\_read} ed \func{aio\_write}. Esse permettono di richiedere una +lettura od una scrittura asincrona di dati, usando la struttura \type{aiocb} +appena descritta; i rispettivi prototipi sono: +\begin{functions} + \headdecl{aio.h} + + \funcdecl{int aio\_read(struct aiocb *aiocbp)} + Richiede una lettura asincrona secondo quanto specificato con \param{aiocbp}. + + \funcdecl{int aio\_write(struct aiocb *aiocbp)} + Richiede una scrittura asincrona secondo quanto specificato con + \param{aiocbp}. + + \bodydesc{Le funzioni restituiscono 0 in caso di successo, e -1 in caso di + errore, nel qual caso \var{errno} viene settata ai valori: + \begin{errlist} + \item[\macro{EBADF}] Si è specificato un file descriptor sbagliato. + \item[\macro{ENOSYS}] La funzione non è implementata. + \item[\macro{EINVAL}] Si è specificato un valore non valido per i campi + \var{aio\_offset} o \var{aio\_reqprio} di \param{aiocbp}. + \item[\macro{EAGAIN}] La coda delle richieste è momentaneamente piena. + \end{errlist} +} +\end{functions} + +Entrambe le funzioni ritornano immediatamente dopo aver messo in coda la +richiesta, o in caso di errore. Non è detto che gli errori \macro{EBADF} ed +\macro{EINVAL} siano rilevati immediatamente al momento della chiamata, +potrebbero anche emergere nelle fasi successive delle operazioni. Lettura e +scrittura avvengono alla posizione indicata da \var{aio\_offset}, a meno che +il file non sia stato aperto in \textit{append mode} (vedi +\secref{sec:file_open}), nel qual caso le scritture vengono effettuate +comunque alla fine de file, nell'ordine delle chiamate a \func{aio\_write}. + +Si tenga inoltre presente che deallocare la memoria indirizzata da +\param{aiocbp} o modificarne i valori prima della conclusione di una +operazione può dar luogo a risultati impredicibili, perché l'accesso ai vari +campi per eseguire l'operazione può avvenire in un momento qualsiasi dopo la +richiesta. Questo comporta che occorre evitare di usare per \param{aiocbp} +variabili automatiche e che non si deve riutilizzare la stessa struttura per +un'ulteriore operazione fintanto che la precedente non sia stata ultimata. In +generale per ogni operazione di I/O asincrono si deve utilizzare una diversa +struttura \type{aiocb}. + +Dato che si opera in modalità asincrona, il successo di \func{aio\_read} o +\func{aio\_write} non implica che le operazioni siano state effettivamente +eseguite in maniera corretta; per verificarne l'esito l'interfaccia prevede +altre due funzioni, che permettono di controllare lo stato di esecuzione. La +prima è \func{aio\_error}, che serve a determinare un eventuale stato di +errore; il suo prototipo è: +\begin{prototype}{aio.h} + {int aio\_error(const struct aiocb *aiocbp)} + + Determina lo stato di errore delle operazioni di I/O associate a + \param{aiocbp}. + + \bodydesc{La funzione restituisce 0 se le operazioni si sono concluse con + successo, altrimenti restituisce il codice di errore.} +% }, che viene salvato +% anche in \var{errno}, i valori possibili sono: +% \begin{errlist} +% \item[\macro{ENOSYS}] La funzione non è implementata. +% \item[\macro{EINPROGRESS}] L'operazione è ancora in corso. +% \item[\macro{EINVAL}] Si è specificato un valore non valido per i campi +% \var{aio\_offset} o \var{aio\_reqprio} di \param{aiocbp}. +% \item[\macro{EBADF}] Si è specificato un file descriptor sbagliato. +% \end{errlist} +% più tutti quelli possibili per le sottostanti operazioni, .} +\end{prototype} + +Se l'operazione non si è ancora completata viene restituito l'errore di +\macro{EINPROGRESS}. La funzione ritorna zero quando l'operazione si è +conclusa con successo, altrimenti restituisce il codice dell'errore +verificatosi, ed esegue il corrispondente settaggio di \var{errno}. Il codice +può essere sia \macro{EINVAL} ed \macro{EBADF}, dovuti ad un valore errato per +\param{aiocbp}, che uno degli errori possibili durante l'esecuzione +dell'operazione di I/O richiesta, nel qual caso saranno restituiti, a seconda +del caso, i codici di errore delle system call \func{read}, \func{write} e +\func{fsync}. + +Una volta che si sia certi che le operazioni siano state concluse (cioè dopo +che una chiamata ad \func{aio\_error} non ha restituito \macro{EINPROGRESS}, +si potrà usare la seconda funzione dell'interfaccia, \func{aio\_return}, che +permette di verificare il completamento delle operazioni di I/O asincrono; il +suo prototipo è: +\begin{prototype}{aio.h} +{ssize\_t aio\_return(const struct aiocb *aiocbp)} + +Recupera il valore dello stato di ritorno delle operazioni di I/O associate a +\param{aiocbp}. + +\bodydesc{La funzione restituisce lo stato di uscita dell'operazione + eseguita.} +\end{prototype} + +La funzione deve essere chiamata una sola volte per ciascuna operazione +asincrona, essa infatti fa sì che il sistema rilasci le risorse ad essa +associate. É per questo motivo che occorre chiamare la funzione solo dopo che +l'operazione cui \param{aiocbp} fa riferimento si è completata. Una chiamata +precedente il completamento delle operazioni darebbe risultati indeterminati. + +La funzione restituisce il valore di ritorno relativo all'operazione eseguita, +così come ricavato dalla sottostante system call (il numero di byte letti, +scritti o il valore di ritorno di \func{fsync}). É importante chiamare sempre +questa funzione, altrimenti le risorse disponibili per le operazioni di I/O +asincrono non verrebbero liberate, rischiando di arrivare ad un loro +esaurimento. + +Oltre alle operazioni di lettura e scrittura l'interfaccia POSIX.1b mette a +disposizione un'altra operazione, quella di sincronizzazione delll'I/O, essa è +compiuta dalla funzione \func{aio\_fsync}, che ha lo stesso effetto della +analoga \func{fsync}, ma viene esguita in maniera asincrona; il suo prototipo +è: +\begin{prototype}{aio.h} +{ssize\_t aio\_return(int op, struct aiocb *aiocbp)} + +Richiede la sincronizzazione dei dati per il file indicato da \param{aiocbp}. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, che può essere, con le stesse modalità di \func{aio\_read}, + \macro{EAGAIN}, \macro{EBADF} o \macro{EINVAL}.} +\end{prototype} +La funzione richiede la sincronizzazione delle operazioni di I/O, ritornando +immediatamente. L'esecuzione effettiva della sincronizzazione dovrà essere +verificata con \func{aio\_error} e \func{aio\_return} come per le operazioni +di lettura e scrittura. L'argomento \param{op} permette di indicare la +modalità di esecuzione, se si specifica il valore \macro{O\_DSYNC} le +operazioni saranno completate con una chiamata a \func{fdatasync}, se si +specifica \macro{O\_SYNC} con una chiamata a \func{fsync} (per i dettagli vedi +\secref{sec:file_sync}). + +Il successo della chiamata assicura la sincronizzazione delle operazioni fino +allora richieste, niente è garantito riguardo la sincronizzazione dei dati +relativi ad eventuali operazioni richieste successivamente. Se si è +specificato un meccanismo di notifica questo sarà innescato una volta che le +operazioni di sincronizzazione dei dati saranno completate. + +In alcuni casi può essere necessario interrompere le operazioni (in genere +quando viene richiesta un'uscita immediata dal programam), per questo lo +standard POSIX.1b prevede una funzioni apposita, \func{aio\_cancel}, che +permette di cancellare una operazione richiesta in precedenza; il suo +prototipo è: +\begin{prototype}{aio.h} +{int aio\_cancel(int fildes, struct aiocb *aiocbp)} + +Richiede la cancellazione delle operazioni sul file \param{fildes} specificate +da \param{aiocbp}. + +\bodydesc{La funzione restituisce il risultato dell'operazione con un codice + di positivo, e -1 in caso di errore, che avviene qualora si sia specificato + un valore non valido di \param{fildes}, setta \var{errno} al valore + \macro{EBADF}.} +\end{prototype} -\subsection{I/O multiplo} +La funzione permette di cancellare una operazione specifica sul file +\param{fildes}, o tutte le operazioni pendenti, specificando \macro{NULL} come +valore di \param{aiocbp}. Quando una operazione viene cancellata una +successiva chiamata ad \func{aio\_error} riporterà \macro{ECANCELED} come +codice di errore, ed il suo codice di ritorno sarà -1, inoltre il meccanismo +di notifica non verrà invocato. Se si specifica una operazione relativa ad un +altro file descriptor il risultato è indeterminato. + +In caso di successo, i possibili valori di ritorno per \func{aio\_cancel} sono +tre (anch'essi definiti in \file{aio.h}): +\begin{basedescript}{\desclabelwidth{3.0cm}} +\item[\macro{AIO\_ALLDONE}] indica che le operazioni di cui si è richiesta la + cancellazione sono state già completate, + +\item[\macro{AIO\_CANCELED}] indica che tutte le operazioni richieste sono + state cancellate, + +\item[\macro{AIO\_NOTCANCELED}] indica che alcune delle operazioni erano in + corso e non sono state cancellate. +\end{basedescript} + +Nel caso si abbia \macro{AIO\_NOTCANCELED} occorrerà chiamare +\func{aio\_error} per determinare quali sono le operazioni effettivamente +cancellate. Le operazioni che non sono state cancellate proseguiranno il loro +corso normale, compreso quanto richiesto riguardo al meccanismo di notifica +del loro avvenuto completamento. + +Benché l'I/O asincrono preveda un meccanismo di notifica, l'interfaccia +fornisce anche una apposita funzione, \func{aio\_suspend}, che permette di +sospendere l'esecuzione del processo chiamante fino al completamento di una +specifica operazione; il suo prototipo è: +\begin{prototype}{aio.h} +{int aio\_suspend(const struct aiocb * const list[], int nent, const struct + timespec *timeout)} + + Attende, per un massimo di \param{timeout}, il completamento di una delle + operazioni specificate da \param{list}. + + \bodydesc{La funzione restituisce 0 se una (o più) operazioni sono state + completate, e -1 in caso di errorem nel qual caso \var{errno} viene + settata ai valori: + \begin{errlist} + \item[\macro{EAGAIN}] Nessuna operazione è stata completata entro + \param{timeout}. + \item[\macro{ENOSYS}] La funzione non è implementata. + \item[\macro{EINTR}] La funzione è stata interrotta da un segnale. + \end{errlist} + } +\end{prototype} + +La funzione permette di bloccare il processo fintanto che almeno una delle +\param{nent} operazioni specificate nella lista \param{list} è completata, per +un tempo massimo specificato da \param{timout}, o fintanto che non arrivi un +segnale.\footnote{si tenga conto che questo segnale può anche essere quello + utilizzato come meccanismo di notifica.} La lista deve essere inizializzata +con delle strutture \var{aiocb} relative ad operazioni effettivamente +richieste, ma può contenere puntatori nulli, che saranno ignorati. In caso si +siano specificati valori non validi l'effetto è indefinito. Un valore +\macro{NULL} per \param{timout} comporta l'assenza di timeout. + +Lo standard POSIX.1b infine ha previsto pure una funzione, \func{lio\_listio}, +che permette di effettuare la richiesta di una intera lista di operazioni di +lettura o scrittura; il suo prototipo è: +\begin{prototype}{aio.h} + {int lio\_listio(int mode, struct aiocb * const list[], int nent, struct + sigevent *sig)} + + Richiede l'esecuzione delle operazioni di I/O elencata da \param{list}, + secondo la modalità \param{mode}. + + \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di + errore, nel qual caso \var{errno} viene settata ai valori: + \begin{errlist} + \item[\macro{EAGAIN}] Nessuna operazione è stata completata entro + \param{timeout}. + \item[\macro{ENOSYS}] La funzione non è implementata. + \item[\macro{EINTR}] La funzione è stata interrotta da un segnale. + \end{errlist} + } +\end{prototype} + +La funzione esegue la richiesta delle \param{nent} operazioni indicate dalla +lista \param{list}; questa deve contenere gli indirizzi di altrettanti +\textit{control block}, opportunamente inizializzati; in particolare nel caso +dovrà essere specificato il tipo di operazione tramite il campo +\var{aio\_lio\_opcode}, che può prendere i tre valori: +\begin{basedescript}{\desclabelwidth{2.0cm}} +\item[\macro{LIO\_READ}] si richiede una operazione di lettura. +\item[\macro{LIO\_WRITE}] si richiede una operazione di scrittura. +\item[\macro{LIO\_NOP}] non si effettua nessuna operazione. +\end{basedescript} +l'ultimo valore viene usato quando si ha a che fare con un vettore di +dimensione fissa, per poter specificare solo alcune operazioni, o quando si è +dovuto cancellare delle operazioni e si deve ripetere la richiesta per quelle +non completate. + +L'argomento \param{mode} permette di stabilire il comportamento della +funzione, se viene specificato il valore \macro{LIO\_WAIT} la funzione si +blocca fino al completamento di tutte le operazioni richieste; se invece si +spercifica \macro{LIO\_NOWAIT} la funzione ritorna immediatamente dopo aver +messo in coda tutte le richieste. In questo caso il chiamante può richiedere +la notifica del completamento di tutte le richieste, settando l'argomento +\param{sig} in maniera analoga a come si fa per il campo \var{aio\_sigevent} +di \type{aiocb}. + + + +\subsection{I/O vettorizzato} \label{sec:file_multiple_io} -Un caso abbastanza comune è quello in cui ci si trova a dover affrontare una +Un caso abbastanza comune è quello in cui ci si trova a dover eseguire una serie multipla di operazioni di I/O, come una serie di letture o scritture di -vari buffer. In questo caso +vari buffer. Un esempio tipico è quando i dati sono strutturati nei campi di +una struttura ed essi devono essere caricati o salvati su un file. Benché +l'operazione sia facilmente eseguibile attraverso una serie multipla di +chiamate, ci sono casi in cui si vuole poter contare sulla atomicità delle +operazioni. + +Per questo motivo BSD 4.2\footnote{Le due funzioni sono riprese da BSD4.4 ed + integrate anche dallo standard Unix 98; fino alle libc5 Linux usava + \type{size\_t} come tipo dell'argomento \param{count}, una scelta logica, + che è stata dismessa per restare aderenti allo standard.} ha introdotto due +nuove system call, \func{readv} e \func{writev}, che permettono di effettare +con una sola chiamata una lettura o una scrittura su una serie di buffer +(quello che viene chiamato \textsl{I/O vettorizzato}. I relativi prototipi +sono: +\begin{functions} + \headdecl{sys/uio.h} + + \funcdecl{int readv(int fd, const struct iovec *vector, int count)} Esegue + una lettura vettorizzata da \param{fd} nei \param{count} buffer specificati + da \param{vector}. + + \funcdecl{int writev(int fd, const struct iovec *vector, int count)} Esegue + una scrittura vettorizzata da \param{fd} nei \param{count} buffer + specificati da \param{vector}. + + \bodydesc{Le funzioni restituiscono il numero di byte letti o scritti in + caso di successo, e -1 in caso di errore, nel qual caso \var{errno} viene + settata ai valori: + \begin{errlist} + \item[\macro{EBADF}] si è specificato un file descriptor sbagliato. + \item[\macro{EINVAL}] si è specificato un valore non valido per uno degli + argomenti (ad esempio \param{count} è maggiore di \macro{MAX\_IOVEC}). + \item[\macro{EINTR}] la funzione è stata interrotta da un segnale prima di + di avere eseguito una qualunque lettura o scrittura. + \item[\macro{EAGAIN}] \param{fd} è stato aperto in modalità non bloccante e + non ci sono dati in lettura. + \item[\macro{EOPNOTSUPP}] La coda delle richieste è momentaneamente piena. + \end{errlist} + ed inoltre \macro{EISDIR}, \macro{ENOMEM}, \macro{EFAULT} (se non sono stato + allocati correttamente i buffer specificati nei campi \func{iov\_base}), più + tutti gli ulteriori errori che potrebbero avere le usuali funzioni di + lettura e scrittura eseguite su \param{fd}.} +\end{functions} + +Entrambe le funzioni usano una struttura \type{iovec}, definita in +\figref{fig:file_iovec}, che definisce dove i dati devono essere letti o +scritti. Il primo campo, \var{iov\_base}, contiene l'indirizzo del buffer ed +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} + \end{minipage} + \normalsize + \caption{La struttura \type{iovec}, usata dalle operazioni di I/O + vettorizzato.} + \label{fig:file_iovec} +\end{figure} + +I buffer da utlizzare sono specificati attraverso l'argomento \var{vector} che +è un array di tale strutture, la cui lunghezza è specificata da \param{count}. +Essi verranno letti (o scritti) nell'ordine in cui li si sono specificati. \subsection{File mappati in memoria} \label{sec:file_memory_map} +Una modalità alternativa di I/O, che usa una interfaccia completamente diversa +rispetto a quella classica, è quella dei file \textsl{mappati in memoria} (il +cosiddetto \textit{memory-mapped I/O}). In sostanza quello che si fa è usare +il meccanismo della \textsl{paginazione}\index{paginazione} usato per la +memoria virtuale (vedi \secref{sec:proc_mem_gen}) vedere il file in una +sezione dello spazio di indirizzi del processo, in modo che l'accesso a +quest'ultimo avvenga con le normali operazioni di lettura e scrittura delle +variabili in memoria. + +Questa interfaccia è più efficiente dell'uso delle usuali funzioni di I/O, in +quanto permette di caricare in memoria solo le parti del file che sono +effettivamente usate ad un dato istante. Infatti, dato che l'accesso è fatto +direttamente attraverso la memoria virtuale, non è necessario trasferire in un +buffer tutti i dati che potrebbero servire, e poi riscrivere il tutto una +volta completate le operazioni, la scrittura e la lettura avverranno invece +direttamente sulla sezione di memoria mappata, che sarà a sua volta letta o +scritta sul file, una pagina alla volta (e solo per le parti effettivamente +usate) in maniera trasparente attraverso il meccanismo della paginazione. +L'acceso alle pagine non ancora caricate avverrà allo stesso modo con cui +vengono caricate in memoria le pagine che sono state salvate sullo swap. + +Inoltre in situazioni in cui la memoria è scarsa, le pagine che mappano un +file vengono salvate automaticamente, così come le pagine dei programmi +vengono scritte sulla swap; questo consente di accedere ai file su dimensioni +il cui solo limite è quello dello spazio di indirizzi disponibile, + + +La funzione che permette di attivare il memory mapping di un file è +\func{mmap}, il suo prototipo è: +\begin{functions} + + \headdecl{unistd.h} + \headdecl{sys/mman.h} + \funcdecl{void * mmap(void *start, size\_t length, int prot, int flags, int + fd, off\_t offset)} + + Esegue la mappatura in memoria del file \param{fd}. + + \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di + errore nel qual caso \var{errno} viene settata ai valori: + \begin{errlist} + \item[\macro{EBADF}] Il file descriptor non è valido, e non si è usato + \macro{MAP\_ANONYMOUS}. + \item[\macro{EACCES}] \macro{MAP\_PRIVATE}. + \item[\macro{EINTR}] La funzione è stata interrotta da un segnale. + \end{errlist} + ed inoltre \macro{ENOMEM}.} +\end{functions} +Una volta completate le operazioni di I/O si può eliminare la mappatura della +memoria usando la funzione \func{munmap}, il cui prototipo è: +\begin{functions} + \headdecl{unistd.h} + \headdecl{sys/mman.h} + \funcdecl{int munmap(void *start, size\_t length)} + + Esegue la mappatura in memoria del file \param{fd}. + + \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di + errore nel qual caso \var{errno} viene settata ai valori: + \begin{errlist} + \item[\macro{EBADF}] Il file descriptor non è valido, e non si è usato + \macro{MAP\_ANONYMOUS}. + \item[\macro{EACCES}] \macro{MAP\_PRIVATE}. + \item[\macro{EINTR}] La funzione è stata interrotta da un segnale. + \end{errlist} + ed inoltre \macro{ENOMEM}.} +\end{functions} \section{Il file locking} @@ -443,7 +943,6 @@ Il \textit{mandatory locking} - %%% Local Variables: %%% mode: latex %%% TeX-master: "gapil"