X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;f=fileadv.tex;h=dc5bcb8f0a32f586484783ffde7f7eafeb6f8c37;hb=871b3aab1edf47ce16e21b1faaff09c5a935da52;hp=797ecfbf9e4eccfeddc55ff2c59c4798b9121466;hpb=8ed116559b281bf39527afbdd7a172572698c1c1;p=gapil.git diff --git a/fileadv.tex b/fileadv.tex index 797ecfb..dc5bcb8 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -107,10 +107,10 @@ Il primo kernel unix-like ad introdurre una interfaccia per l'\textit{I/O descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno + \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno degli insiemi. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. - \item[\errcode{EINVAL}] Si è specificato per \param{ndfs} un valore negativo + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] si è specificato per \param{ndfs} un valore negativo o un valore non valido per \param{timeout}. \end{errlist} ed inoltre \errval{ENOMEM}. @@ -267,10 +267,10 @@ precedenti, ed inoltre aggiunge a \func{select} una nuova funzione descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno + \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno degli insiemi. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. - \item[\errcode{EINVAL}] Si è specificato per \param{ndfs} un valore negativo + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] si è specificato per \param{ndfs} un valore negativo o un valore non valido per \param{timeout}. \end{errlist} ed inoltre \errval{ENOMEM}.} @@ -350,10 +350,10 @@ cui prototipo in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore, ed in quest'ultimo caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno + \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno degli insiemi. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. - \item[\errcode{EINVAL}] Il valore di \param{nfds} eccede il limite + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] il valore di \param{nfds} eccede il limite \macro{RLIMIT\_NOFILE}. \end{errlist} ed inoltre \errval{EFAULT} e \errval{ENOMEM}.} @@ -496,10 +496,10 @@ prototipo in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore, ed in quest'ultimo caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno + \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno degli insiemi. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. - \item[\errcode{EINVAL}] Il valore di \param{nfds} eccede il limite + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] il valore di \param{nfds} eccede il limite \macro{RLIMIT\_NOFILE}. \end{errlist} ed inoltre \errval{EFAULT} e \errval{ENOMEM}.} @@ -699,7 +699,7 @@ 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}.} + partire dal 2.6.9 si può specificare anche un valore \texttt{NULL}.} @@ -741,7 +741,7 @@ si usa come valore lo stesso \param{fd}. (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{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}).\\ @@ -1248,7 +1248,7 @@ viene segnalata, ma poi (operazione che può essere molto onerosa quando una directory contiene un gran numero di file). Infine l'uso dei segnali come interfaccia di notifica comporta tutti i problemi di gestione visti in sez.~\ref{sec:sig_management} e -sez.~\ref{sec:sig_control}. Per tutta questa serie di motivi in generale +sez.~\ref{sec:sig_adv_control}. Per tutta questa serie di motivi in generale quella di \textit{dnotify} viene considerata una interfaccia di usabilità problematica. @@ -1734,11 +1734,6 @@ raggruppati in un solo evento. \index{file!inotify|)} -% 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} \label{sec:file_asyncronous_io} @@ -2730,8 +2725,6 @@ mappatura che gi \itindend{memory~mapping} - - \subsection{I/O vettorizzato: \func{readv} e \func{writev}} \label{sec:file_multiple_io} @@ -3100,7 +3093,7 @@ descrizioni complete di tutti i valori possibili anche quando, come per meccanismi della memoria virtuale per eseguire i trasferimenti di dati (in maniera analoga a \func{mmap}), qualora le pagine non possano essere spostate dalla pipe o il buffer non corrisponda a pagine intere esse saranno - comunque comunque copiate.} + comunque copiate.} \footnotetext{questa opzione consente di utilizzare delle opzioni di gestione dei socket che permettono di ottimizzare le trasmissioni via rete, si veda @@ -3138,7 +3131,6 @@ file destinazione. Il passo successivo (\texttt{\small 18--22}), quello di destinazione (\texttt{\small 23--27}) ed infine (\texttt{\small 28--31}) la \textit{pipe} che verrà usata come buffer. - \begin{figure}[!htbp] \footnotesize \centering \begin{minipage}[c]{15cm} @@ -3207,7 +3199,7 @@ Come accennato con l'introduzione di \func{splice} sono state realizzate altre due system call, \func{vmsplice} e \func{tee}, che utilizzano la stessa infrastruttura e si basano sullo stesso concetto di manipolazione e trasferimento di dati attraverso un buffer in kernel space; benché queste non -attengono strettamente ad operazioni di trasferiemento dati fra file +attengono strettamente ad operazioni di trasferimento dati fra file descriptor, le tratteremo qui. La prima funzione, \funcd{vmsplice}, è la più simile a \func{splice} e come @@ -3241,7 +3233,7 @@ corrispondente al suo capo aperto in scrittura (di nuovo si faccia riferimento a sez.~\ref{sec:ipc_unix}), mentre per indicare quali zone di memoria devono essere trasferita si deve utilizzare un vettore di strutture \struct{iovec} (vedi fig.~\ref{fig:file_iovec}), con le stesse con cui le si usano per l'I/O -vettorizzato; le dimensioni del sudetto vettore devono essere passate +vettorizzato; le dimensioni del suddetto vettore devono essere passate nell'argomento \param{nr\_segs} che indica il numero di segmenti di memoria da trasferire. Sia per il vettore che per il valore massimo di \param{nr\_segs} valgono le stesse limitazioni illustrate in sez.~\ref{sec:file_multiple_io}. @@ -3249,7 +3241,7 @@ valgono le stesse limitazioni illustrate in sez.~\ref{sec:file_multiple_io}. In caso di successo la funzione ritorna il numero di byte trasferiti sulla pipe, in generale (se i dati una volta creati non devono essere riutilizzati) è opportuno utilizzare il flag \const{SPLICE\_F\_GIFT}; questo fa si che il -kernel possa rimuovere le relative pagine dallo spazio degli indifizzi del +kernel possa rimuovere le relative pagine dallo spazio degli indirizzi del processo, e scaricarle nella cache, così che queste possono essere utilizzate immediatamente senza necessità di eseguire una copia dei dati che contengono. @@ -3290,32 +3282,64 @@ comportamento delle \textit{pipe} si veda sez.~\ref{sec:ipc_unix}). La funzione restituisce il numero di byte copiati da una \textit{pipe} all'altra (o $-1$ in caso di errore), un valore nullo indica che non ci sono -byte disponibili da copiare (la funzione in questo caso non si blocca, a -differenza di quanto avverrebbe per una normale lettura). Un esempio di -realizzazione del comando \texttt{tee} usando questa funzione, ripreso da -quello fornito nella pagina di manuale, è riportato in fig.. - +byte disponibili da copiare e che il capo in scrittura della pipe è stato +chiuso.\footnote{si tenga presente però che questo non avviene se si è + impostato il flag \const{SPLICE\_F\_NONBLOCK}, in tal caso infatti si + avrebbe un errore di \errcode{EAGAIN}.} Un esempio di realizzazione del +comando \texttt{tee} usando questa funzione, ripreso da quello fornito nella +pagina di manuale e dall'esempio allegato al patch originale, è riportato in +fig.~\ref{fig:tee_example}. Il programma consente di copiare il contenuto +dello standard input sullo standard output e su un file specificato come +argomento, il codice completo si trova nel file \texttt{tee.c} dei sorgenti +allegati alla guida. +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/tee.c} + \end{minipage} + \normalsize + \caption{Esempio di codice che usa \func{tee} per copiare i dati dello + standard input sullo standard output e su un file.} + \label{fig:tee_example} +\end{figure} -Infine come nota finale riguardo \func{splice}, \func{vmsplice} e \func{tee} -occorre sottolineare che benché si sia parlato finora di trasferimenti o copie -di dati in realtà nella loro implementazione non è affatto detto che questi -vengono effettivamente spostati o copiati, il kernel infatti realizza le -\textit{pipe} come un insieme di puntatori\footnote{per essere precisi si - tratta di un semplice buffer circolare, un buon articolo sul tema si trova - su \href{http://lwn.net/Articles/118750/} +La prima parte del programma (\texttt{\small 10--35}) si cura semplicemente di +controllare (\texttt{\small 11--14}) che sia stato fornito almeno un argomento +(il nome del file su cui scrivere), di aprirlo ({\small 15--19}) e che sia lo +standard input (\texttt{\small 20--27}) che lo standard output (\texttt{\small + 28--35}) corrispondano ad una \textit{pipe}. + +Il ciclo principale (\texttt{\small 37--58}) inizia con la chiamata a +\func{tee} che duplica il contenuto dello standard input sullo standard output +(\texttt{\small 39}), questa parte è del tutto analoga ad una lettura ed +infatti come nell'esempio di fig.~\ref{fig:splice_example} si controlla il +valore di ritorno della funzione in \var{len}; se questo è nullo significa che +non ci sono più dati da leggere e si chiude il ciclo (\texttt{\small 40}), se +è negativo c'è stato un errore, ed allora si ripete la chiamata se questo è +dovuto ad una interruzione (\texttt{\small 42--44}) o si stampa un messaggio +di errore e si esce negli altri casi (\texttt{\small 44--47}). + +Una volta completata la copia dei dati sullo standard output si possono +estrarre dalla standard input e scrivere sul file, di nuovo su usa un ciclo di +scrittura (\texttt{\small 50--58}) in cui si ripete una chiamata a +\func{splice} (\texttt{\small 51}) fintanto che non si sono scritti tutti i +\var{len} byte copiati in precedenza con \func{tee} (il funzionamento è +identico all'analogo ciclo di scrittura del precedente esempio di +fig.~\ref{fig:splice_example}). + +Infine una nota finale riguardo \func{splice}, \func{vmsplice} e \func{tee}: +occorre sottolineare che benché finora si sia parlato di trasferimenti o copie +di dati in realtà nella implementazione di queste system call non è affatto +detto che i dati vengono effettivamente spostati o copiati, il kernel infatti +realizza le \textit{pipe} come un insieme di puntatori\footnote{per essere + precisi si tratta di un semplice buffer circolare, un buon articolo sul tema + si trova su \href{http://lwn.net/Articles/118750/} {\texttt{http://lwn.net/Articles/118750/}}.} alle pagine di memoria interna che contengono i dati, per questo una volta che i dati sono presenti nella memoria del kernel tutto quello che viene fatto è creare i suddetti puntatori -ed aumentare il numero di referenze, pertanto anche con \func{tee} non viene -mai copiato nessun byte, vengono semplicemente copiati i puntatori. - - -% TODO documentare le funzioni tee e splice -% http://kerneltrap.org/node/6505 e http://lwn.net/Articles/178199/ e -% http://lwn.net/Articles/179492/ e http://lwn.net/Articles/181169 -% e http://en.wikipedia.org/wiki/Splice_(system_call) - +ed aumentare il numero di referenze; questo significa che anche con \func{tee} +non viene mai copiato nessun byte, vengono semplicemente copiati i puntatori. @@ -3335,12 +3359,82 @@ specifiche dei singoli programmi, che avendo una conoscenza diretta di come verranno usati i file, possono necessitare di effettuare delle ottimizzazioni specifiche, relative alle proprie modalità di I/O sugli stessi. Tratteremo in questa sezione una serie funzioni che consentono ai programmi di ottimizzare -il loro accesso ai dati dei file. +il loro accesso ai dati dei file e controllare la gestione del relativo +\textit{caching}. + +Una prima funzione che può essere utilizzata per modificare la gestione +ordinaria dell'I/O su file è \funcd{readahead},\footnote{questa è una funzione + specifica di Linux, introdotta con il kernel 2.4.13, e non deve essere usata + se si vogliono scrivere programmi portabili.} che consente di richiedere una +lettura anticipata del contenuto dello stesso in modo da riempire la memoria +di cache, così che le seguenti operazioni di lettura non debbano bloccarsi +nell'I/O su disco, il suo prototipo è: +\begin{functions} + \headdecl{fcntl.h} + + \funcdecl{ssize\_t readahead(int fd, off64\_t *offset, size\_t count)} + + Legge il contenuto di un file nella cache di memoria. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EBADF}] \param{fd} non è valido o non è aperto in lettura. + \item[\errcode{EINVAL}] \param{fd} si riferisce ad un tipo di file che non + supporta l'operazione (come una pipe o un socket).. + \end{errlist} + } +\end{functions} + +La funzione richiede che sul file \param{fd}, a partire dalla posizione +\param{offset}, venga letto in anticipo il contenuto, portandolo nella cache +della memoria virtuale, per un ammontare di \param{count} bytes. La funzione +usa la memoria virtuale ed il meccanismo della paginazione per cui la lettura +verrà eseguita in blocchi corrispondenti alle dimensioni delle pagine di +memoria, ed i valori di \param{offset} e \param{count} arrotondati di +conseguenza. + +La funzione esegue la lettura bloccandosi fintanto che questa non viene +completata. La posizione corrente sul file non viene modificata ed +indipendentemente da quanto indicato con \param{count} la lettura dei dati si +interromperà una volta raggiunta la fine del file. + +Si può utilizzare questa funzione per velocizzare le operazioni di lettura +all'interno del programma tutte le volte che si conosce in anticipo quanti +dati saranno necessari in seguito. Si potrà così concentrare in un unico +momento (ad esempio in fase di inizializzazione) la lettura, così da ottenere +una migliore risposta nelle operazioni successive. + +Il concetto di \func{readahead} viene generalizzato da +\funcd{posix\_fadvise},\footnote{al contario di \func{readahead} la funzione è + definita nello standard POSIX.1-2001 (anche se l'argomento \param{len} è + stato modificato da \ctyp{size\_t} a \ctyp{off\_t} nella revisione + POSIX.1-2003 TC5), però è disponibile su Linux a partire dai kernel della + serie 2.6.} che consente di ``\textsl{avvisare}'' il kernel sulle modalità +con cui si intende accedere nel futuro ad una certa porzione di un file, così +che esso possa provvedere le opportune ottimizzazioni; il suo prototipo è: +\begin{functions} + \headdecl{fcntl.h} + + \funcdecl{int posix\_fadvise(int fd, off\_t offset, off\_t len, int advice)} + + Dichiara al kernel le future modalità di accesso ad un file. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EBADF}] \param{fd} non è valido o non è aperto in lettura. + \item[\errcode{EINVAL}] \param{fd} si riferisce ad un tipo di file che non + supporta l'operazione (come una pipe o un socket).. + \end{errlist} + } +\end{functions} + + -% TODO documentare \func{madvise} -% TODO documentare \func{mincore} % TODO documentare \func{posix\_fadvise} +% TODO documentare \func{readahead} % vedi http://insights.oetiker.ch/linux/fadvise.html % questo tread? http://www.ussg.iu.edu/hypermail/linux/kernel/0703.1/0032.html @@ -3378,7 +3472,7 @@ in cui diversi processi scrivono, mescolando in maniera imprevedibile il loro output sul file. In tutti questi casi il \textit{file locking} è la tecnica che permette di -evitare le \textit{race condition} \itindex{race~condition}, attraverso una +evitare le \itindex{race~condition} \textit{race condition}, attraverso una serie di funzioni che permettono di bloccare l'accesso al file da parte di altri processi, così da evitare le sovrapposizioni, e garantire la atomicità delle operazioni di scrittura. @@ -4251,9 +4345,10 @@ possibilit % LocalWords: EPOLLHUP EPOLLET EPOLLONESHOT shot maxevents ctlv ALL DONT HPUX % LocalWords: FOLLOW ONESHOT ONLYDIR FreeBSD EIO caching sysctl instances name % LocalWords: watches IGNORED ISDIR OVERFLOW overflow UNMOUNT queued cookie ls -% LocalWords: NUL sizeof casting printevent nread limits sysconf SC wrapper +% LocalWords: NUL sizeof casting printevent nread limits sysconf SC wrapper Di % LocalWords: splice result argument DMA controller zerocopy Linus Larry Voy -% LocalWords: Jens Anxboe vmsplice seek ESPIPE GIFT TCP CORK MSG splicecp +% LocalWords: Jens Anxboe vmsplice seek ESPIPE GIFT TCP CORK MSG splicecp nr +% LocalWords: nwrite segs patch readahead %%% Local Variables: