X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=process.tex;h=0ca84e1ae3d4d6fea72496775311966a3bffbdf5;hp=23206304b18e5554b471042a77943c67ecafa198;hb=4504f0bd1ad5cb4b933e6ee80a767103e517376f;hpb=b4611e5ee9458ce9b06b2cd35b661331b687ba78 diff --git a/process.tex b/process.tex index 2320630..0ca84e1 100644 --- a/process.tex +++ b/process.tex @@ -481,11 +481,6 @@ sia pi restituito da una precedente chiamata a una qualunque delle funzioni di allocazione e che non sia già stato liberato da un'altra chiamata a \texttt{free}, in caso contrario il comportamento della funzione è indefinito. -Uno degli errori più comuni (specie se si ha a che fare con array di -puntatori) è infatti quello di chiamare \texttt{free} due volte; per evitare -questo problema una soluzione è quella di assegnare sempre a \texttt{NULL} -ogni puntatore liberato con \texttt{free}, dato che, quando il parametro è un -puntatore nullo, non viene non viene effettuata nessuna operazione. La funzione \texttt{realloc} si usa invece per cambiare (in genere aumentare) la dimensione di un'area di memoria precedentemente allocata, la funzione @@ -510,27 +505,158 @@ essere altri puntatori che puntino all'interno di un'area che si vuole ridimensionare. +Uno degli errori più comuni (specie se si ha a che fare con array di +puntatori) è infatti quello di chiamare \texttt{free} più di una volta sullo +stesso puntatore; per evitare questo problema una soluzione di ripiego è +quella di assegnare sempre a \texttt{NULL} ogni puntatore liberato con +\texttt{free}, dato che, quando il parametro è un puntatore nullo, +\texttt{free} non esegue nessuna operazione. + +Linux e le glibc hanno una implementazione delle routine di allocazione che è +controllabile dall'utente attraverso alcune variabili di ambiente, in +particolare diventa possibile tracciare questo tipo di errori usando la +variabile \texttt{MALLOC\_CHECK\_} che quando viene settata mette in uso una +versione meno efficiente delle funzioni, che però è più tollerante nei +confronti di piccoli errori come quello di chiamate doppie a \texttt{free}; in +pparticolare se la variabile è posta a zero gli errori vengono ignorati, se è +posta ad 1 viene stampato un avviso sullo standard error e se + +Il problema più comune e più difficile da tracciare che si incontra con +l'allocazione della memoria è però quando la memoria non più utilizzata non +viene opportunamente liberata (quello che in inglese viene chiamato +\textit{memory-leak}, traducibile come \textsl{perdita di memoria}). + +Un caso tipico è quando l'allocazione viene fatta da una subroutine per un uso +locale, ma la memoria non viene liberata una volta usata; chiamate ripetute +alla stessa suubroutine causeranno a lungo andare un esaurimento della memoria +disponibile, con un conseguente crash dell'applicazione che può avvenire in +qualunque momento senza nessuna relazione con la subroutine che contiene +l'errore. + +Per questo motivo l'implementazione delle routine di allocazione delle glibc +mette a disposizione una serie di funzionalità (su cui torneremo in +\secref{sec:proc_mem_advanced}) che permettono di tracciare le allocazioni e +le disallocazione, e definisce anche una serie di possibili agganci che +permettono di sostituire alle funzioni di libreria una propria versione (che +può essere più o meno specializzata per il debugging). - altrimenti si quello che -viene definito una \textsl{perdita di memoria} (in inglese -\textit{memory-leak}) +\subsection{La funzione \texttt{alloca}} +\label{sec:proc_mem_alloca} +Una alternativa possibile all'uso di \texttt{malloc}, che non soffre del tipo +di problemi di memomry leak descritti in precedenza è la funzione +\texttt{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} bytes 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}. +\end{prototype} +ma in questo caso non è più necessario liberare la memoria in quanto questa +viene rilasciata automaticamente al ritorno della funzione. -\subsection{La funzione \texttt{alloca}} -\label{sec:proc_mem_alloca} +Come è evidente questa funzione ha molti vantaggi, e permette di evitare i +problemi di memory leak non essendo più necessaria la deallocazione esplicita; +una delle ragioni principali per usarla è però che funziona anche quando si +usa \texttt{longjump} per uscire con un salto non locale da una funzione (vedi +\secref{sec:proc_longjmp}), +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. + +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{segmentation violation} analogo a quello che si avrebbe da +una ricorsione infinita. \subsection{Le funzioni \texttt{brk} e \texttt{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 \texttt{malloc}. Le funzione sono: +\begin{prototype}{unistd.h}{int *brk(void end\_data\_segment)} + Sposta la fine del segmento dei dati all'indirizzo specificato da + \texttt{end\_data\_segment}. + + La funzione restituisce 0 in caso di successo e -1 in caso di fallimento, + nel qual caso \texttt{errno} viene settata a \texttt{ENOMEM}. +\end{prototype} +\begin{prototype}{unistd.h}{int *sbrk(ptrdiff\_t increment)} + Incrementa lo spazio dati di un programma di \texttt{increment}. Un valore + zero restituisce l'attuale posizione della fine del segmento dati. + + La funzione restituisce il puntatore all'inzio della nuova 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}. +\end{prototype} + +Queste funzioni sono state deliberatamente escluse dallo standard POSIX.1 e +per i programmi normali è opportuno usare le funzioni di allocazione standard +descritte in precedenza, che sono costruite su di esse. In genere si usa +\texttt{sbrk} con un valore zero per ottenere l'attuale posizione della fine +del segmento dati. + + +% \subsection{La personalizzazione delle funzioni di allocazione} +% \label{sec:proc_mem_malloc_custom} + \subsection{Il controllo della memoria virtuale} -\label{sec:proc_mem_sbrk} +\label{sec:proc_mem_mlock} + +Come spiegato in \secref{sec:proc_mem_gen} il kernel gestisce la memoria in +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 +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: +\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 + la pagina in memoria; per questo motivi processi critici che hanno esigenze + di tempo reale o tolleranze critiche nella risposte (ad esempio processi che + trattano campionamenti sonori) possono non essere in grado di sopportare + le variazioni della velocità di accesso dovuta alla paginazione. + + In certi casi poi un programmatore può conoscere meglio dell'algoritmo di + allocazione delle pagine le esigenze specifiche del suo programma e decidere + quali pagine di memoria è opportuno che restino in memoria per un aumento + delle prestazioni. In genere queste sono esigenze particolari e richiedono + anche un aumento delle priorità in esecuzione (vedi \secref{sec:xxx_xxx}). + +\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. +\end{itemize} \section{Il controllo di flusso non locale} -\label{sec:proc_flux} +\label{sec:proc_longjmp} + +Il controllo del flusso di un programma in genere viene effettuato con le +varie istruzioni del linguaggio C, la più bistrattata delle quali è il +\texttt{goto} ampiamente deprecato in favore di costrutti più puliti; esiste +però un caso in l'uso di questa istruzione porta all'implementazione più +efficiente, quello dell'uscita in caso di errore. + +Il C però non consente di effettuare un salto ad una label definita in +un'altra funzione, per cui se l'errore avviene in funzioni profondamente +annidate \section{La gestione di parametri e opzioni}