X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=a0c5f05952542ec2a59cb6906e3d02f3cfaaf658;hp=7b3a6b10b551532e357c3828ad6cb72fbd1c5ebe;hb=91a32c34eddac8bd0e550468c771d869beccdbc5;hpb=44ef16d86264e295b1987168b99aed3190753b3e diff --git a/fileadv.tex b/fileadv.tex index 7b3a6b1..a0c5f05 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 @@ -55,7 +54,7 @@ bloccante. -\subsection{Le funzioni \func{poll} e \func{select}} +\subsection{L'I/O multiplexing} \label{sec:file_multiplexing} Per superare il problema di dover usare il \textit{polling} per controllare la @@ -67,9 +66,12 @@ usualmente \textit{I/O multiplexing}, apparsa in BSD4.2 e standardizzata in BSD4.4, ma è stata portata su tutti i sistemi che supportano i \textit{socket}, compreso le varianti di System V.} con la funzione \func{select}, il cui prototipo è: -\begin{prototype}{sys/select.h} - {int select(int n, fd\_set *readfds, fd\_set *writefds, fd\_set *exceptfds, - struct timeval *timeout)} +\begin{functions} + \headdecl{sys/time.h} + \headdecl{sys/types.h} + \headdecl{unistd.h} + \funcdecl{int select(int n, fd\_set *readfds, fd\_set *writefds, fd\_set + *exceptfds, struct timeval *timeout)} Attende che uno dei file descriptor degli insiemi specificati diventi attivo. @@ -85,11 +87,11 @@ con la funzione \func{select}, il cui prototipo \end{errlist} ed inoltre \macro{ENOMEM}. } -\end{prototype} +\end{functions} 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}. @@ -101,7 +103,9 @@ file descriptor, (in maniera analoga a come un \textit{signal set}, vedi manipolazione di questi \textit{file descriptor set} si possono usare delle opportune macro di preprocessore: \begin{functions} - \headdecl{sys/select.h} + \headdecl{sys/time.h} + \headdecl{sys/types.h} + \headdecl{unistd.h} \funcdecl{FD\_ZERO(fd\_set *set)} Inizializza l'insieme (vuoto). @@ -153,16 +157,18 @@ In Linux \func{select} modifica anche il valore di \param{timeout}, settandolo al tempo restante; questo è utile quando la funzione viene interrotta da un segnale, in tal caso infatti si ha un errore di \macro{EINTR}, ed occorre rilanciare la funzione; in questo modo non è necessario ricalcolare tutte le -volte il tempo rimanente.\footnote{questo però 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.} +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 getire l'\textit{I/O - multiplexing}, basata sulla funzione \func{poll}, il cui prototipo è: +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 \acr{libc} 5.4.28.} il cui prototipo è: \begin{prototype}{sys/poll.h} {int poll(struct pollfd *ufds, unsigned int nfds, int timeout)} @@ -180,39 +186,583 @@ specificati da \param{ufds}. ed inoltre \macro{EFAULT} e \macro{ENOMEM}.} \end{prototype} +La funzione tiene sotto controllo un numero \param{ndfs} di file descriptor +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 +millisecondi (un valore 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} + \end{minipage} + \normalsize + \caption{La struttura \type{pollfd}, utilizzata per specificare le modalità + di controllo di un file descriptor alla funzione \func{poll}.} + \label{fig:file_pollfd} +\end{figure} + +Per ciascun file da controllare deve essere opportunamente predisposta una +struttura \type{pollfd}; nel campo \var{fd} deve essere specificato il file +descriptor, mentre nel campo \var{events} il tipo di evento su cui si vuole +attendere; quest'ultimo deve essere specificato come maschera binaria dei +primi tre valori riportati in \tabref{tab:file_pollfd_flags} (gli altri +vengono utilizzati solo per \var{revents} come valori in uscita). + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|c|l|} + \hline + \textbf{Flag} & \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \macro{POLLIN} & 0x001 & È possibile la lettura immediata.\\ + \macro{POLLPRI} & 0x002 & Sono presenti dati urgenti.\\ + \macro{POLLOUT} & 0x004 & È possibile la scrittura immediata.\\ + \hline + \macro{POLLERR} & 0x008 & C'è una condizione di errore.\\ + \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.\\ + \macro{POLLRDBAND}& 0x080 & Sono disponibili in lettura dati ad alta + priorità. \\ + \macro{POLLWRNORM}& 0x100 & È possibile la scrittura di dati normali. \\ + \macro{POLLWRBAND}& 0x200 & È possibile la scrittura di dati ad + alta priorità. \\ + \macro{POLLMSG} & 0x400 & Estensione propria di Linux.\\ + \hline + \end{tabular} + \caption{Costanti per l'identificazione dei vari bit dei campi + \var{events} e \var{revents} di \type{pollfd}.} + \label{tab:file_pollfd_flags} +\end{table} + +La funzione ritorna, restituendo il numero di file per i quali si è verificata +una delle condizioni di attesa richieste o un errore. Lo stato dei file +all'uscita della funzione viene restituito nel campo \var{revents} della +relativa struttura \type{pollfd}, che viene settato alla maschera binaria dei +valori riportati in \tabref{tab:file_pollfd_flags}, ed oltre alle tre +condizioni specificate tramite \var{events} può riportare anche l'occorrere di +una condizione di errore. + +Lo standard POSIX è rimasto a lungo senza primitive per l'\textit{I/O + multiplexing}, che è stata introdotto con le ultime revisioni dello standard +(POSIX 1003.1g-2000 e POSIX 1003.1-2001). Esso prevede che tutte le funzioni +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.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 + \acr{glibc} 2.2.2-2.2.4 se si è definito \macro{\_XOPEN\_SOURCE} con valore + maggiore di 600.} il cui prototipo è: +\begin{prototype}{sys/select.h} + {int pselect(int n, fd\_set *readfds, fd\_set *writefds, fd\_set *exceptfds, + struct timespec *timeout, sigset\_t *sigmask)} + + Attende che uno dei file descriptor degli insiemi specificati diventi + attivo. + + \bodydesc{La funzione in caso di successo restituisce il numero di file + descriptor (anche nullo) che sono attivi, 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 in uno + degli insiemi. + \item[\macro{EINTR}] La funzione è stata interrotta da un segnale. + \item[\macro{EINVAL}] Si è specificato per \param{n} un valore negativo. + \end{errlist} + ed inoltre \macro{ENOMEM}.} +\end{prototype} + +La funzione è sostanzialmente identica a \func{select}, solo che usa una +struttura \type{timespec} per indicare con maggiore precisione il timeout e +non ne aggiorna il valore in caso di interruzione, inoltre prende un argomento +aggiuntivo \param{sigmask} che è il puntatore ad una maschera di segnali (si +veda \secref{sec:sig_sigmask}). La maschera corrente viene sostituita da +questa immediatamente prima di eseguire l'attesa, e ripristinata al ritorno +della funzione. + +L'uso di \param{sigmask} è stato introdotto allo scopo di prevenire possibili +race condition\footnote{in Linux però, non esistendo una system call apposita, + la funzione è implementata nelle \acr{glibc} usando \func{select}, e la + possibilità di una race condition resta.} quando si deve eseguire un test su +una variabile settata da un manipolatore sulla base dell'occorrenza di un +segnale per decidere se lanciare \func{select}. Fra il test e l'esecuzione è +presente una finestra in cui potrebbe arrivare il segnale che non sarebbe +rilevato; la race condition diventa superabile disabilitando il segnale prima +del test e riabilitandolo poi grazie all'uso di \param{sigmask}. + -\subsection{L'I/O asincrono} +\subsection{L'\textsl{I/O asincrono}} \label{sec:file_asyncronous_io} -Una modalità alternativa all'uso dell'I/O non bloccante è quella di fare -ricorso all'I/O asincrono. Abbiamo accennato in \secref{sec:file_open} che è -possibile, attraverso l'uso del flag \macro{O\_ASYNC}, aprire un file in -modalità asincrona, così come è possibile settare questo flag attraverso l'uso -di \func{fcntl}. +Una modalità alternativa all'uso dell'\textit{I/O multiplexing} è quella di +fare ricorso al cosiddetto \textsl{I/O asincrono}. Il concetto base +dell'\textsl{I/O asincrono} è che le funzioni di I/O non attendono il +completamento delle operazioni prima di ritornare, così che il processo non +viene bloccato. In questo modo diventa ad esempio possibile effettuare una +richiesta preventiva di dati, in modo da poter effettuare in contemporanea le +operazioni di calcolo e quelle di I/O. + +Abbiamo accennato in \secref{sec:file_open} che è possibile, attraverso l'uso +del flag \macro{O\_ASYNC},\footnote{l'uso del flag di \macro{O\_ASYNC} e dei + comandi \macro{F\_SETOWN} e \macro{F\_GETOWN} per \func{fcntl} è specifico + 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}). + +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 +descriptor; quello che succede è che il sistema genera un segnale (normalmente +\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. + +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 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. + +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 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 +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 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à di questa funzionalità. + +\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 cui file descriptor deve essere specificato tramite il campo +\var{aio\_fildes}; 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. + +Dato che più operazioni possono essere eseguita in maniera asincrona, il +concetto di posizione corrente sul file viene a mancare; pertanto ciascuna +operazione 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} poi +andrà specificato l'indirizzo del buffer usato per l'I/O, ed in +\var{aio\_nbytes} la lunghezza del trasferimento. + +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 dalla funzione \func{lio\_listio}, che +permette di far partire una serie di operazioni in contemporanea su una lista +di file. 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 POSIX.1b per l'I/O asincrono sono +\func{aio\_read} e \func{aio\_write}. Esse servono a 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, effettuando le chiamate all'interno di una subroutine, +e che non si deve riutilizzare la stessa struttura per un'ulteriore operazione +fintanto che la precedente non si sia ultimata. In generale per ogni +operazione di I/O asincrono si deve utilizzare una ed una sola struttura +\type{aiocb}. + +Si ricordi che, operando in modalità asincrona, il successo di queste funzioni +non implica che le operazioni richieste siano state effettivamente eseguite in +maniera corretta. Per verificare l'esito delle operazioni 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} -In tal caso il sistema genera un segnale \macro{SIGIO} tutte le volte che sono -presenti dei dati in input su un file aperto in questa modalità. Uno dei -problemi che si presentavano con le prime implementazioni di questa modalità -di I/O è che essa poteva essere usata in maniera semplice aprendo un solo file -per processo, dato che altrimenti si sarebbe dovuto provvedere ad effettuare -una serie di controlli su tutti i file aperti per distinguere a quale fosse -dovuto l'emissione del segnale. +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 di errore, ed esegue +il settaggio di \var{errno}. In caso caso di errore esso può essere sia uno +dei precedentemente specificati \macro{EINVAL} ed \macro{EBADF}, dovuti ad un +valore errato per \param{aiocbp} che uno dei possibili errori dovuti alle +chiamate al sistema sottostanti l'esecuzione dell'operazione di I/O richiesta, +relativi alle funzioni \func{read}, \func{write} e \func{fsync}. + + +Una volta che si sia certi che le operazioni si siano concluse (cioè dopo che +una chiamata ad \func{aio\_error} non ha restituito \macro{EINPROGRESS}, si +può usare la seconda funzione dell'interfaccia, \func{aio\_return}, per +verificare il completamento delle operazioni di I/O asincrono, il cui +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} -Tutto questo adesso può essere evitato facendo ricorso alle informazioni -restituite al manipolatore del segnale attraverso la struttura -\var{siginfo\_t} (vedi \figref{fig:sig_siginfo_t}), il cui campo \var{si\_fd} -riporta il file descriptor che ha generato il segnale. +La funzione deve essere chiamata una sola volte per ciascuna operazione +asincrona, essa infatti fa sì che il sistema rilasci le risorse associate a +ciascuna operazione. Per questo motivo occorre chiamare la funzione solo dopo +che l'operazione cui \param{aiocbp} fa riferimento si è completata. + +La funzione restituisce il valore di ritorno relativa 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, essendo la +richiesta asincrona, 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} (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} + +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 +\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. + +I possibili valori di ritorno di \func{aio\_cancel} sono tre: +\macro{AIO\_ALLDONE} indica che le operazioni di cui si è richiesta la +cancellazione sono state già completate, \macro{AIO\_CANCELED} indica che +tutte le operazioni richieste sono state cancellate, e +\macro{AIO\_NOTCANCELED} che alcune delle operazioni erano in corso e non sono +state cancellate. + +In quest'ultimo caso occorre chiamare \func{aio\_error} per determinare quali +sono le operazioni cancellate. Le operazioni che non sono state cancellate +proseguono il loro corso normale, compreso quanto relativo al meccanismo di +notifica del loro avvenuto completamento. + +Benché l'I/O asincrono preveda un meccanismo di notifica, che permette di +bloccare un processo in maniera relativamente semplice fino al completamento +di una determinata operazione, lo standard fornisce anche una apposita +funzione, \func{aio\_suspend}, che permette di sospendere l'esecuzione di un +processo 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 chiamante 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 una attesa +infinita. + +Lo standard 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 + 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 esegue la richiesta delle \param{nent} operazioni indicate dalla +lista \param{list}; questa deve contenere gli indirizzi di altrettanti 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{description*} +\item[\macro{LIO\_READ}] richiede una operazione di lettura. +\item[\macro{LIO\_WRITE}] richiede una operazione di scrittura. +\item[\macro{LIO\_NOP}] non effettua nessuna operazione. +\end{description*} +l'ultimo 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 +una notifica del completamento di tutte le richieste settando \param{sig}. -\subsection{File mappati in memoria} -\label{sec:file_memory_map} \subsection{I/O multiplo} \label{sec:file_multiple_io} +Un caso abbastanza comune è quello in cui ci si trova a dover affrontare una +serie multipla di operazioni di I/O, come una serie di letture o scritture di +vari buffer. In questo caso + + + +\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}. 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}) per trasformare vedere il file in una sezione dello +spazio di indirizzi del processo, in modo che l'accesso a quest'ultimo con le +normali operazioni di lettura e scrittura delle variabili in memoria, si +trasformi in I/O sul file stesso. + \section{Il file locking}