X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;f=fileadv.tex;h=f7fba16f329e1116e2f3d44123f7d51635667b7b;hb=1ee77ce91c941b6be83ee7ae20f2a3e3bf032e1a;hp=6a94f22492fa08ef6ca8047c9e49681ca23d269d;hpb=34c8ae732b8ffe5f4a87cdd0eba3a02df4c76d55;p=gapil.git diff --git a/fileadv.tex b/fileadv.tex index 6a94f22..f7fba16 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -998,7 +998,7 @@ si pu \begin{figure}[htb] \centering - \includegraphics[width=13cm]{img/mmap_layout} + \includegraphics[width=14cm]{img/mmap_layout} \caption{Disposizione della memoria di un processo quando si esegue la mappatura in memoria di un file.} \label{fig:file_mmap_layout} @@ -1038,7 +1038,7 @@ eseguire la mappatura in memoria di un file, \funcdecl{void * mmap(void * start, size\_t length, int prot, int flags, int fd, off\_t offset)} - Esegue la mappatura in memoria del file \param{fd}. + Esegue la mappatura in memoria della sezione specificata del file \param{fd}. \bodydesc{La funzione restituisce il puntatore alla zona di memoria mappata in caso di successo, e \const{MAP\_FAILED} (-1) in caso di errore, nel @@ -1056,11 +1056,18 @@ eseguire la mappatura in memoria di un file, dimensione delle pagine). \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 numero di mappature possibili. \item[\errcode{ENODEV}] Il filesystem di \param{fd} non supporta il memory mapping. + \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 + file aperti (vedi sez.~\ref{sec:sys_resource_limit}). \end{errlist} } \end{functions} @@ -1213,7 +1220,7 @@ effettive del file o della sezione che si vuole mappare. \begin{figure}[!htb] \centering - \includegraphics[width=10cm]{img/mmap_boundary} + \includegraphics[width=12cm]{img/mmap_boundary} \caption{Schema della mappatura in memoria di una sezione di file di dimensioni non corrispondenti al bordo di una pagina.} \label{fig:file_mmap_boundary} @@ -1256,7 +1263,7 @@ che sono utilizzabili solo con questa interfaccia. \begin{figure}[htb] \centering - \includegraphics[width=10cm]{img/mmap_exceed} + \includegraphics[width=12cm]{img/mmap_exceed} \caption{Schema della mappatura in memoria di file di dimensioni inferiori alla lunghezza richiesta.} \label{fig:file_mmap_exceed} @@ -1376,27 +1383,66 @@ mappatura della memoria usando la funzione \funcd{munmap}, il suo prototipo } \end{functions} -La funzione cancella la mappatura per l'intervallo specificato attraverso -\param{start} e \param{length}, ed ogni successivo accesso a tale regione -causerà un errore di accesso in memoria. L'argomento \param{start} deve essere -allineato alle dimensioni di una pagina di memoria, e la mappatura di tutte le -pagine contenute (anche parzialmente) nell'intervallo indicato, verrà rimossa. -Indicare un intervallo che non contiene pagine mappate non è un errore. +La funzione cancella la mappatura per l'intervallo specificato con +\param{start} e \param{length}; ogni successivo accesso a tale regione causerà +un errore di accesso in memoria. L'argomento \param{start} deve essere +allineato alle dimensioni di una pagina, e la mappatura di tutte le pagine +contenute anche parzialmente nell'intervallo indicato, verrà rimossa. +Indicare un intervallo che non contiene mappature non è un errore. Si tenga +presente inoltre che alla conclusione di un processo ogni pagina mappata verrà +automaticamente rilasciata, mentre la chiusura del file descriptor usato per +il \textit{memory mapping} non ha alcun effetto su di esso. + +Lo standard POSIX prevede anche una funzione che permetta di cambiare le +protezioni delle pagine di memoria; lo standard prevede che essa si applichi +solo ai \textit{memory mapping} creati con \func{mmap}, ma nel caso di Linux +la funzione può essere usata con qualunque pagina valida nella memoria +virtuale. Questa funzione è \funcd{mprotect} ed il suo prototipo è: +\begin{functions} +% \headdecl{unistd.h} + \headdecl{sys/mman.h} + + \funcdecl{int mprotect(const void *addr, size\_t len, int prot)} + + Modifica le protezioni delle pagine di memoria comprese nell'intervallo + specificato. + + \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}] il valore di \param{addr} non è valido o non è un + multiplo di \const{PAGE\_SIZE}. + \item[\errcode{EACCESS}] l'operazione non è consentita, ad esempio si è + cercato di marcare con \const{PROT\_WRITE} un segmento di memoria cui si + ha solo accesso in lettura. +% \item[\errcode{ENOMEM}] non è stato possibile allocare le risorse +% necessarie all'interno del kernel. +% \item[\errcode{EFAULT}] si è specificato un indirizzo di memoria non +% accessibile. + \end{errlist} + ed inoltre \errval{ENOMEM} ed \errval{EFAULT}. + } +\end{functions} + + +La funzione prende come argomenti un indirizzo di partenza in \param{addr}, +allineato alle dimensioni delle pagine di memoria, ed una dimensione +\param{size}. La nuova protezione deve essere specificata in \param{prot} con +una combinazione dei valori di tab.~\ref{tab:file_mmap_prot}. La nuova +protezione verrà applicata a tutte le pagine contenute, anche parzialmente, +dall'intervallo fra \param{addr} e \param{addr}+\param{size}-1. -Alla conclusione del processo, ogni pagina mappata verrà automaticamente -rilasciata, mentre la chiusura del file descriptor usato per effettuare la -mappatura in memoria non ha alcun effetto sulla stessa. Infine Linux supporta alcune operazioni specifiche non disponibili su altri -kernel unix-like. La prima di queste operazioni è la possibilità di modificare -una mappatura precedente, ad esempio per espanderla o restringerla. Questo è -realizzato dalla funzione \funcd{mremap}, il cui prototipo è: +kernel unix-like. La prima di queste è la possibilità di modificare un +precedente \textit{memory mapping}, ad esempio per espanderlo o restringerlo. +Questo è realizzato dalla funzione \funcd{mremap}, il cui prototipo è: \begin{functions} \headdecl{unistd.h} \headdecl{sys/mman.h} \funcdecl{void * mremap(void *old\_address, size\_t old\_size , size\_t - new\_size, unsigned long flags);} + new\_size, unsigned long flags)} Restringe o allarga una mappatura in memoria di un file. @@ -1405,62 +1451,122 @@ realizzato dalla funzione \funcd{mremap}, il cui prototipo -1}) in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EINVAL}] Uno dei puntatori non è validi, in genere si è - usato un valore di \param{old\_address} non allineato ad una pagina di - memoria. - \item[\errcode{EFAULT}] Ci sono degli indirizzi non validi nell'intervallo - di memoria specificato dagli argomenti \param{old\_address} e - \param{old\_size}, o ci sono altre mappatura di un tipo non - corrispondente a quella richiesta. - \item[\errcode{ENOMEM}] Non c'è memoria sufficiente oppure l'area di + \item[\errcode{EINVAL}] il valore di \param{old\_address} non è un + puntatore valido. + \item[\errcode{EFAULT}] ci sono indirizzi non validi nell'intervallo + specificato da \param{old\_address} e \param{old\_size}, o ci sono altre + mappature di tipo non corrispondente a quella richiesta. + \item[\errcode{ENOMEM}] non c'è memoria sufficiente oppure l'area di memoria non può essere espansa all'indirizzo virtuale corrente, e non si è specificato \const{MREMAP\_MAYMOVE} nei flag. - \item[\errcode{EAGAIN}] Il segmento di memoria scelto è bloccato e non può + \item[\errcode{EAGAIN}] il segmento di memoria scelto è bloccato e non può essere rimappato. \end{errlist} } \end{functions} -La funzione richiede come argomenti \param{old\_address} che specifica il -precedente indirizzo per la mappatura in memoria e \param{old\_size}, che ne -indica la dimensione, con \param{new\_size} si specifica invece la nuova -dimensione che si vuole ottenere. Questa funzione usa il meccanismo della -\index{memoria~virtuale}memoria virtuale per modificare l'associazione fra gli -indirizzi virtuali del processo e le pagine di memoria, modificando una -precedente mappatura. - -Infine l'argomento \param{flags} è una maschera binaria per i flag che -controllano il comportamento della funzione. Il solo valore utilizzato è -\const{MREMAP\_MAYMOVE}\footnote{per poter utilizzare questa costante occorre - aver definito \macro{\_GNU\_SOURCE} prima di includere \file{sys/mman.h}.} -che consente di eseguire l'espansione anche quando non è possibile utilizzare -il predente indirizzo. Per questo motivo la funzione restituisce sempre -l'indirizzo della nuova zona di memoria, che, se si è usato questo flag, non è -detto coincida con \param{old\_address}. - -La mappatura in memoria di un file viene normalmente eseguita in maniera -lineare, cioè parti successive di un file vengono mappate linearmente su -indirizzi successivi in memoria (la prima pagina di un file viene mappata -sulla prima pagina di memoria). Esistono però delle applicazioni\footnote{in - particolare la tecnica è usata dai database o dai programmi che realizzano - macchine virtuali.} in cui è utile poter mappare parti diverse di un file su -diverse zone di memoria. - -Questo è ovviamente sempre possibile eseguendo più volte \func{mmap} per -ciascuna delle diverse aree, ma questo approccio ha delle conseguenze molto -pesanti in termini di prestazioni. Si ricordi infatti che il \textit{memory - mapping} in memoria funziona facendo ricorso al meccaniso della -\index{memoria~virtuale} memoria virtuale per trascrivere su un file le -operazioni effettuate sugli indirizzi di memoria sui cui esso è mappato. - -Per questo motivo con il kernel 2.5.46 è stato introdotto ad opera di Ingo -Molnar un meccanismo che consente la mappatura non lineare, ed i due nuovi -flag \const{MAP\_POPULATE} e \const{MAP\_NONBLOCK} per \func{mmap}. +La funzione richiede come argomenti \param{old\_address} (che deve essere +allineato alle dimensioni di una pagina di memoria) che specifica il +precedente indirizzo del \textit{memory mapping} e \param{old\_size}, che ne +indica la dimensione. Con \param{new\_size} si specifica invece la nuova +dimensione che si vuole ottenere. Infine l'argomento \param{flags} è una +maschera binaria per i flag che controllano il comportamento della funzione. +Il solo valore utilizzato è \const{MREMAP\_MAYMOVE}\footnote{per poter + utilizzare questa costante occorre aver definito \macro{\_GNU\_SOURCE} prima + di includere \file{sys/mman.h}.} che consente di eseguire l'espansione +anche quando non è possibile utilizzare il precedente indirizzo. Per questo +motivo, se si è usato questo flag, la funzione può restituire un indirizzo +della nuova zona di memoria che non è detto coincida con \param{old\_address}. + +La funzione si appoggia al sistema della \index{memoria~virtuale} memoria +virtuale per modificare l'associazione fra gli indirizzi virtuali del processo +e le pagine di memoria, modificando i dati direttamente nella +\itindex{page~table} \textit{page table} del processo. Come per +\func{mprotect} la funzione può essere usata in generale, anche per pagine di +memoria non corrispondenti ad un \textit{memory mapping}, e consente così di +implementare la funzione \func{realloc} in maniera molto efficiente. + +Una caratteristica comune a tutti i sistemi unix-like è che la mappatura in +memoria di un file viene eseguita in maniera lineare, cioè parti successive di +un file vengono mappate linearmente su indirizzi successivi in memoria. +Esistono però delle applicazioni\footnote{in particolare la tecnica è usata + dai database o dai programmi che realizzano macchine virtuali.} in cui è +utile poter mappare sezioni diverse di un file su diverse zone di memoria. + +Questo è ovviamente sempre possibile eseguendo la funzione \func{mmap} per +ciascuna delle diverse aree,\footnote{ed in effetti è quello che veniva fatto + anche con Linux prima che fossero introdotte queste estensioni.} ma questo +approccio ha delle conseguenze molto pesanti in termini di prestazioni. +Infatti per ciascuna mappatura in memoria deve essere definita nella +\itindex{page~table} \textit{page table} del processo una nuova area di +memoria virtuale\footnote{quella che nel gergo del kernel viene chiamata VMA + (\textit{virtual memory area}).} che corrisponda alla mappatura, in modo che +questa diventi visibile nello spazio degli indirizzi come illustrato in +fig.~\ref{fig:file_mmap_layout}. + +Quando un processo esegue un gran numero di mappature diverse\footnote{si può + arrivare anche a centinaia di migliaia.} per realizzare a mano una mappatura +non-linare si avrà un accrescimento eccessivo della sua \itindex{page~table} +\textit{page table}, e lo stesso accadrà per tutti gli altri processi che +utilizzano questa tecnica. In situazioni in cui le applicazioni hanno queste +esigenze si avranno delle prestazioni ridotte, dato che il kernel dovrà +impiegare molte risorse\footnote{sia in termini di memoria interna per i dati + delle \itindex{page~table} \textit{page table}, che di CPU per il loro + aggiornamento.} solo per mantenere i dati di una gran quantità di +\textit{memory mapping}. + +Per questo motivo con il kernel 2.5.46 è stato introdotto, ad opera di Ingo +Molnar, un meccanismo che consente la mappatura non-lineare. Anche questa è +una caratteristica specifica di Linux, non presente in altri sistemi +unix-like. Diventa così possibile utilizzare una sola mappatura +iniziale\footnote{e quindi una sola \textit{virtual memory area} nella + \itindex{page~table} \textit{page table} del processo.} e poi rimappare a +piacere all'interno di questa i dati del file. Ciò è possibile grazie ad una +nuova system call, \funcd{remap\_file\_pages}, il cui prototipo è: +\begin{functions} + \headdecl{sys/mman.h} + + \funcdecl{int remap\_file\_pages(void *start, size\_t size, int prot, + ssize\_t pgoff, int flags)} + + Permette di rimappare non linearmente un precedente \textit{memory mapping}. + + \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 + argomenti o \param{start} non fa riferimento ad un \textit{memory + mapping} valido creato con \const{MAP\_SHARED}. + \end{errlist} + } +\end{functions} + +Per poter utilizzare questa funzione occorre anzitutto effettuare +preliminarmente una chiamata a \func{mmap} con \const{MAP\_SHARED} per +definire l'area di memoria che poi sarà rimappata non linearmente. Poi di +chiamerà questa funzione per modificare le corrispondenze fra pagine di +memoria e pagine del file; si tenga presente che \func{remap\_file\_pages} +permette anche di mappare la stessa pagina di un file in più pagine della +regione mappata. + +La funzione richiede che si identifichi la pagine di file che si vuole +rimappare con \param{pgoff} e \param{size}, che ne indicano l'inizio (in unità +della dimensione delle pagine di memoria) \param{start} + +Insiema al meccanismo per la mappatura non-lineare con sono stati introdotti +anche due nuovi flag per \func{mmap}: \const{MAP\_POPULATE} e +\const{MAP\_NONBLOCK}. \itindend{memory~mapping} +% i raw device +%\subsection{I \textit{raw} device} +%\label{sec:file_raw_device} + + + \section{Il file locking} \label{sec:file_locking}