From a35faaa061294d0c8d5c9e8b57b865d4a5c20a6c Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sat, 2 Jan 2010 14:51:00 +0000 Subject: [PATCH] Trattati gli sparse file e gli hole dei file, e aggiunte varie correzioni --- fileadv.tex | 114 ++++++++++++++++++++++++++++--------------------- filedir.tex | 13 +++--- fileunix.tex | 73 ++++++++++++++++++++++++++----- sockctrl.tex | 2 +- trasplayer.tex | 2 +- 5 files changed, 138 insertions(+), 66 deletions(-) diff --git a/fileadv.tex b/fileadv.tex index 9724919..5a6a3c2 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -4315,17 +4315,17 @@ anche se un valore nullo avrebbe dato gli stessi risultati, l'uso di questi flag, che si ricordi servono solo a dare suggerimenti al kernel, permette in genere di migliorare le prestazioni. -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 +Come accennato con l'introduzione di \func{splice} sono state realizzate anche +altre due \textit{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 trasferimento dati fra file -descriptor, le tratteremo qui. +descriptor, le tratteremo qui, essendo strettamente correlate fra loro. La prima funzione, \funcd{vmsplice}, è la più simile a \func{splice} e come -indica il suo nome consente di trasferire i dati dalla memoria di un processo -(ad esempio per un file mappato in memoria) verso una \textit{pipe}, il suo -prototipo è: +indica il suo nome consente di trasferire i dati dalla memoria virtuale di un +processo (ad esempio per un file mappato in memoria) verso una \textit{pipe}; +il suo prototipo è: \begin{functions} \headdecl{fcntl.h} \headdecl{sys/uio.h} @@ -4351,24 +4351,25 @@ prototipo La \textit{pipe} indicata da \param{fd} dovrà essere specificata tramite il file descriptor 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 dovrà utilizzare un vettore di -strutture \struct{iovec} (vedi fig.~\ref{fig:file_iovec}), esattamente con gli -stessi criteri con cui le si usano per l'I/O vettorizzato, indicando gli -indirizzi e le dimensioni di ciascun segmento di memoria su cui si vuole -operare; 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}. +faccia riferimento a sez.~\ref{sec:ipc_unix}), mentre per indicare quali +segmenti della memoria del processo devono essere trasferiti verso di essa si +dovrà utilizzare un vettore di strutture \struct{iovec} (vedi +fig.~\ref{fig:file_iovec}), esattamente con gli stessi criteri con cui le si +usano per l'I/O vettorizzato, indicando gli indirizzi e le dimensioni di +ciascun segmento di memoria su cui si vuole operare; 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}. In caso di successo la funzione ritorna il numero di byte trasferiti sulla \textit{pipe}. In generale, se i dati una volta creati non devono essere riutilizzati (se cioè l'applicazione che chiama \func{vmsplice} non modificherà più la memoria trasferita), è opportuno utilizzare per \param{flag} il valore \const{SPLICE\_F\_GIFT}; questo fa sì che il 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. +possa rimuovere le relative pagine dalla cache della memoria virtuale, così +che queste possono essere utilizzate immediatamente senza necessità di +eseguire una copia dei dati che contengono. La seconda funzione aggiunta insieme a \func{splice} è \func{tee}, che deve il suo nome all'omonimo comando in user space, perché in analogia con questo @@ -4403,7 +4404,12 @@ sorgente e \param{fd\_out} il capo in scrittura della \textit{pipe} destinazione; a differenza di quanto avviene con \func{read} i dati letti con \func{tee} da \func{fd\_in} non vengono \textsl{consumati} e restano disponibili sulla \textit{pipe} per una successiva lettura (di nuovo per il -comportamento delle \textit{pipe} si veda sez.~\ref{sec:ipc_unix}). +comportamento delle \textit{pipe} si veda sez.~\ref{sec:ipc_unix}). Al +momento\footnote{quello della stesura di questo paragrafo, avvenuta il Gennaio + 2010, in futuro potrebbe essere implementato anche \const{SPLICE\_F\_MORE}.} +il solo valore utilizzabile per \param{flag}, fra quelli elencati in +tab.~\ref{tab:splice_flag}, è \const{SPLICE\_F\_NONBLOCK} che rende la +funzione non bloccante. 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 @@ -4521,35 +4527,37 @@ La funzione richiede che venga letto in anticipo il contenuto del file \index{memoria~virtuale} memoria virtuale ed il meccanismo della \index{paginazione} paginazione per cui la lettura viene eseguita in blocchi corrispondenti alle dimensioni delle pagine di memoria, ed i valori di -\param{offset} e \param{count} arrotondati di conseguenza. +\param{offset} e \param{count} vengono arrotondati di conseguenza. La funzione estende quello che è un comportamento normale del kernel che quando si legge un file, aspettandosi che l'accesso prosegua, esegue sempre una lettura preventiva di una certa quantità di dati; questo meccanismo di lettura anticipata viene chiamato \textit{read-ahead}, da cui deriva il nome -della funzione. La funzione, per ottimizzare gli accessi a disco, effettua la -lettura in cache della sezione richiesta e si blocca 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 -interrompe una volta raggiunta la fine del file. +della funzione. La funzione \func{readahead}, per ottimizzare gli accessi a +disco, effettua la lettura in cache della sezione richiesta e si blocca +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 interrompe 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. +all'interno di un programma tutte le volte che si conosce in anticipo quanti +dati saranno necessari nelle elaborazioni successive. Si potrà così +concentrare in un unico momento (ad esempio in fase di inizializzazione) la +lettura dei dati da disco, così da ottenere una migliore velocità di risposta +nelle operazioni successive. \itindend{read-ahead} Il concetto di \func{readahead} viene generalizzato nello standard -POSIX.1-2001 dalla funzione \funcd{posix\_fadvise},\footnote{anche se +POSIX.1-2001 dalla funzione \func{posix\_fadvise},\footnote{anche se l'argomento \param{len} è stato modificato da \ctyp{size\_t} a \ctyp{off\_t} nella revisione POSIX.1-2003 TC5.} che consente di ``\textsl{avvisare}'' il kernel sulle modalità con cui si intende accedere nel futuro ad una certa porzione di un file,\footnote{la funzione però è stata introdotta su Linux solo a partire dal kernel 2.5.60.} così che esso possa provvedere le -opportune ottimizzazioni; il suo prototipo, che può è disponibile solo se si -definisce la macro \macro{\_XOPEN\_SOURCE} ad almeno 600, è: +opportune ottimizzazioni; il prototipo di \funcd{posix\_fadvise}, che è +disponibile soltanto se è stata definita la macro \macro{\_XOPEN\_SOURCE} ad +valore di almeno 600, è: \begin{functions} \headdecl{fcntl.h} @@ -4617,18 +4625,18 @@ che utilizza semplicemente l'informazione. Come \func{madvise} anche \func{posix\_fadvise} si appoggia al sistema della memoria virtuale ed al meccanismo standard del \textit{read-ahead} utilizzato -dal kernel; in particolare con \const{POSIX\_FADV\_SEQUENTIAL} si raddoppia la -dimensione dell'ammontare di dati letti preventivamente rispetto al default, -aspettandosi appunto una lettura sequenziale che li utilizzerà, mentre con -\const{POSIX\_FADV\_RANDOM} si disabilita del tutto il suddetto meccanismo, -dato che con un accesso del tutto casuale è inutile mettersi a leggere i dati -immediatamente successivi gli attuali; infine l'uso di -\const{POSIX\_FADV\_NORMAL} consente di riportarsi al comportamento di -default. +dal kernel; in particolare utilizzando il valore +\const{POSIX\_FADV\_SEQUENTIAL} si raddoppia la dimensione dell'ammontare di +dati letti preventivamente rispetto al default, aspettandosi appunto una +lettura sequenziale che li utilizzerà, mentre con \const{POSIX\_FADV\_RANDOM} +si disabilita del tutto il suddetto meccanismo, dato che con un accesso del +tutto casuale è inutile mettersi a leggere i dati immediatamente successivi +gli attuali; infine l'uso di \const{POSIX\_FADV\_NORMAL} consente di +riportarsi al comportamento di default. Le due modalità \const{POSIX\_FADV\_NOREUSE} e \const{POSIX\_FADV\_WILLNEED} fino al kernel 2.6.18 erano equivalenti, a partire da questo kernel la prima -viene non ha più alcune effetto, mentre la seconda dà inizio ad una lettura in +viene non ha più alcun effetto, mentre la seconda dà inizio ad una lettura in cache della regione del file indicata. La quantità di dati che verranno letti è ovviamente limitata in base al carico che si viene a creare sul sistema della memoria virtuale, ma in genere una lettura di qualche megabyte viene @@ -4647,11 +4655,12 @@ nuovi dati utili.\footnote{la pagina di manuale riporta l'esempio dello Sia \func{posix\_fadvise} che \func{readahead} attengono alla ottimizzazione dell'accesso in lettura; lo standard POSIX.1-2001 prevede anche una funzione -specifica per le operazioni di scrittura, \func{posix\_fallocate},\footnote{la - funzione è stata introdotta a partire dalle glibc 2.1.94.} che consente di -preallocare dello spazio disco per assicurarsi che una seguente scrittura non -fallisca, il suo prototipo, anch'esso disponibile solo se si definisce la -macro \macro{\_XOPEN\_SOURCE} ad almeno 600, è: +specifica per le operazioni di scrittura, +\funcd{posix\_fallocate},\footnote{la funzione è stata introdotta a partire + dalle glibc 2.1.94.} che consente di preallocare dello spazio disco per +assicurarsi che una seguente scrittura non fallisca, il suo prototipo, +anch'esso disponibile solo se si definisce la macro \macro{\_XOPEN\_SOURCE} ad +almeno 600, è: \begin{functions} \headdecl{fcntl.h} @@ -4686,6 +4695,14 @@ saranno incrementate di conseguenza. Dopo aver eseguito con successo la funzione è garantito che una scrittura nella regione indicata non fallirà per mancanza di spazio disco. +Ci si può chiedere a cosa serva una funzione come \func{posix\_fallocate} dato +che è sempre possibile ottenere l'effetto voluto eseguendo manualmente sul +file la scrittura\footnote{usando \funcd{pwrite} per evitare spostamenti della + posizione corrente sul file.} di una serie di zeri per l'estensione di +spazio necessaria. In realtà questa è la modalità con cui la funzione veniva +realizzata nella prima versione fornita dalle \acr{glibc}, e si trattava nel +caso soltanto di una standardizzazione delle modalità di + % TODO controllare la trattazione della nuova funzionalità di preallocazione % TODO documentare \func{posix\_fadvise} @@ -4792,7 +4809,8 @@ sez.~\ref{sec:intro_syscall}; il suo prototipo % LocalWords: nwrite segs patch readahead posix fadvise TC advice FADV NORMAL % LocalWords: SEQUENTIAL NOREUSE WILLNEED DONTNEED streaming fallocate EFBIG % LocalWords: POLLRDHUP half close pwait Gb madvise MADV ahead REMOVE tmpfs -% LocalWords: DONTFORK DOFORK shmfs preadv pwritev syscall linux loff +% LocalWords: DONTFORK DOFORK shmfs preadv pwritev syscall linux loff head +% LocalWords: MERGEABLE EOVERFLOW conditions prealloca %%% Local Variables: diff --git a/filedir.tex b/filedir.tex index 7b2e770..cfb55da 100644 --- a/filedir.tex +++ b/filedir.tex @@ -1680,9 +1680,10 @@ dimensione inferiore sarebbe inefficiente. Si tenga conto che la lunghezza del file riportata in \var{st\_size} non è detto che corrisponda all'occupazione dello spazio su disco per via della -possibile esistenza dei cosiddetti \textit{holes} (letteralmente -\textsl{buchi}) che si formano tutte le volte che si va a scrivere su un file -dopo aver eseguito una \func{lseek} (vedi sez.~\ref{sec:file_lseek}) oltre la +possibile esistenza dei cosiddetti \index{file!\textit{hole}} \textit{holes} +(letteralmente \textsl{buchi}) che si formano tutte le volte che si va a +scrivere su un \itindex{sparse~file} file dopo aver eseguito una \func{lseek} +(tratteremo in dettaglio l'argomento in sez.~\ref{sec:file_lseek}) oltre la sua fine. In questo caso si avranno risultati differenti a seconda del modo in cui si @@ -1732,9 +1733,9 @@ dimensione si possono usare le due funzioni \funcd{truncate} e Se il file è più lungo della lunghezza specificata i dati in eccesso saranno perduti; il comportamento in caso di lunghezza inferiore non è specificato e dipende dall'implementazione: il file può essere lasciato invariato o esteso -fino alla lunghezza scelta; in quest'ultimo caso lo spazio viene riempito con -zeri (e in genere si ha la creazione di un \textit{hole} nel file). - +fino alla lunghezza scelta; nel caso di Linux viene esteso con la creazione di +un \index{file!\textit{hole}} \textsl{buco} nel \itindex{sparse~file} file e +ad una lettura si otterranno degli zeri. \subsection{I tempi dei file} \label{sec:file_file_times} diff --git a/fileunix.tex b/fileunix.tex index a81c425..cfc948f 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -523,8 +523,10 @@ file. successo e $-1$ in caso di errore nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{ESPIPE}] \param{fd} è una pipe, un socket o una fifo. + \item[\errcode{ESPIPE}] \param{fd} è una pipe, un socket o una fifo. \item[\errcode{EINVAL}] \param{whence} non è un valore valido. + \item[\errcode{EOVERFLOW}] \param{offset} non può essere rappresentato nel + tipo \type{off\_t}. \end{errlist} ed inoltre \errval{EBADF}.} \end{functions} @@ -546,14 +548,12 @@ seguenti valori\footnote{per compatibilit per ottenere la nuova posizione corrente. \end{basedescript} -Come accennato in sez.~\ref{sec:file_file_size} con \func{lseek} è possibile -impostare la posizione corrente anche oltre la fine del file, e alla -successiva scrittura il file sarà esteso. La chiamata non causa nessun accesso -al file, si limita a modificare la posizione corrente (cioè il valore -\var{f\_pos} in \param{file}, vedi fig.~\ref{fig:file_proc_file}). Dato che la -funzione ritorna la nuova posizione, usando il valore zero per \param{offset} -si può riottenere la posizione corrente nel file chiamando la funzione con -\code{lseek(fd, 0, SEEK\_CUR)}. +Si tenga presente che la chiamata a \func{lseek} non causa nessun accesso al +file, si limita a modificare la posizione corrente (cioè il valore +\var{f\_pos} in \param{file}, vedi fig.~\ref{fig:file_proc_file}). Dato che +la funzione ritorna la nuova posizione, usando il valore zero +per \param{offset} si può riottenere la posizione corrente nel file chiamando +la funzione con \code{lseek(fd, 0, SEEK\_CUR)}. Si tenga presente inoltre che usare \const{SEEK\_END} non assicura affatto che la successiva scrittura avvenga alla fine del file, infatti se questo è stato @@ -568,10 +568,63 @@ per i tre casi citati nel prototipo, vale anche per tutti quei dispositivi che non supportano questa funzione, come ad esempio per i file di terminale.\footnote{altri sistemi, usando \const{SEEK\_SET}, in questo caso ritornano il numero di caratteri che vi sono stati scritti.} Lo standard -POSIX però non specifica niente in proposito. Infine alcuni file speciali, ad +POSIX però non specifica niente in proposito. Inoltre alcuni file speciali, ad esempio \file{/dev/null}, non causano un errore ma restituiscono un valore indefinito. +\itindbeg{sparse~file} + +Infine si tenga presente che, come accennato in sez.~\ref{sec:file_file_size}, +con \func{lseek} è possibile impostare una posizione anche oltre la corrente +fine del file; ed in tal caso alla successiva scrittura il file sarà esteso a +partire da detta posizione. In questo caso si ha quella che viene chiamata la +creazione di un \index{file!\textit{hole}} \textsl{buco} nel file, accade cioè +che nonostante la dimensione del file sia cresciuta in seguito alla scrittura +effettuata, lo spazio vuoto fra la precedente fine del file ed la nuova parte +scritta dopo lo spostamento, non corrisponda ad una allocazione effettiva di +spazio su disco, che sarebbe inutile dato che quella zona è effettivamente +vuota. + +Questa è una delle caratteristiche spcifiche della gestione dei file di un +sistema unix-like, ed in questo caso si ha appunto quello che in gergo si +chiama un \index{file!\textit{hole}} \textit{hole} nel file e si dice che il +file in questione è uno \textit{sparse file}. In sostanza, se si ricorda la +struttura di un filesystem illustrata in fig.~\ref{fig:file_filesys_detail}, +quello che accade è che nell'\textit{inode} del file viene segnata +l'allocazione di un blocco di dati a partire dalla nuova posizione, ma non +viene allocato nulla per le posizioni intermedie; in caso di lettura +sequenziale del contenuto del file il kernel si accorgerà della presenza del +buco, e restituirà degli zeri come contenuto di quella parte del file. + +Questa funzionalità comporta una delle caratteristiche della gestione dei file +su Unix che spesso genera più confusione in chi non la conosce, per cui +sommando le dimensioni dei file si può ottenere, se si hanno molti +\textit{sparse file}, un totale anche maggiore della capacità del proprio +disco e comunque maggiore della dimensione che riporta un comando come +\cmd{du}, che calcola lo spazio disco occupato in base al numero dei blocchi +effettivamente allocati per il file. + +Questo avviene proprio perché in un sistema unix-like la dimensione di un file +è una caratteristica del tutto indipendente dalla quantità di spazio disco +effettivamente allocato, e viene registrata sull'\textit{inode} come le altre +proprietà del file. La dimensione viene aggiornata automaticamente quando si +estende un file scrivendoci, e viene riportata dal campo \var{st\_size} di una +struttura \struct{stat} quando si effettua chiamata ad una delle funzioni +\texttt{*stat} viste in sez.~\ref{sec:file_stat}. + +Questo comporta che in generale, fintanto che lo si è scritto sequenzialmente, +la dimensione di un file sarà più o meno corrispondente alla quantità di +spazio disco da esso occupato, ma esistono dei casi, come questo in cui ci si +sposta in una posizione oltre la fine corrente del file, o come quello +accennato in in sez.~\ref{sec:file_file_size} in cui si estende la dimensione +di un file con una \func{truncate}, in cui in sostanza di modifica il valore +della dimensione di \var{st\_size} senza allocare spazio su disco. Questo +consente di creare inizialmente file di dimensioni anche molto grandi, senza +dover occupare da subito dello spazio disco che in realtà sarebbe +inutilizzato. + +\itindend{sparse~file} + \subsection{Le funzioni \func{read} e \func{pread}} \label{sec:file_read} diff --git a/sockctrl.tex b/sockctrl.tex index ba7d304..e1980b0 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -2596,7 +2596,7 @@ altro socket. Questo ovviamente non ha senso per il normale traffico di rete, in cui i pacchetti vengono scambiati direttamente fra due applicazioni; ma quando un sistema supporta il traffico in \itindex{multicast} \textit{multicast}, in cui una applicazione invia i pacchetti a molte altre -(vedi sez.~\ref{sec:multicast_xxx}), allora ha senso che su una macchina i +(vedi sez.~\ref{sec:xxx_multicast}), allora ha senso che su una macchina i pacchetti provenienti dal traffico in \itindex{multicast} \textit{multicast} possano essere ricevuti da più applicazioni\footnote{l'esempio classico di traffico in \textit{multicast} è quello di uno streaming di dati (audio, diff --git a/trasplayer.tex b/trasplayer.tex index b3d70b3..d8a0804 100644 --- a/trasplayer.tex +++ b/trasplayer.tex @@ -58,7 +58,7 @@ comando \cmd{netstat} nel campo \textit{State}. \centering \includegraphics[width=10cm]{img/tcp_head} \caption{L'intestazione del protocollo TCP.} - \label{fig:UDP_header} + \label{fig:TCP_header} \end{figure} \itindbeg{Maximum~Segment~Size|(} -- 2.30.2