X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=1286c56ac2c3a3f2b13d42e4f1a9473d5fc7f866;hp=33aa027651d1f005a2ccd0e63f7329ae73392935;hb=3d3b2a0c6e47ab411bb55925dd9330a7dbd95f96;hpb=167155d57830c0f07aa4c3c4c4b0090332b2fe07 diff --git a/fileadv.tex b/fileadv.tex index 33aa027..1286c56 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -616,8 +616,11 @@ chiamare la funzione \funcd{epoll\_create}, il cui prototipo } \end{prototype} -La funzione restituisce un file descriptor speciale, detto anche \textit{epoll - descriptor} che viene associato alla infrastruttura utilizzata dal kernel +La funzione restituisce un file descriptor speciale,\footnote{esso non è + associato a nessun file su disco, inoltre a differenza dei normali file + descriptor non può essere inviato ad un altro processo attraverso un socket + locale (vedi sez.~\ref{sec:sock_fd_passing}).} detto anche \textit{epoll + descriptor}, che viene associato alla infrastruttura utilizzata dal kernel per gestire la notifica degli eventi; l'argomento \param{size} serve a dare l'indicazione del numero di file descriptor che si vorranno tenere sotto controllo, ma costituisce solo un suggerimento per semplificare l'allocazione @@ -653,23 +656,28 @@ controllare, per questo si deve usare la seconda funzione dell'interfaccia, Il comportamento della funzione viene controllato dal valore dall'argomento \param{op} che consente di specificare quale operazione deve essere eseguita. -Le costanti che definiscono i valori utilizzabili per l'argomento \param{op} +Le costanti che definiscono i valori utilizzabili per \param{op} sono riportate in tab.~\ref{tab:epoll_ctl_operation}, assieme al significato delle operazioni cui fanno riferimento. \begin{table}[htb] \centering \footnotesize - \begin{tabular}[c]{|l|l|} + \begin{tabular}[c]{|l|p{8cm}|} \hline \textbf{Valore} & \textbf{Significato} \\ \hline \hline \const{EPOLL\_CTL\_ADD}& aggiunge un nuovo file descriptor da osservare \param{fd} alla lista dei file descriptor - controllati tramite \param{epfd}.\\ - \const{EPOLL\_CTL\_MOD}& .\\ - \const{EPOLL\_CTL\_DEL}& .\\ + controllati tramite \param{epfd}, in + \param{event} devono essere specificate le + modalità di osservazione.\\ + \const{EPOLL\_CTL\_MOD}& modifica le modalità di osservazione del file + descriptor \param{fd} secondo il contenuto di + \param{event}.\\ + \const{EPOLL\_CTL\_DEL}& rimuove il file descriptor \param{fd} dalla lista + dei file controllati tramite \param{epfd}.\\ \hline \end{tabular} \caption{Valori dell'argomento \param{op} che consentono di scegliere quale @@ -677,6 +685,22 @@ delle operazioni cui fanno riferimento. \label{tab:epoll_ctl_operation} \end{table} +La funzione prende sempre come primo argomento un file descriptor di +\textit{epoll}, \param{epfd}, che deve essere stato ottenuto in precedenza con +una chiamata a \func{epoll\_create}. L'argomento \param{fd} indica invece il +file descriptor che si vuole tenere sotto controllo, quest'ultimo può essere +un qualunque file descriptor utilizzabile con \func{poll}, ed anche un altro +file descriptor di \textit{epoll}, ma non lo stesso \param{epfd}. + +L'ultimo argomento, \param{event}, deve essere un puntatore ad una struttura +di tipo \struct{epoll\_event}, ed ha significato solo con le operazioni +\const{EPOLL\_CTL\_MOD} e \const{EPOLL\_CTL\_ADD}, per le quali serve ad +indicare quale tipo di evento relativo ad \param{fd} si vuole che sia tenuto +sotto controllo. L'argomento viene ignorato con l'operazione +\const{EPOLL\_CTL\_DEL}.\footnote{fino al kernel 2.6.9 era comunque richiesto + che questo fosse un puntatore valido, anche se poi veniva ignorato, a + partire dal 2.6.9 si può specificare anche anche un valore \texttt{NULL}.} + \begin{figure}[!htb] @@ -685,57 +709,113 @@ delle operazioni cui fanno riferimento. \includestruct{listati/epoll_event.h} \end{minipage} \normalsize - \caption{La struttura \structd{epoll\_event}, .} - \label{fig:} + \caption{La struttura \structd{epoll\_event}, che consente di specificare + gli eventi associati ad un file descriptor controllato con + \textit{epoll}.} + \label{fig:epoll_event} \end{figure} +La struttura \struct{epoll\_event} è l'analoga di \struct{pollfd} e come +quest'ultima serve sia in ingresso (quando usata con \func{epoll\_ctl}) ad +impostare quali eventi osservare, che in uscita (nei risultati ottenuti con +\func{epoll\_wait}) per ricevere le notifiche degli eventi avvenuti. La sua +definizione è riportata in fig.~\ref{fig:epoll_event}. - +Il primo campo, \var{events}, è una maschera binaria in cui ciascun bit +corrisponde o ad un tipo di evento, o una modalità di notifica; detto campo +deve essere specificato come OR aritmetico delle costanti riportate in +tab.~\ref{tab:epoll_events}. Il secondo campo, \var{data}, serve ad indicare a +quale file descriptor si intende fare riferimento, ed in astratto può +contenere un valore qualsiasi che permetta di identificarlo, di norma comunque +si usa come valore lo stesso \param{fd}. \begin{table}[htb] \centering \footnotesize - \begin{tabular}[c]{|l|l|} + \begin{tabular}[c]{|l|p{8cm}|} \hline \textbf{Valore} & \textbf{Significato} \\ \hline \hline - \const{EPOLLIN}& .\\ - \const{EPOLLOUT}& .\\ - \const{EPOLLRDHUP}& .\\ - \const{EPOLLPRI}& .\\ - \const{EPOLLERR}& .\\ - \const{EPOLLHUP}& .\\ - \const{EPOLLET}& .\\ - \const{EPOLLONESHOT}& .\\ + \const{EPOLLIN} & Il file è pronto per le operazioni di lettura + (analogo di \const{POLLIN}).\\ + \const{EPOLLOUT} & Il file è pronto per le operazioni di scrittura + (analogo di \const{POLLOUT}).\\ + \const{EPOLLRDHUP} & l'altro capo di un socket di tipo + \const{SOCK\_STREAM} (vedi sez.~\ref{sec:sock_type}) + ha chiuso la connessione o il capo in scrittura + della stessa (vedi sez.~\ref{sec:TCP_shutdown}).\\ + \const{EPOLLPRI} & Ci sono \itindex{out-of-band} dati urgenti + disponibili in lettura (analogo di + \const{POLLPRI}); questa condizione viene comunque + riportata in uscita, e non è necessaria impostarla + in ingresso.\\ + \const{EPOLLERR} & Si è verificata una condizione di errore + (analogo di \const{POLLERR}); questa condizione + viene comunque riportata in uscita, e non è + necessaria impostarla in ingresso.\\ + \const{EPOLLHUP} & Si è verificata una condizione di hung-up.\\ + \const{EPOLLET} & Imposta la notifica in modalità \textit{edge + triggered} per il file descriptor associato.\\ + \const{EPOLLONESHOT}& Imposta la modalità \textit{one-shot} per il file + descriptor associato.\footnotemark\\ \hline \end{tabular} - \caption{Valori del campo \param{events} di \struct{epoll\_event}.} + \caption{Costanti che identificano i bit del campo \param{events} di + \struct{epoll\_event}.} \label{tab:epoll_events} \end{table} - -, da -specificare con le costanti riportate in tab., -La funzione prende come primo argomento un file descriptor di \textit{epoll} -\param{epfd}, che deve essere stato ottenuto tramite \func{epoll\_create}, e -consente di impostare le operazioni di osservazione su un altro file -descriptor \param{fd} - - - - -La funzione esegue l'operazione di controllo specificata dall'argomento -\param{op}, - - -\param{fd} alla lista dei file descriptor osservati - -sull'\textit{epoll descripter} \param{epfd} - - -La funzione che consente di attendere è \funcd{epoll\_wait}, il cui prototipo -è: +\footnotetext{questa modalità è disponibile solo a partire dal kernel 2.6.2.} + +Le modalità di utilizzo di \textit{epoll} prevedano che si definisca qual'è +l'insieme dei file descriptor da tenere sotto controllo tramite un certo +\textit{epoll descriptor} \param{epfd} attraverso una serie di chiamate a +\const{EPOLL\_CTL\_ADD}.\footnote{un difetto dell'interfaccia è che queste + chiamate devono essere ripetute per ciascun file descriptor, incorrendo in + una perdita di prestazioni qualora il numero di file descriptor sia molto + grande; per questo è stato proposto di introdurre come estensione una + funzione \func{epoll\_ctlv} che consenta di effettuare con una sola chiamata + le impostazioni per un blocco di file descriptor.} L'uso di +\const{EPOLL\_CTL\_MOD} consente in seguito di modificare le modalità di +osservazione di un file descriptor che sia già stato aggiunto alla lista di +osservazione. + +Le impostazioni di default prevedono che la notifica degli eventi richiesti +sia effettuata in modalità \textit{level triggered}, a meno che sul file +descriptor non si sia impostata la modalità \textit{edge triggered}, +registrandolo con \const{EPOLLET} attivo nel campo \var{events}. Si tenga +presente che è possibile tenere sotto osservazione uno stesso file descriptor +su due \textit{epoll descriptor} diversi, ed entrambi riceveranno le +notifiche, anche se questa pratica è sconsigliata. + +Qualora non si abbia più interesse nell'osservazione di un file descriptor lo +si può rimuovere dalla lista associata a \param{epfd} con +\const{EPOLL\_CTL\_DEL}; si tenga conto inoltre che i file descriptor sotto +osservazione che vengono chiusi sono eliminati dalla lista automaticamente e +non è necessario usare \const{EPOLL\_CTL\_DEL}. + +Infine una particolare modalità di notifica è quella impostata con +\const{EPOLLONESHOT}: a causa dell'implementazione di \textit{epoll} infatti +quando si è in modalità \textit{edge triggered} l'arrivo in rapida successione +di dati in blocchi separati\footnote{questo è tipico con i socket di rete, in + quanto i dati arrivano a pacchetti.} può causare una generazione di eventi +(ad esempio segnalazioni di dati in lettura disponibili) anche se la +condizione è già stata rilevata.\footnote{si avrebbe cioè una rottura della + logica \textit{edge triggered}.} + +Anche se la situazione è facile da gestire, la si può evitare utilizzando +\const{EPOLLONESHOT} per impostare la modalità \textit{one-shot}, in cui la +notifica di un evento viene effettuata una sola volta, dopo di che il file +descriptor osservato, pur restando nella lista di osservazione, viene +automaticamente disattivato,\footnote{la cosa avviene contestualmente al + ritorno di \func{epoll\_wait} a causa dell'evento in questione.} e per +essere riutilizzato dovrà essere riabilitato esplicitamente con una successiva +chiamata con \const{EPOLL\_CTL\_MOD}. + +Una volta impostato l'insieme di file descriptor che si vogliono osservare con +i relativi eventi, la funzione che consente di attendere l'occorrenza di uno +di tali eventi è \funcd{epoll\_wait}, il cui prototipo è: \begin{prototype}{sys/epoll.h} {int epoll\_wait(int epfd, struct epoll\_event * events, int maxevents, int timeout)} @@ -751,16 +831,61 @@ La funzione che consente di attendere \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima della scadenza di \param{timeout}. \item[\errcode{EINVAL}] il file descriptor \param{epfd} non è stato ottenuto - con \func{epoll\_create}, o \param{fd} è lo stesso \param{epfd} o - l'operazione richiesta con \param{op} non è supportata. + con \func{epoll\_create}, o \param{maxevents} non è maggiore di zero. \end{errlist} } \end{prototype} +La funzione si blocca in attesa di un evento per i file descriptor registrati +nella lista di osservazione di \param{epfd} fino ad un tempo massimo +specificato in millisecondi tramite l'argomento \param{timeout}. Gli eventi +registrati vengono riportati in un vettore di strutture \struct{epoll\_event} +(che deve essere stato allocato in precedenza) all'indirizzo indicato +dall'argomento \param{events}, fino ad un numero massimo di eventi impostato +con l'argomento \param{maxevents}. + +La funzione ritorna il numero di eventi rilevati, o un valore nullo qualora +sia scaduto il tempo massimo impostato con \param{timeout}. Per quest'ultimo, +oltre ad un numero di millisecondi, si può utilizzare il valore nullo, che +indica di non attendere e ritornare immediatamente,\footnote{anche in questo + caso il valore di ritorno sarà nullo.} o $-1$, che indica un'attesa +indefinita. L'argomento \param{maxevents} dovrà invece essere sempre un intero +positivo. + +Come accennato la funzione restituisce i suoi risultati nel vettore di +strutture \struct{epoll\_event} puntato da \param{events}; in tal caso nel +campo \param{events} di ciascuna di esse saranno attivi i flag relativi agli +eventi accaduti, mentre nel campo \var{data} sarà restituito il valore che era +stato impostato (per il file descriptor per cui si è verificato l'evento) +quando questo era stato registrato con le operazioni \const{EPOLL\_CTL\_MOD} o +\const{EPOLL\_CTL\_ADD}. + +Si ricordi che le occasioni per cui \func{epoll\_wait} ritorna dipendono da +come si è impostata la modalità di osservazione (se \textit{level triggered} o +\textit{edge triggered}) del singolo file descriptor. L'interfaccia assicura +che se arrivano più eventi fra due chiamate successive ad \func{epoll\_wait} +questi vengano combinati. Inoltre qualora su di esso fossero presenti eventi +non ancora notificati, e si effettuasse una modifica dell'osservazione con +\const{EPOLL\_CTL\_MOD} questi verrebbero riletti alla luce delle modifiche. + +Si tenga presente infine che con l'uso della modalità \textit{edge triggered} +il ritorno di \func{epoll\_wait} indica un file descriptor è pronto e resterà +tale fintanto che non si sono completamente esaurite le operazioni su di esso, +questo può essere rilevato con un errore di \errcode{EAGAIN} in una +\func{read} o una \func{write},\footnote{è opportuno ricordare ancora una + volta che l'uso dell'I/O multiplexing richiede di operare sui file in + modalità non bloccante.} ma anche con il fatto che sono stati restituiti +meno dati di quelli richiesti. + +Come per le precedenti \func{select} e \func{poll}, essendo queste funzioni +utiilizzate prevalentemente con i server di rete, tratteremo degli esempi del +loro più avanti, nella trattazione dei socket, ed in particolare in +sez.~\ref{sec:TCP_sock_multiplexing}. + \itindend{epoll} -% TODO epoll -% + + \section{L'accesso \textsl{asincrono} ai file} \label{sec:file_asyncronous_access} @@ -773,44 +898,52 @@ contesto le modalit \textsl{asincrona}, quelle cioè in cui un processo non deve bloccarsi in attesa della disponibilità dell'accesso al file, ma può proseguire nell'esecuzione utilizzando invece un meccanismo di notifica asincrono (di -norma un segnale), per essere avvisato della possibilità di eseguire le +norma un segnale, ma esistono anche altre interfacce, come \itindex{inotify} +\textit{inotify}), per essere avvisato della possibilità di eseguire le operazioni di I/O volute. -\subsection{Operazioni asincrone sui file} +\subsection{Il \textit{Signal driven I/O}} \label{sec:file_asyncronous_operation} -Abbiamo accennato in sez.~\ref{sec:file_open} che è possibile, attraverso l'uso -del flag \const{O\_ASYNC},\footnote{l'uso del flag di \const{O\_ASYNC} e dei - comandi \const{F\_SETOWN} e \const{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à impostando questo flag attraverso -l'uso di \func{fcntl} con il comando \const{F\_SETFL} (vedi -sez.~\ref{sec:file_fcntl}). - -In realtà in questo caso non si tratta di eseguire delle operazioni di lettura -o scrittura del file in modo asincrono (tratteremo questo, che più -propriamente è detto \textsl{I/O asincrono} in -sez.~\ref{sec:file_asyncronous_io}), quanto di un meccanismo asincrono di -notifica delle variazione dello stato del file descriptor aperto in questo -modo. - -Quello che succede in questo caso è che il sistema genera un segnale -(normalmente \const{SIGIO}, ma è possibile usarne altri con il comando -\const{F\_SETSIG} di \func{fcntl}) 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 \const{F\_SETOWN} di \func{fcntl}, +Abbiamo accennato in sez.~\ref{sec:file_open} che è possibile, attraverso +l'uso del flag \const{O\_ASYNC},\footnote{l'uso del flag di \const{O\_ASYNC} e + dei comandi \const{F\_SETOWN} e \const{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à impostando questo flag +attraverso l'uso di \func{fcntl} con il comando \const{F\_SETFL} (vedi +sez.~\ref{sec:file_fcntl}). + +In realtà parlare di apertura in modalità asincrona non significa che le +operazioni di lettura o scrittura del file vengono eseguite in modo asincrono +(tratteremo questo, che è ciò che più propriamente viene chiamato \textsl{I/O + asincrono}, in sez.~\ref{sec:file_asyncronous_io}), quanto dell'attivazione +un meccanismo di notifica asincrona delle variazione dello stato del file +descriptor aperto in questo modo. Quello che succede in questo caso è che il +sistema genera un segnale (normalmente \const{SIGIO}, ma è possibile usarne +altri con il comando \const{F\_SETSIG} di \func{fcntl}) tutte le volte che +diventa possibile leggere o scrivere dal file descriptor che si è posto in +questa modalità.\footnote{questa modalità non è utilizzabile con i file + ordinari ma solo con socket, file di terminale o pseudo terminale, e, a + partire dal kernel 2.6, anche per fifo e pipe.} + +Si può inoltre selezionare, con il comando \const{F\_SETOWN} di \func{fcntl}, quale processo (o gruppo di processi) riceverà il segnale. Se pertanto si effettuano le operazioni di I/O in risposta alla ricezione del segnale non ci sarà più la necessità di restare bloccati in attesa della disponibilità di -accesso ai file; per questo motivo Stevens chiama questa modalità -\textit{signal driven I/O}. - -Questa è un'altra modalità di gestione I/O, alternativa all'uso di -\itindex{epoll} \textit{epoll}, che consente di evitare l'uso delle funzioni -\func{poll} o \func{select} che, come illustrato in sez.~\ref{sec:file_epoll}, -quando vengono usate con un numero molto grande di file descriptor, non hanno -buone prestazioni. +accesso ai file. + +Per questo motivo Stevens, ed anche le pagine di manuale di +Linux, chiamano questa modalità ``\textit{Signal driven I/O}''. Questa è +ancora un'altra modalità di gestione dell'I/O, alternativa all'uso di +\itindex{epoll} \textit{epoll},\footnote{anche se le prestazioni ottenute con + questa tecnica sono inferiori, il vantaggio è che questa modalità è + utilizzabile anche con kernel che non supportano \textit{epoll}, come quelli + della serie 2.4, ottenendo comunque prestazioni superiori a quelle che si + hanno con \func{poll} e \func{select}.} che consente di evitare l'uso delle +funzioni \func{poll} o \func{select} che, come illustrato in +sez.~\ref{sec:file_epoll}, quando vengono usate con un numero molto grande di +file descriptor, non hanno buone prestazioni. Tuttavia con l'implementazione classica dei segnali questa modalità di I/O presenta notevoli problemi, dato che non è possibile determinare, quando i @@ -848,11 +981,16 @@ un file su cui l'accesso funzioni come \func{poll} e \func{select}, almeno fintanto che non si satura la coda. -Se infatti si eccedono le dimensioni di quest'ultima, il kernel, non potendo -più assicurare il comportamento corretto per un segnale real-time, invierà al -suo posto un solo \const{SIGIO}, su cui si saranno accumulati tutti i segnali -in eccesso, e si dovrà allora determinare con un ciclo quali sono i file -diventati attivi. +Se infatti si eccedono le dimensioni di quest'ultima, il kernel, non potendo +più assicurare il comportamento corretto per un segnale real-time, invierà al +suo posto un solo \const{SIGIO}, su cui si saranno accumulati tutti i segnali +in eccesso, e si dovrà allora determinare con un ciclo quali sono i file +diventati attivi. L'unico modo per essere sicuri che questo non avvenga è di +impostare la lunghezza della coda dei segnali real-time ad una dimensione +identica al valore massimo del numero di file descriptor +utilizzabili.\footnote{vale a dire impostare il contenuto di + \texttt{/proc/sys/kernel/rtsig-max} allo stesso valore di quello di + \texttt{/proc/sys/fs/file-max}.} % TODO fare esempio che usa O_ASYNC @@ -1280,7 +1418,8 @@ numero di file che sono cambiati. % TODO inserire anche eventfd (vedi http://lwn.net/Articles/233462/) - +% e le restanti signalfd e timerfd introdotte con il 2.6.22 +% o trovargli un posto migliore \subsection{L'interfaccia POSIX per l'I/O asincrono} @@ -1518,7 +1657,7 @@ 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 programma), per questo lo -standard POSIX.1b prevede una funzioni apposita, \funcd{aio\_cancel}, che +standard POSIX.1b prevede una funzione apposita, \funcd{aio\_cancel}, che permette di cancellare una operazione richiesta in precedenza; il suo prototipo è: \begin{prototype}{aio.h} @@ -2346,15 +2485,17 @@ mappatura che gi \itindend{memory~mapping} -\subsection{L'I/O diretto fra file descriptor} -\label{sec:file_sendfile_splice} +%\subsection{L'I/O diretto fra file descriptor} +%\label{sec:file_sendfile_splice} + + +%Uno dei problemi -Uno dei problemi +%NdA è da finire, sul perché non è abilitata fra file vedi: -NdA è da finire, sul perché non è abilitata fra file vedi: +%\href{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html} +%{\texttt{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}} -\href{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html} -{\texttt{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}} % TODO documentare la funzione sendfile % TODO documentare le funzioni tee e splice @@ -2363,6 +2504,17 @@ NdA % e http://en.wikipedia.org/wiki/Splice_(system_call) + + +%\subsection{Gestione avanzata del caching dei dati} +%\label{sec:file_fadvise} + +% TODO documentare \func{madvise} +% TODO documentare \func{mincore} +% TODO documentare \func{posix\_fadvise} +% vedi http://insights.oetiker.ch/linux/fadvise.html +% questo tread? http://www.ussg.iu.edu/hypermail/linux/kernel/0703.1/0032.html + %\subsection{L'utilizzo delle porte di I/O} %\label{sec:file_io_port} % @@ -3261,7 +3413,8 @@ possibilit % LocalWords: dell'inode CLOSE NOWRITE MOVE MOVED FROM TO rm wd event page ctl % LocalWords: attribute Universe epoll Solaris kqueue level triggered Jonathan % LocalWords: Lemon BSDCON edge Libenzi kevent backporting epfd EEXIST ENOENT -% LocalWords: MOD +% LocalWords: MOD wait EPOLLIN EPOLLOUT EPOLLRDHUP SOCK EPOLLPRI EPOLLERR one +% LocalWords: EPOLLHUP EPOLLET EPOLLONESHOT shot maxevents %%% Local Variables: