X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;f=fileadv.tex;h=4761523154cd2904385bb04b0817431e1f6ab982;hb=154622c3382b558d8c0b7984ee76d96d09d44c71;hp=67887c80e9eebed448e62ec16837a22ba1e8c491;hpb=492c6b5ca2c851077e63d3a53c9f735e854e8765;p=gapil.git diff --git a/fileadv.tex b/fileadv.tex index 67887c8..4761523 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -1301,12 +1301,14 @@ eliminato.\footnote{anzi, una delle capacit Inoltre trattandosi di un file descriptor a tutti gli effetti, esso potrà essere utilizzato come argomento per le funzioni \func{select} e \func{poll} e con l'interfaccia di \textit{epoll}; siccome gli eventi vengono notificati -come dati disponibili in lettura sul file descriptor, dette funzioni -ritorneranno tutte le volte che si avrà un evento di notifica. Così, invece di -dover utilizzare i segnali,\footnote{considerati una pessima scelta dal punto - di vista dell'interfaccia utente.} si potrà gestire l'osservazione delle -modifiche con una qualunque delle modalità di \textit{I/O multiplexing} -illustrate in sez.~\ref{sec:file_multiplexing}. +come dati disponibili in lettura, dette funzioni ritorneranno tutte le volte +che si avrà un evento di notifica. Così, invece di dover utilizzare i +segnali,\footnote{considerati una pessima scelta dal punto di vista + dell'interfaccia utente.} si potrà gestire l'osservazione degli eventi con +una qualunque delle modalità di \textit{I/O multiplexing} illustrate in +sez.~\ref{sec:file_multiplexing}. Qualora si voglia cessare l'osservazione, +sarà sufficiente chiudere il file descriptor e tutte le risorse allocate +saranno automaticamente rilasciate. Infine l'interfaccia di \textit{inotify} consente di mettere sotto osservazione, oltre che una directory, anche singoli file. Una volta creata @@ -1361,7 +1363,7 @@ flag della prima parte. \footnotesize \begin{tabular}[c]{|l|c|p{10cm}|} \hline - \textbf{Flag} & & \textbf{Significato} \\ + \textbf{Valore} & & \textbf{Significato} \\ \hline \hline \const{IN\_ACCESS} &$\bullet$& C'è stato accesso al file in @@ -1380,7 +1382,7 @@ flag della prima parte. \const{IN\_DELETE} &$\bullet$& È stato cancellato un file o una directory in una directory sotto osservazione.\\ - \const{IN\_DELETE\_SELF} & & È stato cancellato il file (o la + \const{IN\_DELETE\_SELF} & -- & È stato cancellato il file (o la directory) sotto osservazione.\\ \const{IN\_MODIFY} &$\bullet$& È stato modificato il file.\\ \const{IN\_MOVE\_SELF} & & È stato rinominato il file (o la @@ -1391,17 +1393,17 @@ flag della prima parte. directory sotto osservazione.\\ \const{IN\_OPEN} &$\bullet$& Un file è stato aperto.\\ \hline - \const{IN\_CLOSE} & -- & Combinazione di + \const{IN\_CLOSE} & & Combinazione di \const{IN\_CLOSE\_WRITE} e \const{IN\_CLOSE\_NOWRITE}.\\ - \const{IN\_MOVE} & -- & Combinazione di + \const{IN\_MOVE} & & Combinazione di \const{IN\_MOVED\_FROM} e \const{IN\_MOVED\_TO}.\\ - \const{IN\_ALL\_EVENTS} & -- & Combinazione di tutti i flag + \const{IN\_ALL\_EVENTS} & & Combinazione di tutti i flag possibili.\\ \hline \end{tabular} - \caption{Le costanti che identificano i valori per la maschera binaria + \caption{Le costanti che identificano i bit della maschera binaria dell'argomento \param{mask} di \func{inotify\_add\_watch} che indicano il tipo di evento da tenere sotto osservazione.} \label{tab:inotify_event_watch} @@ -1422,7 +1424,7 @@ contrario dei precedenti non vengono mai impostati nei risultati in uscita. \footnotesize \begin{tabular}[c]{|l|p{10cm}|} \hline - \textbf{Flag} & \textbf{Significato} \\ + \textbf{Valore} & \textbf{Significato} \\ \hline \hline \const{IN\_DONT\_FOLLOW}& Non dereferenzia \param{pathname} se questo è un @@ -1438,7 +1440,7 @@ contrario dei precedenti non vengono mai impostati nei risultati in uscita. quelli per i file che contiene.\\ \hline \end{tabular} - \caption{Le costanti che identificano i valori per la maschera binaria + \caption{Le costanti che identificano i bit della maschera binaria dell'argomento \param{mask} di \func{inotify\_add\_watch} che indicano le modalità di osservazione.} \label{tab:inotify_add_watch_flag} @@ -1552,7 +1554,7 @@ aggiuntivi\footnote{questi compaiono solo nel campo \var{mask} di \footnotesize \begin{tabular}[c]{|l|p{10cm}|} \hline - \textbf{Flag} & \textbf{Significato} \\ + \textbf{Valore} & \textbf{Significato} \\ \hline \hline \const{IN\_IGNORED} & L'osservatore è stato rimosso, sia in maniera @@ -1572,7 +1574,7 @@ aggiuntivi\footnote{questi compaiono solo nel campo \var{mask} di osservazione è stato smontato.\\ \hline \end{tabular} - \caption{Le costanti che identificano i flag aggiuntivi usati nella maschera + \caption{Le costanti che identificano i bit aggiuntivi usati nella maschera binaria del campo \var{mask} di \struct{inotify\_event}.} \label{tab:inotify_read_event_flag} \end{table} @@ -1717,9 +1719,9 @@ nelle dimensioni del buffer specificato.\footnote{si avr riferimento sempre al codice di fig.~\ref{fig:inotify_monitor_example}, che \var{read} sarà in genere minore delle dimensioni di \var{buffer} ed uguale soltanto qualora gli eventi corrispondano esattamente alle dimensioni di - quest'ultimo.} Se gli eventi sono di più ne saranno restituiti solo quelli -che entrano interamente nel buffer e gli altri saranno restituiti alla -successiva chiamata di \func{read}. + quest'ultimo.} Se gli eventi sono di più saranno restituiti solo quelli che +entrano interamente nel buffer e gli altri saranno restituiti alla successiva +chiamata di \func{read}. Infine un'ultima caratteristica dell'interfaccia di \textit{inotify} è che gli eventi restituiti nella lettura formano una sequenza ordinata, è cioè @@ -1729,9 +1731,6 @@ verificano pi dei campi \var{wd}, \var{mask}, \var{cookie}, e \var{name}) questi vengono raggruppati in un solo evento. -% TODO inserire anche inotify, vedi http://www.linuxjournal.com/article/8478 -% TODO e man inotify - \index{file!inotify|)} @@ -1860,11 +1859,11 @@ appena descritta; i rispettivi prototipi sono: \bodydesc{Le funzioni restituiscono 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}] Si è specificato un file descriptor sbagliato. - \item[\errcode{ENOSYS}] La funzione non è implementata. - \item[\errcode{EINVAL}] Si è specificato un valore non valido per i campi + \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato. + \item[\errcode{ENOSYS}] la funzione non è implementata. + \item[\errcode{EINVAL}] si è specificato un valore non valido per i campi \var{aio\_offset} o \var{aio\_reqprio} di \param{aiocbp}. - \item[\errcode{EAGAIN}] La coda delle richieste è momentaneamente piena. + \item[\errcode{EAGAIN}] la coda delle richieste è momentaneamente piena. \end{errlist} } \end{functions} @@ -2031,10 +2030,10 @@ specifica operazione; il suo prototipo completate, e -1 in caso di errore nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EAGAIN}] Nessuna operazione è stata completata entro + \item[\errcode{EAGAIN}] nessuna operazione è stata completata entro \param{timeout}. - \item[\errcode{ENOSYS}] La funzione non è implementata. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. + \item[\errcode{ENOSYS}] la funzione non è implementata. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. \end{errlist} } \end{prototype} @@ -2062,13 +2061,13 @@ lettura o scrittura; il suo prototipo \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{EAGAIN}] Nessuna operazione è stata completata entro + \item[\errcode{EAGAIN}] nessuna operazione è stata completata entro \param{timeout}. - \item[\errcode{EINVAL}] Si è passato un valore di \param{mode} non valido + \item[\errcode{EINVAL}] si è passato un valore di \param{mode} non valido o un numero di operazioni \param{nent} maggiore di \const{AIO\_LISTIO\_MAX}. - \item[\errcode{ENOSYS}] La funzione non è implementata. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. + \item[\errcode{ENOSYS}] la funzione non è implementata. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. \end{errlist} } \end{prototype} @@ -2104,199 +2103,8 @@ Oltre alle precedenti modalit asincrono}, esistono altre funzioni che implementano delle modalità di accesso ai file più evolute rispetto alle normali funzioni di lettura e scrittura che abbiamo esaminato in sez.~\ref{sec:file_base_func}. In questa -sezione allora prenderemo in esame le interfacce per l'\textsl{I/O - vettorizzato} e per l'\textsl{I/O mappato in memoria} e la funzione -\func{sendfile}. - - -\subsection{I/O vettorizzato} -\label{sec:file_multiple_io} - -Un caso abbastanza comune è quello in cui ci si trova a dover eseguire una -serie multipla di operazioni di I/O, come una serie di letture o scritture di -vari buffer. Un esempio tipico è quando i dati sono strutturati nei campi di -una struttura ed essi devono essere caricati o salvati su un file. Benché -l'operazione sia facilmente eseguibile attraverso una serie multipla di -chiamate, ci sono casi in cui si vuole poter contare sulla atomicità delle -operazioni. - -Per questo motivo su BSD 4.2 sono state introdotte due nuove system call, -\funcd{readv} e \funcd{writev},\footnote{in Linux le due funzioni sono riprese - da BSD4.4, esse sono previste anche dallo standard POSIX.1-2001.} che -permettono di effettuare con una sola chiamata una lettura o una scrittura su -una serie di buffer (quello che viene chiamato \textsl{I/O vettorizzato}. I -relativi prototipi sono: -\begin{functions} - \headdecl{sys/uio.h} - - \funcdecl{int readv(int fd, const struct iovec *vector, int count)} - \funcdecl{int writev(int fd, const struct iovec *vector, int count)} - - Eseguono rispettivamente una lettura o una scrittura vettorizzata. - - \bodydesc{Le funzioni restituiscono il numero di byte letti o scritti in - caso di successo, e -1 in caso di errore, nel qual caso \var{errno} - assumerà uno dei valori: - \begin{errlist} - \item[\errcode{EINVAL}] si è specificato un valore non valido per uno degli - argomenti (ad esempio \param{count} è maggiore di \const{IOV\_MAX}). - \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima di - di avere eseguito una qualunque lettura o scrittura. - \item[\errcode{EAGAIN}] \param{fd} è stato aperto in modalità non bloccante e - non ci sono dati in lettura. - \item[\errcode{EOPNOTSUPP}] la coda delle richieste è momentaneamente piena. - \end{errlist} - ed anche \errval{EISDIR}, \errval{EBADF}, \errval{ENOMEM}, \errval{EFAULT} - (se non sono stati allocati correttamente i buffer specificati nei campi - \var{iov\_base}), più gli eventuali errori delle funzioni di lettura e - scrittura eseguite su \param{fd}.} -\end{functions} - -Entrambe le funzioni usano una struttura \struct{iovec}, la cui definizione è -riportata in fig.~\ref{fig:file_iovec}, che definisce dove i dati devono -essere letti o scritti ed in che quantità. Il primo campo della struttura, -\var{iov\_base}, contiene l'indirizzo del buffer ed il secondo, -\var{iov\_len}, la dimensione dello stesso. - -\begin{figure}[!htb] - \footnotesize \centering - \begin{minipage}[c]{15cm} - \includestruct{listati/iovec.h} - \end{minipage} - \normalsize - \caption{La struttura \structd{iovec}, usata dalle operazioni di I/O - vettorizzato.} - \label{fig:file_iovec} -\end{figure} - -La lista dei buffer da utilizzare viene indicata attraverso l'argomento -\param{vector} che è un vettore di strutture \struct{iovec}, la cui lunghezza -è specificata dall'argomento \param{count}.\footnote{fino alle libc5, Linux - usava \type{size\_t} come tipo dell'argomento \param{count}, una scelta - logica, che però è stata dismessa per restare aderenti allo standard - POSIX.1-2001.} Ciascuna struttura dovrà essere inizializzata opportunamente -per indicare i vari buffer da e verso i quali verrà eseguito il trasferimento -dei dati. Essi verranno letti (o scritti) nell'ordine in cui li si sono -specificati nel vettore \param{vector}. - -La standardizzazione delle due funzioni all'interno della revisione -POSIX.1-2001 prevede anche che sia possibile avere un limite al numero di -elementi del vettore \param{vector}. Qualora questo sussista, esso deve essere -indicato dal valore dalla costante \const{IOV\_MAX}, definita come le altre -costanti analoghe (vedi sez.~\ref{sec:sys_limits}) in \file{limits.h}; lo -stesso valore deve essere ottenibile in esecuzione tramite la funzione -\func{sysconf} richiedendo l'argomento \const{\_SC\_IOV\_MAX} (vedi -sez.~\ref{sec:sys_sysconf}). - -Nel caso di Linux il limite di sistema è di 1024, però se si usano le -\acr{glibc} queste forniscono un \textit{wrapper} per le system call che si -accorge se una operazione supererà il precedente limite, in tal caso i dati -verranno letti o scritti con le usuali \func{read} e \func{write} usando un -buffer di dimensioni sufficienti appositamente allocato e sufficiente a -contenere tutti i dati indicati da \param{vector}. L'operazione avrà successo -ma si perderà l'atomicità del trasferimento da e verso la destinazione finale. - - -% TODO verificare cosa succederà a preadv e pwritev o alla nuova niovec -% vedi http://lwn.net/Articles/164887/ - - -\subsection{L'I/O diretto fra file descriptor} -\label{sec:file_sendfile_splice} - - -Uno dei problemi che si presenta nella gestione dell'I/O è quello in cui si -devono trasferire grandi quantità di dati da un file descriptor ed un altro; -questo usualmente comporta la lettura dei dati dal primo file descriptor in un -buffer in memoria, da cui essi vengono poi scritti sul secondo. - -Benché il kernel ottimizzi la gestione di questo processo quando si ha a che -fare con file normali, in generale quando i dati da trasferire sono molti si -pone il problema di effettuare trasferimenti di grandi quantità di dati da -kernel space a user space e all'indietro, quando in realtà sarebbe molto più -efficiente tenere tutto in kernel space. Tratteremo in questa sezione alcune -funzioni specialistiche che permettono di ottimizzare le prestazioni in questo -tipo di situazioni. - -La prima funzione che si pone l'obiettivo di ottimizzare il trasferimento dei -dati fra due file descriptor è \funcd{sendfile}; la funzione è presente in -diverse versioni di Unix,\footnote{la si ritrova ad esempio in FreeBSD, HPUX - ed altri Unix.} ma non è presente né in POSIX.1-2001 né in altri standard, -per cui vengono utilizzati diversi prototipi e semantiche -differenti;\footnote{pertanto si eviti di utilizzarla se si devono scrivere - programmi portabili.} nel caso di Linux il suo prototipo è: -\begin{functions} - \headdecl{sys/sendfile.h} - - \funcdecl{ssize\_t sendfile(int out\_fd, int in\_fd, off\_t *offset, size\_t - count)} - - Copia dei dati da un file descriptor ad un altro. - - \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{EAGAIN}] si è impostata la modalità non bloccante su - \param{out\_fd} e la scrittura si bloccherebbe. - \item[\errcode{EINVAL}] i file descriptor non sono validi, o sono bloccati - o una operazione di \func{mmap} non è disponibile per \param{in\_fd}. - \item[\errcode{EIO}] si è avuto un errore di lettura da \param{in\_fd}. - \item[\errcode{ENOMEM}] non c'è memoria sufficiente per la lettura da - \param{in\_fd}. - \end{errlist} - ed inoltre \errcode{EBADF} e \errcode{EFAULT}. - } -\end{functions} - - -%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}} - - -% TODO documentare la funzione sendfile -% 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://en.wikipedia.org/wiki/Splice_(system_call) -% e http://kerneltrap.org/node/6505 - - - - -\subsection{Gestione avanzata dell'accesso ai dati dei file} -\label{sec:file_fadvise} - -Nell'uso generico dell'interfaccia per l'accesso al contenuto dei file le -operazioni di lettura e scrittura non necessitano di nessun intervento di -supervisione da parte dei programmi, si eseguirà una \func{read} o una -\func{write}, i dati verranno passati al kernel che provvederà ad effettuare -tutte le operazioni (e a gestire il \textit{caching} dei dati) per portarle a -termine in quello che ritiene essere il modo più efficiente. - -Il problema è che il concetto di migliore efficienza impiegato dal kernel è -relativo all'uso generico, mentre esistono molti casi in cui ci sono esigenze -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. - - -% 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 -% TODO documentare \func{fallocate}, introdotta con il 2.6.23 -% vedi http://lwn.net/Articles/226710/ e http://lwn.net/Articles/240571/ - - -%\subsection{L'utilizzo delle porte di I/O} -%\label{sec:file_io_port} -% -% TODO l'I/O sulle porte di I/O -% consultare le manpage di ioperm, iopl e outb +sezione allora prenderemo in esame le interfacce per l'\textsl{I/O mappato in + memoria}, per l'\textsl{I/O vettorizzato} e altre funzioni di I/O avanzato. \subsection{File mappati in memoria} @@ -2369,29 +2177,29 @@ eseguire la mappatura in memoria di un file, in caso di successo, e \const{MAP\_FAILED} (-1) in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EBADF}] Il file descriptor non è valido, e non si è usato + \item[\errcode{EBADF}] il file descriptor non è valido, e non si è usato \const{MAP\_ANONYMOUS}. \item[\errcode{EACCES}] o \param{fd} non si riferisce ad un file regolare, o si è usato \const{MAP\_PRIVATE} ma \param{fd} non è aperto in lettura, o si è usato \const{MAP\_SHARED} e impostato \const{PROT\_WRITE} ed \param{fd} non è aperto in lettura/scrittura, o si è impostato \const{PROT\_WRITE} ed \param{fd} è in \textit{append-only}. - \item[\errcode{EINVAL}] I valori di \param{start}, \param{length} o + \item[\errcode{EINVAL}] i valori di \param{start}, \param{length} o \param{offset} non sono validi (o troppo grandi o non allineati sulla dimensione delle pagine). - \item[\errcode{ETXTBSY}] Si è impostato \const{MAP\_DENYWRITE} ma + \item[\errcode{ETXTBSY}] si è impostato \const{MAP\_DENYWRITE} ma \param{fd} è aperto in scrittura. - \item[\errcode{EAGAIN}] Il file è bloccato, o si è bloccata troppa memoria + \item[\errcode{EAGAIN}] il file è bloccato, o si è bloccata troppa memoria rispetto a quanto consentito dai limiti di sistema (vedi sez.~\ref{sec:sys_resource_limit}). - \item[\errcode{ENOMEM}] Non c'è memoria o si è superato il limite sul + \item[\errcode{ENOMEM}] non c'è memoria o si è superato il limite sul numero di mappature possibili. - \item[\errcode{ENODEV}] Il filesystem di \param{fd} non supporta il memory + \item[\errcode{ENODEV}] il filesystem di \param{fd} non supporta il memory mapping. - \item[\errcode{EPERM}] L'argomento \param{prot} ha richiesto + \item[\errcode{EPERM}] l'argomento \param{prot} ha richiesto \const{PROT\_EXEC}, ma il filesystem di \param{fd} è montato con l'opzione \texttt{noexec}. - \item[\errcode{ENFILE}] Si è superato il limite del sistema sul numero di + \item[\errcode{ENFILE}] si è superato il limite del sistema sul numero di file aperti (vedi sez.~\ref{sec:sys_resource_limit}). \end{errlist} } @@ -2422,7 +2230,6 @@ multiplo della dimensione di una pagina di memoria. \label{tab:file_mmap_prot} \end{table} - Il valore dell'argomento \param{prot} indica la protezione\footnote{in Linux la memoria reale è divisa in pagine: ogni processo vede la sua memoria attraverso uno o più segmenti lineari di memoria virtuale. Per ciascuno di @@ -2432,7 +2239,7 @@ Il valore dell'argomento \param{prot} indica la protezione\footnote{in Linux che si chiama una \textit{segment violation}, e la relativa emissione del segnale \const{SIGSEGV}.} da applicare al segmento di memoria e deve essere specificato come maschera binaria ottenuta dall'OR di uno o più dei valori -riportati in tab.~\ref{tab:file_mmap_flag}; il valore specificato deve essere +riportati in tab.~\ref{tab:file_mmap_prot}; il valore specificato deve essere compatibile con la modalità di accesso con cui si è aperto il file. L'argomento \param{flags} specifica infine qual è il tipo di oggetto mappato, @@ -2535,10 +2342,10 @@ eseguita su un segmento di dimensioni rigorosamente multiple di quelle di una pagina, ed in generale queste potranno non corrispondere alle dimensioni effettive del file o della sezione che si vuole mappare. -\footnotetext[20]{Dato che tutti faranno riferimento alle stesse pagine di +\footnotetext[68]{dato che tutti faranno riferimento alle stesse pagine di memoria.} -\footnotetext[21]{L'uso di questo flag con \const{MAP\_SHARED} è stato +\footnotetext[69]{l'uso di questo flag con \const{MAP\_SHARED} è stato implementato in Linux a partire dai kernel della serie 2.4.x; esso consente di creare segmenti di memoria condivisa e torneremo sul suo utilizzo in sez.~\ref{sec:ipc_mmap_anonymous}.} @@ -2649,10 +2456,10 @@ memoria mappata con il file su disco; il suo prototipo \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{EINVAL}] O \param{start} non è multiplo di + \item[\errcode{EINVAL}] o \param{start} non è multiplo di \const{PAGE\_SIZE}, o si è specificato un valore non valido per \param{flags}. - \item[\errcode{EFAULT}] L'intervallo specificato non ricade in una zona + \item[\errcode{EFAULT}] l'intervallo specificato non ricade in una zona precedentemente mappata. \end{errlist} } @@ -2679,7 +2486,8 @@ del file aggiornato. siano invalidate.\\ \hline \end{tabular} - \caption{Valori dell'argomento \param{flag} di \func{msync}.} + \caption{Le costanti che identificano i bit per la maschera binaria + dell'argomento \param{flag} di \func{msync}.} \label{tab:file_mmap_rsync} \end{table} @@ -2706,7 +2514,7 @@ mappatura della memoria usando la funzione \funcd{munmap}, il suo prototipo \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{EINVAL}] L'intervallo specificato non ricade in una zona + \item[\errcode{EINVAL}] l'intervallo specificato non ricade in una zona precedentemente mappata. \end{errlist} } @@ -2863,7 +2671,7 @@ nuova system call, \funcd{remap\_file\_pages}, il cui prototipo \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{EINVAL}] Si è usato un valore non valido per uno degli + \item[\errcode{EINVAL}] si è usato un valore non valido per uno degli argomenti o \param{start} non fa riferimento ad un \textit{memory mapping} valido creato con \const{MAP\_SHARED}. \end{errlist} @@ -2924,6 +2732,511 @@ mappatura che gi +\subsection{I/O vettorizzato: \func{readv} e \func{writev}} +\label{sec:file_multiple_io} + +Un caso abbastanza comune è quello in cui ci si trova a dover eseguire una +serie multipla di operazioni di I/O, come una serie di letture o scritture di +vari buffer. Un esempio tipico è quando i dati sono strutturati nei campi di +una struttura ed essi devono essere caricati o salvati su un file. Benché +l'operazione sia facilmente eseguibile attraverso una serie multipla di +chiamate, ci sono casi in cui si vuole poter contare sulla atomicità delle +operazioni. + +Per questo motivo su BSD 4.2 sono state introdotte due nuove system call, +\funcd{readv} e \funcd{writev},\footnote{in Linux le due funzioni sono riprese + da BSD4.4, esse sono previste anche dallo standard POSIX.1-2001.} che +permettono di effettuare con una sola chiamata una lettura o una scrittura su +una serie di buffer (quello che viene chiamato \textsl{I/O vettorizzato}. I +relativi prototipi sono: +\begin{functions} + \headdecl{sys/uio.h} + + \funcdecl{int readv(int fd, const struct iovec *vector, int count)} + \funcdecl{int writev(int fd, const struct iovec *vector, int count)} + + Eseguono rispettivamente una lettura o una scrittura vettorizzata. + + \bodydesc{Le funzioni restituiscono il numero di byte letti o scritti in + caso di successo, e -1 in caso di errore, nel qual caso \var{errno} + assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EINVAL}] si è specificato un valore non valido per uno degli + argomenti (ad esempio \param{count} è maggiore di \const{IOV\_MAX}). + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima di + di avere eseguito una qualunque lettura o scrittura. + \item[\errcode{EAGAIN}] \param{fd} è stato aperto in modalità non bloccante e + non ci sono dati in lettura. + \item[\errcode{EOPNOTSUPP}] la coda delle richieste è momentaneamente piena. + \end{errlist} + ed anche \errval{EISDIR}, \errval{EBADF}, \errval{ENOMEM}, \errval{EFAULT} + (se non sono stati allocati correttamente i buffer specificati nei campi + \var{iov\_base}), più gli eventuali errori delle funzioni di lettura e + scrittura eseguite su \param{fd}.} +\end{functions} + +Entrambe le funzioni usano una struttura \struct{iovec}, la cui definizione è +riportata in fig.~\ref{fig:file_iovec}, che definisce dove i dati devono +essere letti o scritti ed in che quantità. Il primo campo della struttura, +\var{iov\_base}, contiene l'indirizzo del buffer ed il secondo, +\var{iov\_len}, la dimensione dello stesso. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/iovec.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{iovec}, usata dalle operazioni di I/O + vettorizzato.} + \label{fig:file_iovec} +\end{figure} + +La lista dei buffer da utilizzare viene indicata attraverso l'argomento +\param{vector} che è un vettore di strutture \struct{iovec}, la cui lunghezza +è specificata dall'argomento \param{count}.\footnote{fino alle libc5, Linux + usava \type{size\_t} come tipo dell'argomento \param{count}, una scelta + logica, che però è stata dismessa per restare aderenti allo standard + POSIX.1-2001.} Ciascuna struttura dovrà essere inizializzata opportunamente +per indicare i vari buffer da e verso i quali verrà eseguito il trasferimento +dei dati. Essi verranno letti (o scritti) nell'ordine in cui li si sono +specificati nel vettore \param{vector}. + +La standardizzazione delle due funzioni all'interno della revisione +POSIX.1-2001 prevede anche che sia possibile avere un limite al numero di +elementi del vettore \param{vector}. Qualora questo sussista, esso deve essere +indicato dal valore dalla costante \const{IOV\_MAX}, definita come le altre +costanti analoghe (vedi sez.~\ref{sec:sys_limits}) in \file{limits.h}; lo +stesso valore deve essere ottenibile in esecuzione tramite la funzione +\func{sysconf} richiedendo l'argomento \const{\_SC\_IOV\_MAX} (vedi +sez.~\ref{sec:sys_sysconf}). + +Nel caso di Linux il limite di sistema è di 1024, però se si usano le +\acr{glibc} queste forniscono un \textit{wrapper} per le system call che si +accorge se una operazione supererà il precedente limite, in tal caso i dati +verranno letti o scritti con le usuali \func{read} e \func{write} usando un +buffer di dimensioni sufficienti appositamente allocato e sufficiente a +contenere tutti i dati indicati da \param{vector}. L'operazione avrà successo +ma si perderà l'atomicità del trasferimento da e verso la destinazione finale. + +% TODO verificare cosa succederà a preadv e pwritev o alla nuova niovec +% vedi http://lwn.net/Articles/164887/ + + +\subsection{L'I/O diretto fra file descriptor: \func{sendfile} e \func{splice}} +\label{sec:file_sendfile_splice} + +Uno dei problemi che si presentano nella gestione dell'I/O è quello in cui si +devono trasferire grandi quantità di dati da un file descriptor ed un altro; +questo usualmente comporta la lettura dei dati dal primo file descriptor in un +buffer in memoria, da cui essi vengono poi scritti sul secondo. + +Benché il kernel ottimizzi la gestione di questo processo quando si ha a che +fare con file normali, in generale quando i dati da trasferire sono molti si +pone il problema di effettuare trasferimenti di grandi quantità di dati da +kernel space a user space e all'indietro, quando in realtà potrebbe essere più +efficiente mantenere tutto in kernel space. Tratteremo in questa sezione +alcune funzioni specialistiche che permettono di ottimizzare le prestazioni in +questo tipo di situazioni. + +La prima funzione che si pone l'obiettivo di ottimizzare il trasferimento dei +dati fra due file descriptor è \funcd{sendfile};\footnote{la funzione è stata + introdotta con i kernel della serie 2.2, e disponibile dalle \acr{glibc} + 2.1.} la funzione è presente in diverse versioni di Unix,\footnote{la si + ritrova ad esempio in FreeBSD, HPUX ed altri Unix.} ma non è presente né in +POSIX.1-2001 né in altri standard,\footnote{pertanto si eviti di utilizzarla + se si devono scrivere programmi portabili.} per cui per essa vengono +utilizzati prototipi e semantiche differenti; nel caso di Linux il suo +prototipo è: +\begin{functions} + \headdecl{sys/sendfile.h} + + \funcdecl{ssize\_t sendfile(int out\_fd, int in\_fd, off\_t *offset, size\_t + count)} + + Copia dei dati da un file descriptor ad un altro. + + \bodydesc{La funzione restituisce il numero di byte trasferiti in caso di + successo e $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno + dei valori: + \begin{errlist} + \item[\errcode{EAGAIN}] si è impostata la modalità non bloccante su + \param{out\_fd} e la scrittura si bloccherebbe. + \item[\errcode{EINVAL}] i file descriptor non sono validi, o sono bloccati + (vedi sez.~\ref{sec:file_locking}), o \func{mmap} non è disponibile per + \param{in\_fd}. + \item[\errcode{EIO}] si è avuto un errore di lettura da \param{in\_fd}. + \item[\errcode{ENOMEM}] non c'è memoria sufficiente per la lettura da + \param{in\_fd}. + \end{errlist} + ed inoltre \errcode{EBADF} e \errcode{EFAULT}. + } +\end{functions} + +La funzione copia direttamente \param{count} byte dal file descriptor +\param{in\_fd} al file descriptor \param{out\_fd}; in caso di successo +funzione ritorna il numero di byte effettivamente copiati da \param{in\_fd} a +\param{out\_fd} o $-1$ in caso di errore, come le ordinarie \func{read} e +\func{write} questo valore può essere inferiore a quanto richiesto con +\param{count}. + +Se il puntatore \param{offset} è nullo la funzione legge i dati a partire +dalla posizione corrente su \param{in\_fd}, altrimenti verrà usata la +posizione indicata dal valore puntato da \param{offset}; in questo caso detto +valore sarà aggiornato, come \textit{value result argument}, per indicare la +posizione del byte successivo all'ultimo che è stato letto, mentre la +posizione corrente sul file non sarà modificata. Se invece \param{offset} è +nullo la posizione corrente sul file sarà aggiornata tenendo conto dei byte +letti da \param{in\_fd}. + +Fino ai kernel della serie 2.4 la funzione è utilizzabile su un qualunque file +descriptor, e permette di sostituire la invocazione successiva di una +\func{read} e una \func{write} (e l'allocazione del relativo buffer) con una +sola chiamata a \funcd{sendfile}. In questo modo si può diminuire il numero di +chiamate al sistema e risparmiare in trasferimenti di dati da kernel space a +user space e viceversa. La massima utilità della funzione si ha comunque per +il trasferimento di dati da un file su disco ad un socket di +rete,\footnote{questo è il caso classico del lavoro eseguito da un server web, + ed infatti Apache ha una opzione per il supporto esplicito di questa + funzione.} dato che in questo caso diventa possibile effettuare il +trasferimento diretto via DMA dal controller del disco alla scheda di rete, +senza neanche allocare un buffer nel kernel,\footnote{il meccanismo è detto + \textit{zerocopy} in quanto i dati non vengono mai copiati dal kernel, che + si limita a programmare solo le operazioni di lettura e scrittura via DMA.} +ottenendo la massima efficienza possibile senza pesare neanche sul processore. + +In seguito però ci si è accorti che, fatta eccezione per il trasferimento +diretto da file a socket, non sempre \func{sendfile} comportava miglioramenti +significativi delle prestazioni rispetto all'uso in sequenza di \func{read} e +\func{write},\footnote{nel caso generico infatti il kernel deve comunque + allocare un buffer ed effettuare la copia dei dati, e in tal caso spesso il + guadagno ottenibile nel ridurre il numero di chiamate al sistema non + compensa le ottimizzazioni che possono essere fatte da una applicazione in + user space che ha una conoscenza diretta su come questi sono strutturati.} e +che anzi in certi casi si potevano avere anche dei peggioramenti. Questo ha +portato, per i kernel della serie 2.6,\footnote{per alcune motivazioni di + questa scelta si può fare riferimento a quanto illustrato da Linus Torvalds + in \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}}.} +alla decisione di consentire l'uso della funzione soltanto quando il file da +cui si legge supporta le operazioni di \textit{memory mapping} (vale a dire +non è un socket) e quello su cui si scrive è un socket; in tutti gli altri +casi l'uso di \func{sendfile} darà luogo ad un errore di \errcode{EINVAL}. + +Nonostante ci possano essere casi in cui \func{sendfile} non migliora le +prestazioni, le motivazioni addotte non convincono del tutto e resta il dubbio +se la scelta di disabilitarla sempre per il trasferimento di dati fra file di +dati sia davvero corretta. Se ci sono peggioramenti di prestazioni infatti si +può sempre fare ricorso all'uso successivo di, ma lasciare a disposizione la +funzione consentirebbe se non altro, anche in assenza di guadagni di +prestazioni, di semplificare la gestione della copia dei dati fra file, +evitando di dover gestire l'allocazione di un buffer temporaneo per il loro +trasferimento; inoltre si avrebbe comunque il vantaggio di evitare inutili +trasferimenti di dati da kernel space a user space e viceversa. + +Questo dubbio si può comunque ritenere superato con l'introduzione, avvenuto a +partire dal kernel 2.6.17, della nuova system call \func{splice}. Lo scopo di +questa funzione è quello di fornire un meccanismo generico per il +trasferimento di dati da o verso un file utilizzando un buffer gestito +internamente dal kernel. Descritta in questi termini \func{splice} sembra +semplicemente un ``\textsl{dimezzamento}'' di \func{sendfile}.\footnote{nel + senso che un trasferimento di dati fra due file con \func{sendfile} non + sarebbe altro che la lettura degli stessi su un buffer seguita dalla + relativa scrittura, cosa che in questo caso si dovrebbe eseguire con due + chiamate a \func{splice}.} In realtà le due system call sono profondamente +diverse nel loro meccanismo di funzionamento; \func{sendfile} infatti, come +accennato, non necessita affatto di avere a disposizione un buffer interno, +perché esegue un trasferimento diretto di dati; questo la rende in generale +molto più efficiente, ma anche limitata nelle sue applicazioni, dato che +questo tipo di trasferimento è possibile solo in casi specifici.\footnote{e + nel caso di Linux questi sono anche solo quelli in cui essa può essere + effettivamente utilizzata.} + +Il concetto che sta dietro a \func{splice} invece è diverso,\footnote{in + realtà la proposta originale di Larry Mc Voy non differisce poi tanto negli + scopi da \func{sendfile}, quello che rende \func{splice} davvero diversa è + stata la reinterpretazione che ne è stata fatta nell'implementazione su + Linux realizzata da Jens Anxboe, concetti che sono esposti sinteticamente + dallo stesso Linus Torvalds in \href{http://kerneltrap.org/node/6505} + {\texttt{http://kerneltrap.org/node/6505}}.} si tratta semplicemente di una +funzione che consente di fare in maniera del tutto generica delle operazioni +di trasferimento di dati fra un file e un buffer gestito interamente in kernel +space. In questo caso il cuore della funzione (e delle affini \func{vmsplice} +e \func{tee}, che tratteremo più avanti) è appunto l'uso di un buffer in +kernel space, e questo è anche quello che ne ha semplificato l'adozione, +perché l'infrastruttura per la gestione di un tale buffer è presente fin dagli +albori di Unix per la realizzazione delle \textit{pipe} (vedi +sez.~\ref{sec:ipc_unix}). Dal punto di vista concettuale allora \func{splice} +non è altro che una diversa interfaccia (rispetto alle \textit{pipe}) con cui +utilizzare in user space l'oggetto ``\textsl{buffer in kernel space}''. + +Così se per una \textit{pipe} o una \textit{fifo} il buffer viene utilizzato +come area di memoria (vedi fig.~\ref{fig:ipc_pipe_singular}) dove appoggiare i +dati che vengono trasferiti da un capo all'altro della stessa per creare un +meccanismo di comunicazione fra processi, nel caso di \func{splice} il buffer +viene usato o come fonte dei dati che saranno scritti su un file, o come +destinazione dei dati che vengono letti da un file. La funzione \funcd{splice} +fornisce quindi una interfaccia generica che consente di trasferire dati da un +buffer ad un file o viceversa; il suo prototipo, accessibile solo dopo aver +definito la macro \macro{\_GNU\_SOURCE},\footnote{si ricordi che questa + funzione non è contemplata da nessuno standard, è presente solo su Linux, e + pertanto deve essere evitata se si vogliono scrivere programmi portabili.} +è il seguente: +\begin{functions} + \headdecl{fcntl.h} + + \funcdecl{long splice(int fd\_in, off\_t *off\_in, int fd\_out, off\_t + *off\_out, size\_t len, unsigned int flags)} + + Trasferisce dati da un file verso una pipe o viceversa. + + \bodydesc{La funzione restituisce il numero di byte trasferiti in caso di + successo e $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno + dei valori: + \begin{errlist} + \item[\errcode{EBADF}] uno o entrambi fra \param{fd\_in} e \param{fd\_out} + non sono file descriptor validi o, rispettivamente, non sono stati + aperti in lettura o scrittura. + \item[\errcode{EINVAL}] il filesystem su cui si opera non supporta + \func{splice}, oppure nessuno dei file descriptor è una pipe, oppure si + è dato un valore a \param{off\_in} o \param{off\_out} ma il + corrispondente file è un dispositivo che non supporta la funzione + \func{seek}. + \item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'operazione + richiesta. + \item[\errcode{ESPIPE}] o \param{off\_in} o \param{off\_out} non sono + \const{NULL} ma il corrispondente file descriptor è una \textit{pipe}. + \end{errlist} + } +\end{functions} + +La funzione esegue un trasferimento di \param{len} byte dal file descriptor +\param{fd\_in} al file descriptor \param{fd\_out}, uno dei quali deve essere +una \textit{pipe}; l'altro file descriptor può essere +qualunque.\footnote{questo significa che può essere, oltre che un file di + dati, anche un altra \textit{pipe}, o un socket.} Come accennato una +\textit{pipe} non è altro che un buffer in kernel space, per cui a seconda che +essa sia usata per \param{fd\_in} o \param{fd\_out} si avrà rispettivamente la +copia dei dati dal buffer al file o viceversa. + +In caso di successo la funzione ritorna il numero di byte trasferiti, che può +essere, come per le normali funzioni di lettura e scrittura su file, inferiore +a quelli richiesti; un valore negativo indicherà un errore mentre un valore +nullo indicherà che non ci sono dati da trasferire (ad esempio si è giunti +alla fine del file in lettura). Si tenga presente che, a seconda del verso del +trasferimento dei dati, la funzione si comporta nei confronti del file +descriptor che fa riferimento al file ordinario, come \func{read} o +\func{write}, e pertanto potrà anche bloccarsi (a meno che non si sia aperto +il suddetto file in modalità non bloccante). + +I due argomenti \param{off\_in} e \param{off\_out} consentono di specificare, +come per l'analogo \param{offset} di \func{sendfile}, la posizione all'interno +del file da cui partire per il trasferimento dei dati. Come per +\func{sendfile} un valore nullo indica di usare la posizione corrente sul +file, ed essa sarà aggiornata automaticamente secondo il numero di byte +trasferiti. Un valore non nullo invece deve essere un puntatore ad una +variabile intera che indica la posizione da usare; questa verrà aggiornata, al +ritorno della funzione, al byte successivo all'ultimo byte trasferito. +Ovviamente soltanto uno di questi due argomenti, e più precisamente quello che +fa riferimento al file descriptor non associato alla \textit{pipe}, può essere +specificato come valore non nullo. + +Infine l'argomento \param{flags} consente di controllare alcune +caratteristiche del funzionamento della funzione; il contenuto è una maschera +binaria e deve essere specificato come OR aritmetico dei valori riportati in +tab.~\ref{tab:splice_flag}. Alcuni di questi valori vengono utilizzati anche +dalle funzioni \func{vmslice} e \func{tee} per cui la tabella riporta le +descrizioni complete di tutti i valori possibili anche quando, come per +\const{SPLICE\_F\_GIFT}, questi non hanno effetto su \func{splice}. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{10cm}|} + \hline + \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \const{SPLICE\_F\_MOVE} & Suggerisce al kernel di spostare le pagine + di memoria contenenti i dati invece di + copiarle;\footnotemark viene usato soltanto + da \func{splice}.\\ + \const{SPLICE\_F\_NONBLOCK}& Richiede di operare in modalità non + bloccante; questo flag influisce solo sulle + operazioni che riguardano l'I/O da e verso la + \textit{pipe}. Nel caso di \func{splice} + questo significa che la funzione potrà + comunque bloccarsi nell'accesso agli altri + file descriptor (a meno che anch'essi non + siano stati aperti in modalità non + bloccante).\\ + \const{SPLICE\_F\_MORE} & Indica al kernel che ci sarà l'invio di + ulteriori dati in una \func{splice} + successiva, questo è un suggerimento utile + che viene usato quando \param{fd\_out} è un + socket.\footnotemark Attualmente viene usato + solo da \func{splice}, potrà essere + implementato in futuro anche per + \func{vmslice} e \func{tee}.\\ + \const{SPLICE\_F\_GIFT} & Le pagine di memoria utente sono + ``\textsl{donate}'' al kernel;\footnotemark + se impostato una seguente \func{splice} che + usa \const{SPLICE\_F\_MOVE} potrà spostare le + pagine con successo, altrimenti esse dovranno + essere copiate; per usare questa opzione i + dati dovranno essere opportunamente allineati + in posizione ed in dimensione alle pagine di + memoria. Viene usato soltanto da + \func{vmsplice}.\\ + \hline + \end{tabular} + \caption{Le costanti che identificano i bit della maschera binaria + dell'argomento \param{flags} di \func{slice}, \func{vmslice} e \func{tee}.} + \label{tab:splice_flag} +\end{table} + +\footnotetext{per una maggiore efficienza \func{splice} usa quando possibile i + 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.} + +\footnotetext{questa opzione consente di utilizzare delle opzioni di gestione + dei socket che permettono di ottimizzare le trasmissioni via rete, si veda + la descrizione di \const{TCP\_CORK} in sez.~\ref{sec:sock_tcp_udp_options} e + quella di \const{MSG\_MORE} in sez.~\ref{sec:net_sendmsg}.} + +\footnotetext{questo significa che la cache delle pagine e i dati su disco + potranno differire, e che l'applicazione non potrà modificare quest'area di + memoria.} + +Per capire meglio il funzionamento di \func{splice} vediamo un esempio con un +semplice programma che usa questa funzione per effettuare la copia di un file +su un altro senza utilizzare buffer in user space. Il programma si chiama +\texttt{splicecp.c} ed il codice completo è disponibile coi sorgenti allegati +alla guida, il corpo principale del programma, che non contiene la sezione di +gestione delle opzioni e le funzioni di ausilio è riportato in +fig.~\ref{fig:splice_example}. + +Lo scopo del programma è quello di eseguire la copia dei con \func{splice}, +questo significa che si dovrà usare la funzione due volte, prima per leggere i +dati e poi per scriverli, appoggiandosi ad un buffer in kernel space (vale a +dire ad una \textit{pipe}); lo schema del flusso dei dati è illustrato in +fig.~\ref{fig:splicecp_data_flux}. + +\begin{figure}[htb] + \centering + \includegraphics[height=6cm]{img/splice_copy} + \caption{Struttura del flusso di dati usato dal programma \texttt{splicecp}.} + \label{fig:splicecp_data_flux} +\end{figure} + +Una volta trattate le opzioni il programma verifica che restino +(\texttt{\small 13--16}) i due argomenti che indicano il file sorgente ed il +file destinazione. Il passo successivo è aprire il file sorgente +(\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} + \includecodesample{listati/splicecp.c} + \end{minipage} + \normalsize + \caption{Esempio di codice che usa \func{splice} per effettuare la copia di + un file.} + \label{fig:splice_example} +\end{figure} + +Il ciclo principale (\texttt{\small 33--59}) inizia con la lettura dal file +sorgente tramite la prima \func{splice} (\texttt{\small 34--35}), in questo +caso si è usato come primo argomento il file descriptor del file sorgente e +come terzo quello del capo in scrittura della \textit{pipe} (il funzionamento +delle \textit{pipe} è trattato in dettaglio in sez.~\ref{sec:ipc_unix}, +nell'ottica dell'uso di \func{splice} questa operazione corrisponde +semplicemente alla copia dei dati dal file al buffer). + +La lettura viene eseguita in blocchi pari alla dimensione specificata +dall'opzione \texttt{-s} (il default è 4096); essendo in questo caso +\func{splice} equivalente ad una \func{read} sul file, se ne controlla il +valore di uscita in \var{nread} che indica quanti byte sono stati letti, se +nullo (\texttt{\small 36}) questo significa che si è giunti alla fine del file +sorgente e pertanto l'operazione di copia è conclusa e si può uscire dal +ciclo. In caso di valore negativo c'è stato un errore ed allora si ripete la +lettura se questo è dovuto ad una interruzione, o si esce altrimenti +(\texttt{\small 37--44}). + +Una volta completata con successo la lettura si avvia il ciclo di scrittura +(\texttt{\small 45--58}); questo inizia (\texttt{\small 46--47}) con la +seconda \func{splice} che cerca di scrivere gli \var{nread} byte letti, si +noti come in questo caso il primo argomento sia il capo in lettura della +\textit{pipe} ed il terzo il file descriptor del file di destinazione. + +Di nuovo si controlla il numero di byte effettivamente scritti restituito in +\var{nwrite} e se nullo (\texttt{\small 48}) si ripete la scrittura (in realtà +questo non avverrà mai), in caso di errore (\texttt{\small 49--56}) al solito +si ripete la scrittura se questo è dovuto a una interruzione o si esce con un +messaggio negli altri casi. + +Infine si chiude il ciclo di scrittura sottraendo (\texttt{\small 57}) il +numero di byte scritti a quelli letti, così che il ciclo di scrittura venga +ripetuto (\texttt{\small 58}) qualora la chiamata a \func{splice} non abbia +esaurito tutti i dati presenti sul buffer. + +Si noti come il programma sia concettualmente identico a quello che si sarebbe +scritto usando \func{read} al posto della prima \func{splice} e \func{write} al +posto della seconda, utilizzando un buffer in user space per eseguire la copia +dei file, solo che in questo caso non è stato necessario allocare nessun +buffer e non si è trasferito nessun dato in user space. + + +% 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://en.wikipedia.org/wiki/Splice_(system_call) + + + + +\subsection{Gestione avanzata dell'accesso ai dati dei file} +\label{sec:file_fadvise} + +Nell'uso generico dell'interfaccia per l'accesso al contenuto dei file le +operazioni di lettura e scrittura non necessitano di nessun intervento di +supervisione da parte dei programmi, si eseguirà una \func{read} o una +\func{write}, i dati verranno passati al kernel che provvederà ad effettuare +tutte le operazioni (e a gestire il \textit{caching} dei dati) per portarle a +termine in quello che ritiene essere il modo più efficiente. + +Il problema è che il concetto di migliore efficienza impiegato dal kernel è +relativo all'uso generico, mentre esistono molti casi in cui ci sono esigenze +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. + + +% 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 + +% TODO documentare \func{fallocate}, introdotta con il 2.6.23 +% vedi http://lwn.net/Articles/226710/ e http://lwn.net/Articles/240571/ + + +%\subsection{L'utilizzo delle porte di I/O} +%\label{sec:file_io_port} +% +% TODO l'I/O sulle porte di I/O +% consultare le manpage di ioperm, iopl e outb + + + + \section{Il file locking} \label{sec:file_locking} @@ -3056,7 +3369,7 @@ rimuovere un \textit{file lock} \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{EWOULDBLOCK}] Il file ha già un blocco attivo, e si è + \item[\errcode{EWOULDBLOCK}] il file ha già un blocco attivo, e si è specificato \const{LOCK\_NB}. \end{errlist} } @@ -3192,17 +3505,17 @@ essa viene usata solo secondo il prototipo: \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{EACCES}] L'operazione è proibita per la presenza di + \item[\errcode{EACCES}] l'operazione è proibita per la presenza di \textit{file lock} da parte di altri processi. - \item[\errcode{ENOLCK}] Il sistema non ha le risorse per il locking: ci + \item[\errcode{ENOLCK}] il sistema non ha le risorse per il locking: ci sono troppi segmenti di lock aperti, si è esaurita la tabella dei lock, o il protocollo per il locking remoto è fallito. - \item[\errcode{EDEADLK}] Si è richiesto un lock su una regione bloccata da + \item[\errcode{EDEADLK}] si è richiesto un lock su una regione bloccata da un altro processo che è a sua volta in attesa dello sblocco di un lock mantenuto dal processo corrente; si avrebbe pertanto un \itindex{deadlock} \textit{deadlock}. Non è garantito che il sistema riconosca sempre questa situazione. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale prima + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima di poter acquisire un lock. \end{errlist} ed inoltre \errval{EBADF}, \errval{EFAULT}. @@ -3634,10 +3947,10 @@ che utilizza la funzione \funcd{lockf}, il cui prototipo \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{EWOULDBLOCK}] Non è possibile acquisire il lock, e si è + \item[\errcode{EWOULDBLOCK}] non è possibile acquisire il lock, e si è selezionato \const{LOCK\_NB}, oppure l'operazione è proibita perché il file è mappato in memoria. - \item[\errcode{ENOLCK}] Il sistema non ha le risorse per il locking: ci + \item[\errcode{ENOLCK}] il sistema non ha le risorse per il locking: ci sono troppi segmenti di lock aperti, si è esaurita la tabella dei lock. \end{errlist} ed inoltre \errval{EBADF}, \errval{EINVAL}. @@ -3818,7 +4131,8 @@ 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 +% LocalWords: NUL sizeof casting printevent nread limits sysconf SC wrapper +% LocalWords: splice result argument DMA controller zerocopy Linus %%% Local Variables: