From f4469c536e047bd645eb84db1d5bed531ffde3b5 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Mon, 5 Nov 2001 18:52:06 +0000 Subject: [PATCH] Sistemato il memory locking --- process.tex | 187 +++++++++++++++++++++++++++++++++------------------ prochand.tex | 26 +++---- 2 files changed, 133 insertions(+), 80 deletions(-) diff --git a/process.tex b/process.tex index a096d6a..e022c2e 100644 --- a/process.tex +++ b/process.tex @@ -6,7 +6,7 @@ sistema unix alloca ed utilizza le risorse. Questo capitolo tratter l'interfaccia base fra il sistema e i processi, su come vengono passati i parametri, come viene gestita e allocata la memoria, su come un processo può richiedere servizi al sistema, su cosa deve fare quando ha finito la sua -esecuzione. Nella sezione finale acceneremo ad alcune problematiche generiche +esecuzione. Nella sezione finale accenneremo ad alcune problematiche generiche di programmazione. In genere un programma viene eseguito quando un processo lo fa partire @@ -460,7 +460,7 @@ quattro, i prototipi sono i seguenti: \funcdecl{void free(void *ptr)} Disalloca lo spazio di memoria puntato da \var{ptr}. - La funzione non ritorna nulla. + La funzione non ritorna nulla e non riporta errori. \end{functions} Il puntatore che le funzioni di allocazione ritornano è garantito essere sempre correttamente allineato per tutti i tipi di dati; ad esempio sulle @@ -494,14 +494,16 @@ comporta come \func{malloc}\footnote{questo sotto Linux}), ad esempio quando si deve far crescere la dimensione di un vettore; in questo caso se è disponibile dello spazio adiacente al precedente la funzione lo utilizza, altrimenti rialloca altrove un blocco della dimensione -voluta copiandoci automaticamente il contenuto, lo spazio in più non viene +voluta copiandoci automaticamente il contenuto, lo spazio aggiunto non viene inizializzato. -Il fatto che il blocco di memoria restituito da \func{realloc} possa -cambiare comporta che si deve sempre riassegnare al puntatore passato per il -ridimensionamento il valore di ritorno della funzione, e che non ci devono -essere altri puntatori che puntino all'interno di un'area che si vuole -ridimensionare. +Si deve sempre avere ben presente il fatto che il blocco di memoria restituito +da \func{realloc} può non essere una estensione di quello che gli si è passato +come parametro; pertanto esso deve essere trattato allo stesso modo di una +nuova allocazione; in particolare si dovrà \emph{sempre} eseguire la +riassegnazione di \var{ptr} al valore di ritorno della funzione, e +reinizializzare (o provvedere ad un adeguato aggiornamento qualora ancora +servano) tutti gli altri puntatori al blocco di dati ridimensionato. Uno degli errori più comuni (specie se si ha a che fare con array di puntatori) è infatti quello di chiamare \func{free} più di una volta sullo @@ -545,20 +547,20 @@ permettono di sostituire alle funzioni di libreria una propria versione (che può essere più o meno specializzata per il debugging). -\subsection{La funzione \texttt{alloca}} +\subsection{La funzione \func{alloca}} \label{sec:proc_mem_alloca} -Una alternativa possibile all'uso di \texttt{malloc}, che non soffre del tipo +Una alternativa possibile all'uso di \func{malloc}, che non soffre del tipo di problemi di memory leak descritti in precedenza è la funzione -\texttt{alloca} che invece che allocare la memoria nello heap usa lo il +\func{alloca} che invece che allocare la memoria nello heap usa lo il segmento di stack della funzione corrente. La sintassi è identica: \begin{prototype}{stdlib.h}{void *alloca(size\_t size)} - Alloca \texttt{size} byte nel segmento di stack della funzione chiamante. + Alloca \var{size} byte nel segmento di stack della funzione chiamante. La memoria non viene inizializzata. La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \texttt{NULL} in caso di fallimento, nel qual caso - \texttt{errno} viene settata a \texttt{ENOMEM}. + di successo e \macro{NULL} in caso di fallimento, nel qual caso + \var{errno} viene settata a \macro{ENOMEM}. \end{prototype} ma in questo caso non è più necessario liberare la memoria in quanto questa viene rilasciata automaticamente al ritorno della funzione. @@ -571,33 +573,32 @@ usa \func{longjump} per uscire con un salto non locale da una funzione (vedi Un altro vantaggio e che in Linux la funzione è molto veloce e non viene sprecato spazio, infatti non è necessario gestire un pool di memoria da -riservare e si evitano anche problemi di frammentazione. +riservare e si evitano anche i problemi di frammentazione di quest'ultimo che +comportano inefficienze sia nell'allocazione della memoria che nell'esecuzione +della funzione. -Gli svantaggi sono che la funzione non è disponibile su tutti gli unix, quando -non è possibile aumentare le dimensioni dello stack una volta chiamata una -funzione e quindi l'uso limita la portabilità dei programmi, inoltre se si -cerca di allocare troppa memoria non si ottiene un messaggio di errore, ma un -segnale di \textit{segment violation} analogo a quello che si avrebbe da una -ricorsione infinita. +Gli svantaggi sono che questa funzione non è disponibile su tutti gli unix, +(quando non è possibile aumentare le dimensioni dello stack una volta chiamata +una funzione) e quindi l'uso limita la portabilità dei programmi, inoltre se +si cerca di allocare troppa memoria non si ottiene un messaggio di errore, ma +un segnale di \textit{segment violation} analogo a quello che si avrebbe da +una ricorsione infinita. Inoltre non è chiaramente possibile usare questa funzione per allocare memoria che deve poi essere usata anche al di fuori della funzione in cui questa viene chiamata, in quanto all'uscita dalla funzione lo spazio allocato diventerebbe libero, e potrebbe essere sovrascritto all'invocazione di nuove funzioni con -conseguenze imprevedibili. +conseguenze imprevedibili. Questo è lo stesso problema potenziale che si può +avere con le variabili automatiche, su cui torneremo in +\secref{sec:proc_auto_var}. -Questo è lo stesso problema potenziale che si può avere con le variabili -automatiche; un errore comune infatti è quello di restituire al chiamante un -puntatore ad una di queste variabili, che sarà automaticamente distrutta -all'uscita della funzione, con gli stessi problemi appena citati per -\func{alloca}. \subsection{Le funzioni \func{brk} e \func{sbrk}} \label{sec:proc_mem_sbrk} L'uso di queste funzioni è necessario solo quando si voglia accedere alle analoghe system call a cui fanno da interfaccia (ad esempio per implementare -una propria versione di \func{malloc}. Le funzione sono: +una propria versione di \func{malloc}. Le funzioni sono: \begin{prototype}{unistd.h}{int *brk(void end\_data\_segment)} Sposta la fine del segmento dei dati all'indirizzo specificato da \var{end\_data\_segment}. @@ -633,11 +634,11 @@ maniera trasparente ai processi, decidendo quando rimuovere pagine dalla memoria per metterle nello swap sulla base dell'utilizzo corrente da parte dei vari processi. -Nell'uso comune un processo non deve preoccuparsi di tutto ciò in quanto il +Nell'uso comune un processo non deve preoccuparsi di tutto ciò, in quanto il meccanismo della paginazione riporta in RAM, ed in maniera trasparente, tutte le pagine che gli occorrono; esistono però esigenze particolari in cui non si -vuole che il meccanismo dello \textit{swapping}, in generale i motivi per cui -si possono avere queste necessità sono sostanzialmente due: +vuole che si attivi il meccanismo dello \textit{swapping}, in generale i +motivi per cui si possono avere queste necessità sono sostanzialmente due: \begin{itemize} \item La velocità. Il processo della paginazione è trasparente solo se il programma in esecuzione se non è sensibile al tempo che occorre a riportare @@ -654,11 +655,12 @@ si possono avere queste necessit \item La sicurezza. Se si tengono password o chiavi in memoria queste possono essere portate su disco dal meccanismo della paginazione, questo rende più - lungo il periodo di tempo in cui i segreti sono presenti in chiaro, e - complessa la loro cancellazione (in genere è possibile cancellare della RAM - ma altrettanto non vale per il disco su cui la pagina contenente i segreti - può essere stata salvata). Per questo motivo programmi di crittografia - richiedono il blocco di alcune pagine di memoria. + lungo il periodo di tempo in cui i segreti sono presenti in chiaro e più + complessa la loro cancellazione (ad un processo è possibile cancellare la + memoria su cui scrive le sue variabili, ma non può toccare lo spazio disco + su cui la pagina contenente i segreti può essere stata salvata). Per questo + motivo di solito i programmi di crittografia richiedono il blocco di alcune + pagine di memoria. \end{itemize} Il meccanismo che previene la paginazione di parte della memoria virtuale di @@ -674,50 +676,101 @@ sbloccarla due volte, una pagina o Il \textit{memory lock} persiste fintanto che il processo che detiene la memoria bloccata non la sblocca. Chiaramente la terminazione del processo comporta anche la fine dell'uso della sua memoria virtuale, e quindi anche di -tutti i \textit{memory lock}. +tutti i suoi \textit{memory lock}. I \textit{memory lock} non sono ereditati dai processi figli\footnote{ma siccome Linux usa il copy on write gli indirizzi virtuali del figlio sono mantenuti sullo stesso segmento di RAM del padre, quindi fintanto che un figlio non scrive su un segmento, può usufruire dei memory lock del padre}. -Siccome la presenza di \textit{memory lock} ha un impatto sugli altri processi -solo l'amministratore ha la capacità di bloccare una pagina; ogni processo -però può sbloccare le sue pagine. Il sistema pone dei limiti all'ammontare di -memoria di un processo che può essere bloccata e al totale di memoria fisica -che può dedicare a questo. +Siccome la presenza di un \textit{memory lock} riduce la memoria disponibile +al sistema con un impatto su tutti gli altri processi, solo l'amministratore ha +la capacità di bloccare una pagina. Ogni processo però può sbloccare le sue +pagine. + + +Il sistema pone dei limiti all'ammontare di memoria di un processo che può +essere bloccata e al totale di memoria fisica che può dedicare a questo, lo +standard POSIX.1 richiede che sia definita in \file{unistd.h} la costante +\macro{\_POSIX\_MEMLOCK\_RANGE} per indicare la capacità di eseguire il +\textit{memory locking} e la costante \macro{PAGESIZE} in \file{limits.h} per +indicare la dimensione di una pagina in byte. + Le funzioni per bloccare e sbloccare singole sezioni di memoria sono \func{mlock} e \func{munlock}; i loro prototipi sono: - \begin{functions} -\headdecl{stdlib.h} -\funcdecl{void *calloc(size\_t size)} - Alloca \var{size} byte nello heap. La memoria viene inizializzata a 0. + \headdecl{sys/mman.h} + + \funcdecl{int mlock(const void *addr, size\_t len)} + Blocca la paginazione per l'intervallo di memoria da \var{addr} per + \var{len} byte. Tutte le pagine che contengono una parte dell'intervallo + sono mantenute in RAM per tutta la durata del blocco. + + La funzione ritorna 0 in caso di successo e -1 in caso di errore, nel qual + caso \var{errno} è settata ad uno dei valori seguenti: + \begin{errlist} + \item \macro{ENOMEM} alcuni indirizzi dell'intervallo specificato non + corripondono allo spazio di indirizzi del processo o si è ecceduto il + numero massimo consentito di pagine bloccate. + \item \macro{EPERM} il processo non ha i privilegi richiesti per + l'operazione. + \item \macro{EINVAL} \var{len} non è un valore positivo. + \end{errlist} - La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \macro{NULL} in caso di fallimento, nel qual caso - \var{errno} viene settata a \macro{ENOMEM}. -\funcdecl{void *malloc(size\_t size)} + \funcdecl{int munlock(const void *addr, size\_t len)} Alloca \var{size} byte nello heap. La memoria non viene inizializzata. - La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \macro{NULL} in caso di fallimento, nel qual caso - \var{errno} viene settata a \macro{ENOMEM}. -\funcdecl{void *realloc(void *ptr, size\_t size)} - Cambia la dimensione del blocco allocato all'indirizzo \var{ptr} - portandola a \var{size}. + Sblocca l'intervallo di memoria da \var{addr} per \var{len} byte. La + funzione ritorna 0 in caso di successo e -1 in caso di errore, nel qual caso + \var{errno} è settata ad uno dei valori seguenti: + \begin{errlist} + \item \macro{ENOMEM} alcuni indirizzi dell'intervallo specificato non + corripondono allo spazio di indirizzi del processo. + \item \macro{EINVAL} \var{len} non è un valore positivo. + \end{errlist} +\end{functions} - La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \macro{NULL} in caso di fallimento, nel qual caso - \var{errno} viene settata a \macro{ENOMEM}. -\funcdecl{void free(void *ptr)} - Disalloca lo spazio di memoria puntato da \var{ptr}. +Altre due funzioni, \func{mlockall} e \func{munlockall}, consentono di +bloccare genericamente lo spazio di indirizzi di un processo. I prototipi di +queste funzioni sono: - La funzione non ritorna nulla. -\end{functions} +\begin{functions} + \headdecl{sys/mman.h} + \funcdecl{int mlockall(int flags)} + Blocca la paginazione per lo spazio di indirizzi del processo corrente. + + Codici di ritorno ed errori sono gli stessi di \func{mlock}. + \funcdecl{int munlockall(void)} + Sblocca la paginazione per lo spazio di indirizzi del processo corrente. + + Codici di ritorno ed errori sono gli stessi di \func{munlock}. +\end{functions} +Il parametro \var{flags} di \func{mlockall} permette di controllarne il +comportamento; esso può essere specificato come l'OR aritmetico delle due +costanti: +\begin{description*} +\item[\macro{MCL\_CURRENT}] blocca tutte le pagine correntemente mappate nello + spazio di indirizzi del processo. +\item[\macro{MCL\_FUTURE}] blocca tutte le pagine che saranno mappate nello + spazio di indirizzi del processo. +\end{description*} + +Con \func{mlockall} si può bloccare tutte le pagine mappate nello spazio di +indirizzi del processo, sia che comprendano il segmento di testi, di dati, lo +stack e lo heap e pure le funzioni di libreria chiamate, i file mappati in +memoria, i dati del kernel mappati in user space, la memoria condivisa. L'uso +dei flag permette di selezionare con maggior finezza le pagine da bloccare, ad +esempio limitandosi a tutte le pagine allocate a partire da un certo momento. + +In ogni caso un processo real-time che deve entrare in una sezione critica +deve provvedere a riservare memoria sufficiente prima dell'ingresso, in genere +questo si fa chiamando una funzione che ha allocato una quantità sufficiente +ampia di variabili automatiche, in modo che esse vengano mappate in RAM dallo +stack e poi ci scrive sopra, per scongiurare in partenza un eventuale page +fault causato dal meccanismo di copy on write. \section{La gestione di parametri e opzioni} @@ -848,7 +901,7 @@ comando. Anzitutto si può notare che si è anzitutto (\texttt{\small 1}) disabilitata la stampa di messaggi di errore per opzioni non riconosciute, per poi passare al ciclo per la verifica delle opzioni (\texttt{\small 2-27}); per ciascuna delle -opioni possibili si è poi provveduto ad una opportuna azione, ad esempio per +opzioni possibili si è poi provveduto ad una opportuna azione, ad esempio per le tre opzioni che prevedono un parametro si è effettuata la decodifica del medesimo, il cui indirizzo è contenuto nella variabile \var{optarg}, avvalorando la relativa variabile (\texttt{\small 12-14}, \texttt{\small @@ -953,9 +1006,9 @@ entit Una delle caratteristiche standard del C è che le variabili vengono passate alle subroutine attraverso un meccanismo che viene chiamato \textit{by value} -(diverso ad esempio da quanto avviene con il Fortran, dove le variabli sono -passate, come suol dirsi, \textit{by reference}, o dal C++ dove la modalità del -passaggio può essere controllata con l'operatore \cmd{\&}). +(diverso ad esempio da quanto avviene con il Fortran, dove le variabili sono +passate, come suol dirsi, \textit{by reference}, o dal C++ dove la modalità +del passaggio può essere controllata con l'operatore \cmd{\&}). Il passaggio di una variabile \textit{by value} significa che in realtà quello che viene passato alla subroutine è una copia del valore attuale di quella diff --git a/prochand.tex b/prochand.tex index e6913ed..f81dcc9 100644 --- a/prochand.tex +++ b/prochand.tex @@ -17,7 +17,7 @@ problematiche generiche della programmazione in ambiente multitasking. \label{sec:proc_gen} Partiremo con una introduzione generale ai concetti che stanno alla base della -gestione dei processi in un sitema unix-like. Introdurremo in questa sezione +gestione dei processi in un sistema unix-like. Introdurremo in questa sezione l'architettura della gestione dei processi e le sue principali caratteristiche, e daremo una panoramica sull'uso delle principali funzioni per la gestione dei processi. @@ -219,7 +219,7 @@ Oltre al \acr{pid} e al \acr{ppid}, e a quelli usati per il controllo di sessione, ad ogni processo sono associati altri identificatori, usati per il controllo di accesso, che servono per determinare se il processo può o meno eseguire le operazioni richieste, a seconda dei privilegi e dell'identità di -chi lo ha posto in esecuzione; su questi torneremo in dettaglii più avanti in +chi lo ha posto in esecuzione; su questi torneremo in dettagli più avanti in \secref{sec:proc_perm}. @@ -1332,7 +1332,7 @@ il programma che si settati (il significato di questi bit è affrontato in dettaglio in \secref{sec:file_suid_sgid}). In questo caso essi saranno settati all'utente e al gruppo proprietari del file; questo consente, per programmi in cui ci sia -necessità, di dare a qualunquee utente normale privilegi o permessi di +necessità, di dare a qualunque utente normale privilegi o permessi di un'altro (o dell'amministratore). Come nel caso del \acr{pid} e del \acr{ppid} tutti questi identificatori @@ -1397,10 +1397,10 @@ di utente e gruppo associati dal kernel ad ogni processo, \subsection{Le funzioni \func{setuid} e \func{setgid}} \label{sec:proc_setuid} -Le due funzioni che venfono usate per cambiare identità (cioè utente e gruppo +Le due funzioni che vengono usate per cambiare identità (cioè utente e gruppo di appartenenza) ad un processo sono rispettivamente \func{setuid} e \func{setgid}; come accennato in \secref{sec:proc_user_group} in Linux esse -seguono la sematica POSIX che prevede l'esistenza di \textit{saved user id} e +seguono la semantica POSIX che prevede l'esistenza di \textit{saved user id} e \textit{saved group id}; i loro prototipi sono: \begin{functions} @@ -1426,7 +1426,7 @@ delle funzioni che tratteremo in questa sezione. L'effetto della chiamata è diverso a seconda dei privilegi del processo; se l'\textit{effective user id} è zero (cioè è quello dell'amministratore di -sistema) allora tutti gli identificatatori (\textit{real}, \textit{effective} +sistema) allora tutti gli identificatori (\textit{real}, \textit{effective} e \textit{saved}) vengono settati al valore specificato da \var{uid}, altrimenti viene settato solo l'\textit{effective user id}, e soltanto se il valore specificato corrisponde o al \textit{real user id} o al \textit{saved @@ -1441,7 +1441,7 @@ eventualmente tornare indietro. Come esempio per chiarire dell'uso di queste funzioni prediamo quello con cui viene gestito l'accesso al file \file{/var/log/utmp}. In questo file viene registrato chi sta usando il sistema al momento corrente; chiaramente non può -essere lasciato aperto in scrittura a qualunque utente, che protrebbe +essere lasciato aperto in scrittura a qualunque utente, che potrebbe falsificare la registrazione. Per questo motivo questo file (e l'analogo \file{/var/log/wtmp} su cui vengono registrati login e logout) appartengono ad un gruppo dedicato (\acr{utmp}) ed i programmi che devono accedervi (ad @@ -1519,7 +1519,7 @@ Le funzioni restituiscono 0 in caso di successo e -1 in caso di fallimento: l'unico errore possibile è \macro{EPERM}. \end{functions} -I processi non privileguiati possono settare i \textit{real id} soltanto ai +I processi non privilegiati possono settare i \textit{real id} soltanto ai valori dei loro \textit{effective id} o \textit{real id} e gli \textit{effective id} ai valori dei loro \textit{real id}, \textit{effective id} o \textit{saved id}; valori diversi comportano il fallimento della @@ -1578,9 +1578,9 @@ l'unico errore possibile I processi non privilegiati possono cambiare uno qualunque degli identificatori usando uno qualunque dei valori correnti di \textit{real id}, -\textit{effective id} o \textit{saved id}, l'ammnistratore può specificare i +\textit{effective id} o \textit{saved id}, l'amministratore può specificare i valori che vuole; un valore di -1 per un qualunque parametro lascia inalterato -l'dentificatore corrispondente. +l'identificatore corrispondente. @@ -1634,7 +1634,7 @@ ha temporaneamente assunto l'identit quelli originari per quanto riguarda tutti gli altri controlli di accesso. Le due funzioni usate per cambiare questi identificatori sono \func{setfsuid} -e \func{setfsgid}, ovviamenete sono specifiche di Linux e non devono essere +e \func{setfsgid}, ovviamente sono specifiche di Linux e non devono essere usate se si intendono scrivere programmi portabili; i loro prototipi sono: \begin{functions} @@ -1683,7 +1683,7 @@ quando si ha la certezza che, qualora essa venga effettuata, tutti i passaggi che devono essere compiuti per realizzarla verranno eseguiti senza possibilità di interruzione in una fase intermedia. -In un ambiente multitasking il concetto è esseziale, dato che un processo può +In un ambiente multitasking il concetto è essenziale, dato che un processo può essere interrotto in qualunque momento dal kernel che mette in esecuzione un altro processo o dalla ricezione di un segnale; occorre pertanto essere accorti nei confronti delle possibili \textit{race condition} (vedi @@ -1736,7 +1736,7 @@ di eseguire atomicamente le operazioni necessarie, occorre che le risorse condivise siano opportunamente protette da meccanismi di sincronizzazione (torneremo su queste problematiche di questo tipo in \secref{sec:ipc_semaph}). -Un caso particolare di \textit{race condition} sono poi i cosidetti +Un caso particolare di \textit{race condition} sono poi i cosiddetti \textit{deadlock}; l'esempio tipico è quello di un flag di ``occupazione'' che viene rilasciato da un evento asincrono fra il controllo (in cui viene trovato occupato) e la successiva messa in attesa, attesa che a questo punto diventerà -- 2.30.2