\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}
\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
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}
\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}
\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}
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 è:
+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.
\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 è
+il predente indirizzo. Per questo motivo la funzione può restituire un
+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.
+sulla prima pagina di memoria e così via). 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 la funzione
+\func{mmap} per ciascuna delle diverse aree,\footnote{ed in effetti è quello
+ che veniva fatto prima che fossero introdotte queste estensioni.} ma questo
+approccio ha delle conseguenze molto pesanti in termini di prestazioni. Si
+ricordi infatti che il \textit{memory mapping} funziona facendo ricorso al
+meccanismo della \index{memoria~virtuale} memoria virtuale per trascrivere su
+un file le operazioni effettuate sugli indirizzi di memoria sui cui esso è
+mappato.
+
+Questo vuol dire che per ciascuna mappatura in memoria deve essere definita
+nella \itindex{page~table} \textit{page table} del processo una nuova area
+della memoria virtuale\footnote{quella che nel gergo del kernel viene chiamata
+ una \textit{virtual memory area} o VMA.} 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 vale per ciascun processo che utilizza questa
+tecnica. In situazioni come questa si possono ottenere peggioramenti notevoli
+delle prestazioni, 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 queste mappature.
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}.
+Molnar un meccanismo che consente la mappatura non lineare, che è una seconda
+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. Questo viene realizzato tramite 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}] Uno dei puntatori non è validi, in genere si è
+ usato un valore di \param{old\_address} non allineato ad una pagina di
+ memoria.
+ \end{errlist}
+ }
+\end{functions}
+
+
+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}
\begin{figure}[htb]
\centering
- \includegraphics[width=14cm]{img/proc_beginend}
+ \includegraphics[width=9cm]{img/proc_beginend}
\caption{Schema dell'avvio e della conclusione di un programma.}
\label{fig:proc_prog_start_stop}
\end{figure}
Si ricordi infine che un programma può anche essere interrotto dall'esterno
attraverso l'uso di un segnale (modalità di conclusione non mostrata in
-fig.~\ref{fig:proc_prog_start_stop}); torneremo su questo aspetto in
-cap.~\ref{cha:signals}.
+fig.~\ref{fig:proc_prog_start_stop}); tratteremo nei dettagli i segnali e la
+loro gestione nel capitolo \ref{cha:signals}.
\subsection{I concetti generali}
\label{sec:proc_mem_gen}
-Ci sono vari modi in cui i vari sistemi organizzano la memoria (ed i dettagli
-di basso livello dipendono spesso in maniera diretta dall'architettura
-dell'hardware), ma quello più tipico, usato dai sistemi unix-like come Linux è
-la cosiddetta \textsl{memoria virtuale}\index{memoria~virtuale} che consiste
-nell'assegnare ad ogni processo uno spazio virtuale di indirizzamento lineare,
-in cui gli indirizzi vanno da zero ad un qualche valore massimo.\footnote{nel
- caso di Linux fino al kernel 2.2 detto massimo era, per macchine a 32bit, di
- 2Gb. Con il kernel 2.4 ed il supporto per la \textit{high-memory} il limite
- è stato esteso anche per macchine a 32 bit.}
+Ci sono vari modi in cui i sistemi operativi organizzano la memoria, ed i
+dettagli di basso livello dipendono spesso in maniera diretta
+dall'architettura dell'hardware, ma quello più tipico, usato dai sistemi
+unix-like come Linux è la cosiddetta \textsl{memoria
+ virtuale}\index{memoria~virtuale} che consiste nell'assegnare ad ogni
+processo uno spazio virtuale di indirizzamento lineare, in cui gli indirizzi
+vanno da zero ad un qualche valore massimo.\footnote{nel caso di Linux fino al
+ kernel 2.2 detto massimo era, per macchine a 32bit, di 2Gb. Con il kernel
+ 2.4 ed il supporto per la \textit{high-memory} il limite è stato esteso
+ anche per macchine a 32 bit.}
Come accennato in cap.~\ref{cha:intro_unix} questo spazio di indirizzi è
virtuale e non corrisponde all'effettiva posizione dei dati nella RAM del
indirizzi possibili sono utilizzabili, e quelli usabili non sono
necessariamente adiacenti).
-Per la gestione da parte del kernel la memoria virtuale viene divisa in pagine
-di dimensione fissa (che ad esempio sono di 4kb su macchine a 32 bit e 8kb
-sulle alpha, valori strettamente connessi all'hardware di gestione della
-memoria),\footnote{con le versioni più recenti del kernel è possibile anche
- utilizzare pagine di dimensioni maggiori, per sistemi con grandi
- quantitativi di memoria in cui l'uso di pagine troppo piccole comporta una
- perdita di prestazioni.} e ciascuna pagina della memoria virtuale è
-associata ad un supporto che può essere una pagina di memoria reale o ad un
-dispositivo di stoccaggio secondario (in genere lo spazio disco riservato alla
-swap, o i file che contengono il codice).
-
-Lo stesso pezzo di memoria reale (o di spazio disco) può fare da supporto a
-diverse pagine di memoria virtuale appartenenti a processi diversi (come
-accade in genere per le pagine che contengono il codice delle librerie
-condivise). Ad esempio il codice della funzione \func{printf} starà su una
-sola pagina di memoria reale che farà da supporto a tutte le pagine di memoria
-virtuale di tutti i processi che hanno detta funzione nel loro codice.
-
-La corrispondenza fra le pagine della \index{memoria~virtuale}memoria virtuale
-e quelle della memoria fisica della macchina viene gestita in maniera
-trasparente dal kernel con l'ausilio dell'hardware di gestione della memoria
-(la \textit{Memory Management Unit} del processore). Poiché in genere la
-memoria fisica è solo una piccola frazione della memoria virtuale, è
-necessario un meccanismo che permetta di trasferire le pagine che servono dal
-supporto su cui si trovano in memoria, eliminando quelle che non servono.
-Questo meccanismo è detto \textsl{paginazione}\index{paginazione} (o
-\textit{paging}), ed è uno dei compiti principali del kernel.
+Per la gestione da parte del kernel la memoria viene divisa in pagine di
+dimensione fissa,\footnote{inizialmente questi erano di 4kb sulle macchine a
+ 32 bit e di 8kb sulle alpha, con le versioni più recenti del kernel è
+ possibile anche utilizzare pagine di dimensioni maggiori (4Mb), per sistemi
+ con grandi quantitativi di memoria in cui l'uso di pagine troppo piccole
+ comporta una perdita di prestazioni.} e ciascuna pagina nello spazio di
+indirizzi virtuale è associata ad un supporto che può essere una pagina di
+memoria reale o ad un dispositivo di stoccaggio secondario (come lo spazio
+disco riservato alla swap, o i file che contengono il codice). Per ciasun
+processo il kernel si cura di mantenere un mappa di queste corrispondenze
+nella cosiddetta \itindex{page~table}\textit{page table}.\footnote{questa è
+ una semplificazione brutale, il meccanismo è molto più complesso; una buona
+ trattazione di come Linux gestisce la memoria virtuale si trova su
+ \cite{LinVM}.}
+
+Una stessa pagina di memoria reale può fare da supporto a diverse pagine di
+memoria virtuale appartenenti a processi diversi (come accade in genere per le
+pagine che contengono il codice delle librerie condivise). Ad esempio il
+codice della funzione \func{printf} starà su una sola pagina di memoria reale
+che farà da supporto a tutte le pagine di memoria virtuale di tutti i processi
+che hanno detta funzione nel loro codice.
+
+La corrispondenza fra le pagine della \index{memoria~virtuale} memoria
+virtuale di un processo e quelle della memoria fisica della macchina viene
+gestita in maniera trasparente dal kernel.\footnote{in genere con l'ausilio
+ dell'hardware di gestione della memoria (la \textit{Memory Management Unit}
+ del processore), con i kernel della serie 2.6 è comunque diventato possibile
+ utilizzare Linux anche su architetture che non dispongono di una MMU.}
+Poiché in genere la memoria fisica è solo una piccola frazione della memoria
+virtuale, è necessario un meccanismo che permetta di trasferire le pagine che
+servono dal supporto su cui si trovano in memoria, eliminando quelle che non
+servono. Questo meccanismo è detto \textsl{paginazione} \index{paginazione}
+(o \textit{paging}), ed è uno dei compiti principali del kernel.
Quando un processo cerca di accedere ad una pagina che non è nella memoria
-reale, avviene quello che viene chiamato un \textit{page
- fault}\itindex{page~fault}; la gestione della memoria genera un'interruzione
-e passa il controllo al kernel il quale sospende il processo e si incarica di
-mettere in RAM la pagina richiesta (effettuando tutte le operazioni necessarie
-per reperire lo spazio necessario), per poi restituire il controllo al
-processo.
+reale, avviene quello che viene chiamato un \itindex{page~fault} \textit{page
+ fault}; la gestione della memoria genera un'interruzione e passa il
+controllo al kernel il quale sospende il processo e si incarica di mettere in
+RAM la pagina richiesta (effettuando tutte le operazioni necessarie per
+reperire lo spazio necessario), per poi restituire il controllo al processo.
Dal punto di vista di un processo questo meccanismo è completamente
trasparente, e tutto avviene come se tutte le pagine fossero sempre
in genere il sistema è molto efficiente in questo lavoro; quando però ci siano
esigenze specifiche di prestazioni è possibile usare delle funzioni che
permettono di bloccare il meccanismo della paginazione\index{paginazione} e
-mantenere fisse delle pagine in memoria (vedi sez.~\ref{sec:proc_mem_lock}).
+mantenere fisse delle pagine in memoria (vedi
+sez.~\ref{sec:proc_mem_lock}). Inoltre per certe applicazioni gli algoritmi di
+gestione della memoria
\subsection{La struttura della memoria di un processo}
commette quando si è manipolato male un puntatore e genera quello che viene
chiamato un \textit{segmentation fault}. Se si tenta cioè di leggere o
scrivere da un indirizzo per il quale non esiste un'associazione della pagina
-virtuale, il kernel risponde al relativo \textit{page
- fault}\itindex{page~fault} mandando un segnale \const{SIGSEGV} al processo,
-che normalmente ne causa la terminazione immediata.
+virtuale, il kernel risponde al relativo \itindex{page~fault} \textit{page
+ fault} mandando un segnale \const{SIGSEGV} al processo, che normalmente ne
+causa la terminazione immediata.
È pertanto importante capire come viene strutturata \textsl{la memoria
- virtuale}\index{memoria~virtuale} di un processo. Essa viene divisa in
+ virtuale} \index{memoria~virtuale} di un processo. Essa viene divisa in
\textsl{segmenti}, cioè un insieme contiguo di indirizzi virtuali ai quali il
processo può accedere. Solitamente un programma C viene suddiviso nei
seguenti segmenti:
\subsection{Allocazione della memoria per i programmi C}
\label{sec:proc_mem_alloc}
-Il C supporta, a livello di linguaggio, soltanto due modalità di allocazione
-della memoria: l'\textsl{allocazione statica} e l'\textsl{allocazione
- automatica}.
+Il C supporta direttamente, come linguaggio di programmazione, soltanto due
+modalità di allocazione della memoria: l'\textsl{allocazione statica} e
+l'\textsl{allocazione automatica}.
L'\textsl{allocazione statica} è quella con cui sono memorizzate le variabili
globali e le variabili statiche, cioè le variabili il cui valore deve essere
cioè definire in fase di programmazione una variabile le cui dimensioni
possano essere modificate durante l'esecuzione del programma. Per questo le
librerie del C forniscono una serie opportuna di funzioni per eseguire
-l'allocazione dinamica di memoria (in genere nello heap). Le variabili il
-cui contenuto è allocato in questo modo non potranno essere usate direttamente
-come le altre, ma l'accesso sarà possibile solo in maniera indiretta,
-attraverso dei puntatori.
+l'allocazione dinamica di memoria (in genere nello heap).
+
+Le variabili il cui contenuto è allocato in questo modo non potranno essere
+usate direttamente come le altre, ma l'accesso sarà possibile solo in maniera
+indiretta, attraverso i puntatori alla memoria loro riservata che si sono
+ottenuti dalle funzioni di allocazione.
\subsection{Le funzioni \func{malloc}, \func{calloc}, \func{realloc} e