X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=b4e658b099b25b5204a1c8391d1cd2400d18d780;hp=591f1494453baa0b6ea51d51d13f5c518dd8d0bf;hb=c926ef8d3587f6eaa9c833ef0836604378b6f104;hpb=c68c98c80f516ecefd08f227f54fc81fb27a7872 diff --git a/fileadv.tex b/fileadv.tex index 591f149..b4e658b 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}. @@ -158,18 +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 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,34 +332,47 @@ 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 @@ -368,26 +380,134 @@ 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 -implementata sia direttamente nel kernel, che attraverso l'uso di thread. Al -momento\footnote{fino ai kernel della serie 2.4.x sono presenti solo le due - alternative citate, 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.} sono disponibili due implementazioni, -una in user space, che è integrata nelle \acr{glibc}, ed una ibrida, che si -appoggia ad una estensione del kernel (il cosiddetto KAIO, non incluso nella -versione ufficiale) prodotta da SGI. Quest'ultima ha funzionalità più limitate -ma prestazioni nettamente superiori. +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 inolte 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 attivare 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 .} + \label{fig:file_sigevent} +\end{figure} -Alle funzioni definite essa si può accedere includendo \file{unistd.h} e -definendo la macro \macro{\_POSIX\_ASYNCHRONOUS\_IO}, mentre alla -implementazione di KAIO si accede includendo \file{linux/aio.h}; ambedue però -usano la stessa interfaccia di programmazione. +Infine il campo \var{aio\_sigevent} serve a specificare il modo in cui si +vuole che la notifica del completamento delle operazioni richieste venga +effettuata. 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 usando il segnale + specificato nel campo \var{sigev\_signo}. +\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 principali dell'interfaccia sono quelle per la lettura e +scrittura, \func{aio\_read} e \func{aio\_write}, i cui prototipi sono: +\begin{functions} + \headdecl{aio.h} + \funcdecl{int aio\_read(struct aiocb *aiocbp)} + Richiede una lettura asincrona sul file specificato tramite \param{aiocbp}. + + \funcdecl{int aio\_write(struct aiocb *aiocbp)} + Richiede una scrittura asincrona sul file specificato tramite + \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 in uno + degli insiemi. + \item[\macro{ENOSYS}] La funzione è implementata. + \item[\macro{EINVAL}] Si è specificato un valore negativo non valido per i + campi \var{aio\_offset} o \var{aio\_reqprio}, di \param{aiocbp}. + \end{errlist} + ed inoltre \macro{ENOMEM}.} + +\end{functions} @@ -396,15 +516,21 @@ usano la stessa interfaccia di programmazione. 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 +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.