From 06a411c6242a35082817a74e9a86ec226d8bf0e3 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Tue, 6 Aug 2002 17:38:13 +0000 Subject: [PATCH] Avanti sul memory mapping --- fileadv.tex | 286 +++++++++++++++++++++++++++++++++++++++++++------- fileintro.tex | 31 +++--- fileunix.tex | 3 +- 3 files changed, 269 insertions(+), 51 deletions(-) diff --git a/fileadv.tex b/fileadv.tex index 7a47057..790ccdb 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -830,34 +830,36 @@ Essi verranno letti (o scritti) nell'ordine in cui li si sono specificati. \label{sec:file_memory_map} Una modalità alternativa di I/O, che usa una interfaccia completamente diversa -rispetto a quella classica, è quella dei file \textsl{mappati in memoria} (il -cosiddetto \textit{memory-mapped I/O}). In sostanza quello che si fa è usare -il meccanismo della \textsl{paginazione}\index{paginazione} usato per la -memoria virtuale (vedi \secref{sec:proc_mem_gen}) vedere il file in una -sezione dello spazio di indirizzi del processo, in modo che l'accesso a -quest'ultimo avvenga con le normali operazioni di lettura e scrittura delle -variabili in memoria. - -Questa interfaccia è più efficiente dell'uso delle usuali funzioni di I/O, in -quanto permette di caricare in memoria solo le parti del file che sono -effettivamente usate ad un dato istante. Infatti, dato che l'accesso è fatto -direttamente attraverso la memoria virtuale, non è necessario trasferire in un -buffer tutti i dati che potrebbero servire, e poi riscrivere il tutto una -volta completate le operazioni, la scrittura e la lettura avverranno invece -direttamente sulla sezione di memoria mappata, che sarà a sua volta letta o -scritta sul file, una pagina alla volta (e solo per le parti effettivamente -usate) in maniera trasparente attraverso il meccanismo della paginazione. -L'acceso alle pagine non ancora caricate avverrà allo stesso modo con cui -vengono caricate in memoria le pagine che sono state salvate sullo swap. - -Inoltre in situazioni in cui la memoria è scarsa, le pagine che mappano un +rispetto a quella classica vista in \capref{cha:file_unix_interface}, è il +cosiddetto \textit{memory-mapped I/O}, che attraverso il meccanismo della +\textsl{paginazione}\index{paginazione} usato dalla memoria virtuale (vedi +\secref{sec:proc_mem_gen}) permette di \textsl{mappare} il contenuto di un +file in una sezione dello spazio di indirizzi del processo. + +Tutto questo comporta una notevole semplificazione delle operazioni di I/O, in +quanto non sarà più necessario utilizzare dei buffer intermedi su cui +appoggiare i dati da traferire, ma questi potranno essere acceduti +direttamente dalla sezione di memoria; inoltre questa interfaccia +è più efficiente delle usuali funzioni di I/O, in quanto permette di caricare +in memoria solo le parti del file che sono effettivamente usate ad un dato +istante. + +Infatti, dato che l'accesso è fatto direttamente attraverso la memoria +virtuale, la sezione di memoria mappata su cui si opera sarà a sua volta letta +o scritta sul file una pagina alla volta e solo per le parti effettivamente +usate, il tutto in maniera completamente trasparente al processo; l'acceso +alle pagine non ancora caricate avverrà allo stesso modo con cui vengono +caricate in memoria le pagine che sono state salvate sullo swap. + +Infine in situazioni in cui la memoria è scarsa, le pagine che mappano un file vengono salvate automaticamente, così come le pagine dei programmi vengono scritte sulla swap; questo consente di accedere ai file su dimensioni -il cui solo limite è quello dello spazio di indirizzi disponibile, +il cui solo limite è quello dello spazio di indirizzi disponibile, e non della +memoria su cui possono esserne lette delle porzioni. - -La funzione che permette di attivare il memory mapping di un file è -\func{mmap}, il suo prototipo è: +L'interfaccia prevede varie funzioni per la gestione del \textit{memory + mapping}, la prima di queste è \func{mmap}, che esegue la mappatura in +memoria un file; il suo prototipo è: \begin{functions} \headdecl{unistd.h} @@ -867,18 +869,230 @@ La funzione che permette di attivare il memory mapping di un file fd, off\_t offset)} Esegue la mappatura in memoria del file \param{fd}. + + \bodydesc{La funzione restituisce il puntatore alla zona di memoria mappata + in caso di successo, e \macro{MAP\_FAILED} (-1) in caso di errore, nel + qual caso \var{errno} viene settata ai valori: + \begin{errlist} + \item[\macro{EBADF}] Il file descriptor non è valido, e non si è usato + \macro{MAP\_ANONYMOUS}. + \item[\macro{EACCES}] Il file descriptor non si riferisce ad un file + normale, o si è richiesto \macro{MAP\_PRIVATE} ma \param{fd} non è + aperto in lettura, o si è richiesto \macro{MAP\_SHARED} e settato + \macro{PROT\_WRITE} ed \param{fd} non è aperto in lettura/scrittura, o + si è settato \macro{PROT\_WRITE} ed \param{fd} è in + \textit{append-only}. + \item[\macro{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[\macro{ETXTBSY}] Si è settato \macro{MAP\_DENYWRITE} ma \param{fd} è + aperto in scrittura. + \item[\macro{EAGAIN}] Il file è bloccato, o si è bloccata troppa memoria. + \item[\macro{ENOMEM}] Non c'è memoria o si è superato il limite sul numero + di mappature possibili. + \item[\macro{ENODEV}] Il filesystem di \param{fd} no supporta il memory + mapping. + \end{errlist} + } +\end{functions} + +La funzione richiede di mappare in memoria la sezione del file \param{fd} a +partire da \param{offset} per \param{lenght} byte, preferibilmente +all'indirizzo \param{start}. Il valore di \param{offset} deve essere un +multiplo della dimensione di una pagina di memoria. 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 questi segmenti il kernel + mantiene nella \textit{page table} la mappatura sulle pagine di memoria + reale, ed le modalità di accesso (lettura, esecuzione, scrittura); una loro + violazione causa quella che si chiama una \textit{segment violation}, e la + relativa emissione del segnale \macro{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 \tabref{tab:file_mmap_flag}; il valore +specificato deve essere compatibile con la modalità con cui si è aperto il +file. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \macro{PROT\_EXEC} & Le pagine possono essere eseguite.\\ + \macro{PROT\_READ} & Le pagine possono essere lette.\\ + \macro{PROT\_WRITE} & Le pagine possono essere scritte.\\ + \macro{PROT\_NONE} & L'accesso alle pagine è vietato.\\ + \hline + \end{tabular} + \caption{Valori dell'argomento \param{prot} di \func{mmap}, relativi alla + protezione applicate alle pagine del file mappate in memoria.} + \label{tab:file_mmap_prot} +\end{table} + +L'argomento \param{flags} specifica qual'è il tipo di oggetto mappato, le +opzioni relative alle modalità con cui è effettuata la mappatura e alle +modalità con cui le modifiche alla memoria mappata vengono condivise o +mantenute private al processo che le ha effettuate. Deve essere specificato +come maschera binaria ottenuta dall'OR di uno o più dei valori riportati in +\tabref{tab:file_mmap_flag}. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{10cm}|} + \hline + \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \macro{MAP\_FIXED} & Non permette di restituire un indirizzo diverso + da \param{start}, se questo non può essere usato + \func{mmap} fallisce. Se si setta questo flag il + valore di \param{start} deve essere allineato + alle dimensioni di una pagina. \\ + \macro{MAP\_SHARED} & I cambiamenti sulla memoria mappata vengono + riportati sul file e saranno immediatamente + visibili agli altri processi che mappano lo stesso + file.\footnotemark Il file su disco però non sarà + aggiornato fino alla chiamata di \func{rsync} o + \func{unmap}), e solo allora le modifiche saranno + visibili per l'I/O convenzionale. Incompatibile + con \macro{MAP\_PRIVATE}. \\ + \macro{MAP\_PRIVATE} & I cambiamenti sulla memoria mappata non vengono + riportati sul file. Ne viene fatta una copia + privata cui solo il processo chiamante ha + accesso. Le modifiche sono mantenute attraverso + il meccanismo del \textit{copy on write} e + salvate su swap in caso di necessità. Non è + specificato se i cambiamenti sul file originale + vengano riportati sulla regione + mappata. Incompatibile con \macro{MAP\_SHARED}. \\ + \macro{MAP\_DENYWRITE} & In Linux viene ignorato per evitare + \textit{DoS}\index{DoS} (veniva usato per + segnalare che tentativi di scrittura sul file + dovevano fallire con \macro{ETXTBUSY}).\\ + \macro{MAP\_EXECUTABLE}& Ignorato. \\ + \macro{MAP\_NORESERVE} & Si usa con \macro{MAP\_PRIVATE}. Non riserva + delle pagine di swap ad uso del meccanismo di + \textit{copy on write} per mantere le modifiche + fatte alla regione mappata, in + questo caso dopo una scrittura, se non c'è più + memoria disponibile, si ha l'emissione di + un \macro{SIGSEGV}. \\ + \macro{MAP\_LOCKED} & Se settato impedisce lo swapping delle pagine + mappate. \\ + \macro{MAP\_GROWSDOWN} & Usato per gli stack. Indica alla virtual memory + che la mappatura deve essere effettuata .\\ + \macro{MAP\_ANONYMOUS} & La mappatura non è associata a nessun file. Gli + argomenti \param{fd} e \param{offset} sono + ignorati.\footnotemark\\ + \macro{MAP\_ANON} & Sinonimo di \macro{MAP\_ANONYMOUS}, deprecato.\\ + \macro{MAP\_FILE} & Valore di compatibiità, deprecato.\\ + \hline + \end{tabular} + \caption{Valori possibili dell'argomento \param{flag} di \func{mmap}.} + \label{tab:file_mmap_flag} +\end{table} + +\footnotetext{Dato che tutti faranno riferimento alle stesse pagine di + memoria.} +\footnotetext{L'uso di questo flag con \macro{MAP\_SHARED} è + stato implementato in Linux a partire dai kernel della serie 2.4.x.} + +Un file viene sempre mappato su multipli delle dimensioni di una pagina, +qualora esso sia più corto la parte restante è riempita con zeri; eventuali +scritture in quella zona di memoria non vengono riportate sul file. Se le +dimensioni del file cambiano (esso viene esteso o troncato), non è specificato +quale effetto viene a aversi sulle pagine di memoria che corrispondono alle +regioni aggiunte o tolte. + +Si tenga presente che non tutti i file possono venire mappati in memoria; ad +esempio non è possibile mappare in memoria pipe, socket e fifo; lo stesso vale +anche per alcuni file di dispositivo, che non dispongono dell'operazione +relativa \var{mmap} (si ricordi quanto esposto in \secref{sec:file_vfs_work}). + +La memoria mappata viene mantenuta attraverso una \func{fork}; con gli stessi +attributi. In particolare se la memoria è condivisa lo sarà anche fra padre e +figlio, se è privata ognuno di essi manterrà una sua versione privata. Non c'è +invece nessun passaggio attraverso una \func{exec}, dato che quest'ultima +sostituisce tutto lo spazio degli indirizzo con quello del nuovo programma. + +Quando si effettua una mappatura di un file, i relativi tempi (vedi +\secref{sec:file_file_times}) vengono pure modificati. Il valore di +\var{st\_atime} può venir cambiato in un qualunque momento in cui la mappatura +sia attiva: il primo riferimento ad una pagina mappata aggiorna questo tempo. +I valori di \var{st\_ctime} e \var{st\_mtime} vengono cambiati solo quando è +possibile una scrittura (cioè per un file mappato con \macro{PROT\_WRITE} e +\macro{MAP\_SHARED}) e sono aggiornati dopo la scrittura e prima di una +eventuale \func{msync}. + +Dato per i file mappati in memoria le operazioni di I/O sono gestite +direttamente dalla memoria virtuale, occorre essere consapevoli delle +interazioni che possono esserci con operazioni effettuate con l'interfaccia +standard di \capref{sec:file_unix_interface}. Il problema è che una volta che +si è mappato un file, le operazioni di lettura e scrittura saranno eseguite +sulla memoria, e riportate su disco in maniera autonoma dal sistema della +memoria virtuale. + +Pertanto se si modifica un file con l'interfaccia standard queste modifiche +potranno essere visibili o meno a seconda del momento in cui la memoria +virtuale leggerà dal disco in memoria quella sezione del file, ed è del tutto +indefinito quale può essere il contenuto della memoria mappata. È però +possibile usare la funzione \func{msync} per sincronizzare il contenuto della +memoria con il file su disco; il suo prototipo è: +\begin{functions} + \headdecl{unistd.h} + \headdecl{sys/mman.h} + \funcdecl{int msync(const void *start, size\_t length, int flags)} + + Sincronizza i contenuti di una sezione di un file mappato in memoria. + \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di errore nel qual caso \var{errno} viene settata ai valori: \begin{errlist} - \item[\macro{EBADF}] Il file descriptor non è valido, e non si è usato - \macro{MAP\_ANONYMOUS}. - \item[\macro{EACCES}] \macro{MAP\_PRIVATE}. - \item[\macro{EINTR}] La funzione è stata interrotta da un segnale. + \item[\macro{EINVAL}] O \param{start} non è multiplo di \macro{PAGESIZE}, + o si è specificato un valore non valido per \param{flags}. + \item[\macro{EFAULT}] Il range specificato non ricade in una zona + precedentemente mappata. \end{errlist} - ed inoltre \macro{ENOMEM}.} + } \end{functions} +La funzione esegue la sincronizzazione su file di quanto scritto nella sezione +di memoria indicata da \param{start} e \param{offset}. Provvede anche ad +aggiornare i relativi tempi di modifica; in questo modo le funzioni +dell'interfaccia standard potranno accedere al contenuto aggiornato. + +L'argomento \param{flag} è specificato come maschera binaria composta da un OR +dei valori riportati in \tabref{tab:file_mmap_rsync}, di questi però +\macro{MS\_ASYNC} e \macro{MS\_SYNC} sono incompatibili; con il primo valore +infatti la funzione si limita ad inoltrare la richiesta di sincronizzazione al +meccanismo della memoria virtuale, ritornando subito, mentre con il secondo +attende che la sincronizzazione sia stata effettivamente eseguita. Il terzo +flag fa invalidare le pagine di cui si richiede la sincronizzazione per tutte +le mappature dello stesso file, così che esse possano essere immediatamente +aggiornate ai nuovi valori. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \macro{MS\_ASYNC} & Richiede la sincronizzazione.\\ + \macro{MS\_SYNC} & Attende che la sincronizzazione si eseguita.\\ + \macro{MS\_INVALIDATE}& Richiede che le altre mappature dello stesso file + siano invalidate.\\ + \hline + \end{tabular} + \caption{Valori dell'argomento \param{flag} di \func{msync}.} + \label{tab:file_mmap_rsync} +\end{table} + Una volta completate le operazioni di I/O si può eliminare la mappatura della memoria usando la funzione \func{munmap}, il cui prototipo è: \begin{functions} @@ -887,19 +1101,19 @@ memoria usando la funzione \func{munmap}, il cui prototipo \funcdecl{int munmap(void *start, size\_t length)} - Esegue la mappatura in memoria del file \param{fd}. + Rilascia la mappatura sulla sezione di memoria specificata. \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di errore nel qual caso \var{errno} viene settata ai valori: \begin{errlist} - \item[\macro{EBADF}] Il file descriptor non è valido, e non si è usato - \macro{MAP\_ANONYMOUS}. - \item[\macro{EACCES}] \macro{MAP\_PRIVATE}. - \item[\macro{EINTR}] La funzione è stata interrotta da un segnale. + \item[\macro{EINVAL}] Il range specificato non ricade in una zona + precedentemente mappata. \end{errlist} - ed inoltre \macro{ENOMEM}.} + } \end{functions} +La funzione + \section{Il file locking} \label{sec:file_locking} diff --git a/fileintro.tex b/fileintro.tex index 14e4f27..0a7bb94 100644 --- a/fileintro.tex +++ b/fileintro.tex @@ -448,26 +448,29 @@ operazioni previste dal kernel \begin{table}[htb] \centering \footnotesize - \begin{tabular}[c]{|l|p{7cm}|} + \begin{tabular}[c]{|l|p{8cm}|} \hline \textbf{Funzione} & \textbf{Operazione} \\ \hline \hline - \textsl{\code{open}} & apre il file \\ - \textsl{\code{read}} & legge dal file \\ - \textsl{\code{write}} & scrive sul file \\ - \textsl{\code{llseek}} & sposta la posizione corrente sul file \\ + \textsl{\code{open}} & apre il file (vedi \secref{sec:file_open}). \\ + \textsl{\code{read}} & legge dal file (vedi \secref{sec:file_read}).\\ + \textsl{\code{write}} & scrive sul file (vedi \secref{sec:file_write}).\\ + \textsl{\code{llseek}} & sposta la posizione corrente sul file (vedi + \secref{sec:file_lseek}). \\ \textsl{\code{ioctl}} & accede alle operazioni di controllo - (tramite la \func{ioctl})\\ - \textsl{\code{readdir}}& per leggere il contenuto di una directory \\ - \textsl{\code{poll}} & \\ - \textsl{\code{mmap}} & chiamata dalla system call \func{mmap}. - mappa il file in memoria\\ + (vedi \secref{sec:file_ioctl}).\\ + \textsl{\code{readdir}}& legge il contenuto di una directory \\ + \textsl{\code{poll}} & usata nell'I/O multiplexing (vedi + \secref{sec:file_multiplexing}). \\ + \textsl{\code{mmap}} & mappa il file in memoria (vedi + \secref{sec:file_memory_map}). \\ \textsl{\code{release}}& chiamata quando l'ultima referenza a un file - aperto è chiusa\\ - \textsl{\code{fsync}} & chiamata dalla system call \func{fsync} \\ - \textsl{\code{fasync}} & chiamate da \func{fcntl} quando è abilitato - il modo asincrono per l'I/O su file. \\ + aperto è chiusa. \\ + \textsl{\code{fsync}} & sincronizza il contenuto del file (vedi + \secref{sec:file_sync}). \\ + \textsl{\code{fasync}} & abilita l'I/O asincrono (vedi + \secref{sec:file_asyncronous_io}) sul file. \\ \hline \end{tabular} \caption{Operazioni sui file definite nel VFS.} diff --git a/fileunix.tex b/fileunix.tex index 8f49c5e..4ea7eb9 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -252,7 +252,8 @@ sempre il file descriptor con il valore pi opzione è ignorata. \\ \macro{O\_DIRECTORY} & se \var{pathname} non è una directory la chiamata fallisce. Questo flag è specifico di Linux ed è stato introdotto con il - kernel 2.1.126 per evitare dei DoS\protect\footnotemark\ quando + kernel 2.1.126 per evitare dei + \textit{DoS}\index{DoS}\protect\footnotemark\ quando \func{opendir} viene chiamata su una fifo o su un device di unità a nastri, non deve essere utilizzato al di fuori dell'implementazione di \func{opendir}. \\ -- 2.30.2