+%% process.tex
+%%
+%% Copyright (C) 2000-2009 Simone Piccardi. Permission is granted to
+%% copy, distribute and/or modify this document under the terms of the GNU Free
+%% Documentation License, Version 1.1 or any later version published by the
+%% Free Software Foundation; with the Invariant Sections being "Un preambolo",
+%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the
+%% license is included in the section entitled "GNU Free Documentation
+%% License".
+%%
+
\chapter{L'interfaccia base con i processi}
\label{cha:process_interface}
-Come accennato nell'introduzione il processo è l'unità di base con cui un
-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
+Come accennato nell'introduzione il \textsl{processo} è l'unità di base con
+cui un sistema unix-like alloca ed utilizza le risorse. Questo capitolo
+tratterà l'interfaccia base fra il sistema e i processi, come vengono passati
+gli argomenti, come viene gestita e allocata la memoria, come un processo può
+richiedere servizi al sistema e cosa deve fare quando ha finito la sua
esecuzione. Nella sezione finale accenneremo ad alcune problematiche generiche
di programmazione.
In genere un programma viene eseguito quando un processo lo fa partire
-eseguendo una funzione della famiglia \func{exec}; torneremo su questo e
-sulla la creazione e gestione dei processi nel prossimo capitolo, in questo
+eseguendo una funzione della famiglia \func{exec}; torneremo su questo e sulla
+creazione e gestione dei processi nel prossimo capitolo. In questo
affronteremo l'avvio e il funzionamento di un singolo processo partendo dal
-punto di vista del programma posto in esecuzione.
-
+punto di vista del programma che viene messo in esecuzione.
\section{Esecuzione e conclusione di un programma}
-Una delle concetti base relativi ai processi è che un processo esegue sempre
-uno ed un solo programma: si possono avere più processi che eseguono lo stesso
-programma ma ciascun processo vedrà la sua copia del codice (in realtà il
-kernel fa si che tutte le parti uguali siano condivise) avrà un suo spazio di
-indirizzi, variabili proprie e sarà eseguito in maniera completamente
-indipendente da tutti gli altri.
-
-Anche quando all'interno di un programma possono essere presenti più
-\textsl{filoni} di esecuzione (i cosiddetti \textit{thread}), o questo possa
-essere composto da moduli multipli completamente separati, quando questo sarà
-posto in esecuzione esso apparirà al sistema come un solo processo (il
-discorso dei \textit{thread} comunque in Linux necessita di una trattazione a
-parte per la peculiarità dell'implementazione).
+Uno dei concetti base di Unix è che un processo esegue sempre uno ed un solo
+programma: si possono avere più processi che eseguono lo stesso programma ma
+ciascun processo vedrà la sua copia del codice (in realtà il kernel fa sì che
+tutte le parti uguali siano condivise), avrà un suo spazio di indirizzi,
+variabili proprie e sarà eseguito in maniera completamente indipendente da
+tutti gli altri.\footnote{questo non è del tutto vero nel caso di un programma
+ \textit{multi-thread}, ma la gestione dei \itindex{thread} \textit{thread}
+ in Linux sarà trattata a parte in cap.~\ref{cha:threads}.}
\subsection{La funzione \func{main}}
\label{sec:proc_main}
-Quando un programma viene lanciato il kernel esegue una opportuna routine di
-avvio, usando il programma \cmd{ld-linux.so}, è questo programma che prima
-carica le librerie condivise che servono al programma, effettua il link
-dinamico del codice e poi alla fine lo esegue. Infatti, a meno di non aver
+Quando un programma viene lanciato il kernel esegue un opportuno codice di
+avvio, usando il programma \cmd{ld-linux.so}. Questo programma prima carica
+le librerie condivise che servono al programma, poi effettua il collegamento
+dinamico del codice e alla fine lo esegue. Infatti, a meno di non aver
specificato il flag \texttt{-static} durante la compilazione, tutti i
-programmi in Linux sono incompleti e necessitano di essere linkati alle
-librerie condivise quando vengono avviati. La procedura è controllata da
-alcune variabili di ambiente e dal contenuto di \file{/etc/ld.so.conf}, i
-dettagli sono riportati nella man page di \cmd{ld.so}.
+programmi in Linux sono incompleti e necessitano di essere \textsl{collegati}
+alle librerie condivise quando vengono avviati. La procedura è controllata da
+alcune variabili di ambiente e dal contenuto di \conffile{/etc/ld.so.conf}. I
+dettagli sono riportati nella pagina di manuale di \cmd{ld.so}.
Il sistema fa partire qualunque programma chiamando la funzione \func{main};
sta al programmatore chiamare così la funzione principale del programma da cui
-si suppone iniziale l'esecuzione; in ogni caso senza questa funzione lo stesso
-linker darebbe luogo ad errori.
-
-Lo standard ISO C specifica che la funzione \func{main} può non avere
-argomenti o prendere due argomenti che rappresentano gli argomenti passati da
-linea di comando, in sostanza un prototipo che va sempre bene è il seguente:
-\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
- int main (int argc, char *argv[])
-\end{lstlisting}
-
-In realtà nei sistemi unix esiste un'altro modo per definire la funzione
-\func{main}, che prevede la presenza di un terzo parametro, \var{char
- *envp[]}, che fornisce l'\textsl{ambiente} (vedi \secref{sec:proc_environ})
-del programma; questa forma però non è prevista dallo standard POSIX.1 per cui
-se si vogliono scrivere programmi portabili è meglio evitarla.
+si suppone iniziare l'esecuzione; in ogni caso senza questa funzione lo stesso
+\textit{linker} (si chiama così il programma che effettua i collegamenti di
+cui sopra) darebbe luogo ad errori. Lo standard ISO C specifica che la
+funzione \func{main} può non avere argomenti o prendere due argomenti che
+rappresentano gli argomenti passati da linea di comando, in sostanza un
+prototipo che va sempre bene è il seguente:
+\includecodesnip{listati/main_def.c}
+
+In realtà nei sistemi Unix esiste un altro modo per definire la funzione
+\func{main}, che prevede la presenza di un terzo argomento, \code{char
+ *envp[]}, che fornisce (vedi sez.~\ref{sec:proc_environ})
+l'\textsl{ambiente} del programma; questa forma però non è prevista dallo
+standard POSIX.1 per cui se si vogliono scrivere programmi portabili è meglio
+evitarla.
\subsection{Come chiudere un programma}
\label{sec:proc_conclusion}
-La via normale per la quale un programma finisce è quando la funzione
-\func{main} ritorna, una modalità equivalente di conclusione è quella di
-chiamare direttamente la funzione \func{exit} (che viene comunque chiamata
-dalla routine di avvio del programma quando la funzione \func{main} ritorna).
-Una forma alternativa è quella di chiamare direttamente la system call
-\func{\_exit} che passa il controllo direttamente al kernel.
-
-Oltre alla conclusione ``normale'' esiste anche la possibilità di una
-conclusione ``anomala'' del programma a causa di segnali o della chiamata alla
-funzione \func{abort} (che comunque genera un segnale che termina il
-programma); torneremo su questo in \secref{sec:proc_termination}.
-
-Il valore di ritorno della funzione main, o quello usato nelle chiamate ad
-\func{exit} e \func{\_exit}, viene chiamato \textit{exit status} e passato
-al processo padre che aveva lanciato il programma (in genere la shell). In
-generale si usa questo valore per fornire un'informazione generica sulla
-riuscita o il fallimento del programma; l'informazione è necessariamente
-generica, ed il valore deve essere compreso fra 0 e 255.
-
-In generale si usa la convenzione di restituire 0 in caso di successo e 1 in
-caso di fallimento, i programmi che effettuano dei confronti (come
-\cmd{diff}) usano invece una notazione leggermente diversa, usando 0 per
-indicare la corrispondenza, 1 per indicare la non corrispondenza e 2 per
-indicare l'incapacità di effettuare il confronto. È opportuno adottare una di
-queste convenzioni a seconda dei casi. Si tenga presente che se si raggiunge
-la fine della funzione \func{main} senza ritornare esplicitamente si ha un
-valore di uscita indefinito, è pertanto consigliabile di concludere sempre in
-maniera esplicita detta funzione.
-
-Una altra convenzione riserva i valori da 128 in su per usi speciali, ad
+Normalmente un programma finisce quando la funzione \func{main} ritorna, una
+modalità equivalente di chiudere il programma è quella di chiamare
+direttamente la funzione \func{exit} (che viene comunque chiamata
+automaticamente quando \func{main} ritorna). Una forma alternativa è quella
+di chiamare direttamente la system call \func{\_exit}, che restituisce il
+controllo direttamente alla funzione di conclusione dei processi del kernel.
+
+Oltre alla conclusione ``\textsl{normale}'' esiste anche la possibilità di una
+conclusione ``\textsl{anomala}'' del programma a causa della ricezione di un
+segnale (tratteremo i segnali in cap.~\ref{cha:signals}) o della chiamata alla
+funzione \func{abort}; torneremo su questo in sez.~\ref{sec:proc_termination}.
+
+Il valore di ritorno della funzione \func{main}, o quello usato nelle chiamate
+ad \func{exit} e \func{\_exit}, viene chiamato \textsl{stato di uscita} (o
+\textit{exit status}) e passato al processo che aveva lanciato il programma
+(in genere la shell). In generale si usa questo valore per fornire
+informazioni sulla riuscita o il fallimento del programma; l'informazione è
+necessariamente generica, ed il valore deve essere compreso fra 0 e 255.
+
+La convenzione in uso pressoché universale è quella di restituire 0 in caso di
+successo e 1 in caso di fallimento; l'unica eccezione è per i programmi che
+effettuano dei confronti (come \cmd{diff}), che usano 0 per indicare la
+corrispondenza, 1 per indicare la non corrispondenza e 2 per indicare
+l'incapacità di effettuare il confronto. È opportuno adottare una di queste
+convenzioni a seconda dei casi. Si tenga presente che se si raggiunge la fine
+della funzione \func{main} senza ritornare esplicitamente si ha un valore di
+uscita indefinito, è pertanto consigliabile di concludere sempre in maniera
+esplicita detta funzione.
+
+Un'altra convenzione riserva i valori da 128 a 256 per usi speciali: ad
esempio 128 viene usato per indicare l'incapacità di eseguire un altro
-programma in un sottoprocesso. Benché anche questa convenzione non sia
+programma in un sottoprocesso. Benché questa convenzione non sia
universalmente seguita è una buona idea tenerne conto.
-Si tenga presente inoltre che non è una buona idea usare il valore dell'errore
-restituito dalla variabile \var{errno} come stato di uscita, in generale
-una shell non si cura di tutto questo e comunque il valore dello stato di
-uscita è sempre troncato ad 8 bit, per cui si potrebbe incorrere nel caso in
-cui l'errore 256, diventando zero, verrebbe interpretato come un successo. In
-\file{stdlib.h} sono definite due macro \macro{EXIT\_SUCCESS} e
-\macro{EXIT\_FAILURE}, che in Linux sono poste rispettivamente ai valori 0 e
-1 (di tipo \type{int}), seguendo lo standard POSIX.
+Si tenga presente inoltre che non è una buona idea usare il codice di errore
+restituito dalla variabile \var{errno} (per i dettagli si veda
+sez.~\ref{sec:sys_errors}) come stato di uscita. In generale infatti una shell
+non si cura del valore se non per vedere se è diverso da zero; inoltre il
+valore dello stato di uscita è sempre troncato ad 8 bit, per cui si potrebbe
+incorrere nel caso in cui restituendo un codice di errore 256, si otterrebbe
+uno stato di uscita uguale a zero, che verrebbe interpretato come un successo.
-Infine occorre distinguere fra lo stato di uscita di un programma
-(l'\textit{exit status}) e lo stato di conclusione di un processo (il
-\textit{termination status}), abbiamo già accennato infatti che è comunque
-possibile un processo possa essere terminato (da un segnale) prima che il
-programma in esecuzione si sia concluso. In caso di conclusione normale del
-programma però lo stato di uscita diventa parte dello stato di conclusione del
-processo (vedi \secref{sec:proc_termination}).
+In \file{stdlib.h} sono definite, seguendo lo standard POSIX, le due costanti
+\const{EXIT\_SUCCESS} e \const{EXIT\_FAILURE}, da usare sempre per specificare
+lo stato di uscita di un processo. In Linux esse sono poste rispettivamente ai
+valori di tipo \ctyp{int} 0 e 1.
\subsection{Le funzioni \func{exit} e \func{\_exit}}
\label{sec:proc_exit}
-Come accennato funzioni per l'uscita ``normale'' da un programma sono due, la
-prima è la funzione \func{exit} che è definita dallo standard ANSI C; il
-prototipo della funzione è il seguente:
+Come accennato le funzioni usate per effettuare un'uscita ``\textit{normale}''
+da un programma sono due, la prima è la funzione \funcd{exit}, che è definita
+dallo standard ANSI C ed il cui prototipo è:
\begin{prototype}{stdlib.h}{void exit(int status)}
- Causa la conclusione ordinaria del programma restituendo il valore
- \var{status} al processo padre.
+ Causa la conclusione ordinaria del programma.
\bodydesc{La funzione non ritorna. Il processo viene terminato.}
\end{prototype}
-La funzione \func{exit} è pensata per una conclusione pulita di un programma
-che usa le librerie standard del C; essa esegue tutte le funzioni che sono
-state registrate con \func{atexit} e \func{on\_exit} (vedi
-\secref{sec:proc_atexit}), e chiude tutti gli stream di I/O effettuando il
+La funzione \func{exit} è pensata per eseguire una conclusione pulita di un
+programma che usi le librerie standard del C; essa esegue tutte le funzioni
+che sono state registrate con \func{atexit} e \func{on\_exit} (vedi
+sez.~\ref{sec:proc_atexit}), e chiude tutti gli stream effettuando il
salvataggio dei dati sospesi (chiamando \func{fclose}, vedi
-\secref{sec:file_fclose}), infine ripassa il controllo al kernel chiamando
-\func{\_exit} e passando il valore \var{status} come stato di uscita.
+sez.~\ref{sec:file_fopen}), infine passa il controllo al kernel chiamando
+\func{\_exit} e restituendo il valore di \param{status} come stato di uscita.
-La system call \func{\_exit} restituisce direttamente il controllo al
-kernel, concludendo immediatamente il processo, le eventuali funzioni
-registrate con \func{atexit} e \func{on\_exit} non vengono eseguite. Il
-prototipo della funzione è il seguente:
+La system call \funcd{\_exit} restituisce direttamente il controllo al kernel,
+concludendo immediatamente il processo; i dati sospesi nei buffer degli stream
+non vengono salvati e le eventuali funzioni registrate con \func{atexit} e
+\func{on\_exit} non vengono eseguite. Il prototipo della funzione è:
\begin{prototype}{unistd.h}{void \_exit(int status)}
- Causa la conclusione immediata del programma restituendo il valore
- \var{status} al processo padre.
+ Causa la conclusione immediata del programma.
\bodydesc{La funzione non ritorna. Il processo viene terminato.}
\end{prototype}
La funzione chiude tutti i file descriptor appartenenti al processo (si tenga
presente che questo non comporta il salvataggio dei dati bufferizzati degli
-stream), fa si che ogni figlio del processo sia ereditato da \cmd{init}
-(vedi \secref{cha:process_handling}), manda un segnale \macro{SIGCHLD} al
-processo padre (vedi \ref{sec:sig_job_control}) ed infine ritorna lo stato di
-uscita specificato in \var{status} che può essere raccolto usando la
-funzione \func{wait} (vedi \secref{sec:proc_wait}).
+stream), fa sì che ogni figlio del processo sia adottato da \cmd{init} (vedi
+cap.~\ref{cha:process_handling}), manda un segnale \const{SIGCHLD} al processo
+padre (vedi sez.~\ref{sec:sig_job_control}) ed infine ritorna lo stato di
+uscita specificato in \param{status} che può essere raccolto usando la
+funzione \func{wait} (vedi sez.~\ref{sec:proc_wait}).
\subsection{Le funzioni \func{atexit} e \func{on\_exit}}
\label{sec:proc_atexit}
-Come accennato l'uso di \func{exit} al posto della \func{\_exit} è fatto
-principalmente per permettere una uscita pulita dalle funzioni delle librerie
-standard del C (in particolare per quel che riguarda la chiusura degli
-stream).
-
-Quando si realizza una libreria da usare in varie applicazioni può essere
-perciò utile evitare di richiedere di chiamare esplicitamente un funzione di
-uscita che esegua tutte le operazioni di pulizia prima di uscire (come quella
-di salvare eventuali dati sospesi). È invece molto meno soggetto ad errori e
-completamente trasparente all'utente poter effettuare una chiamata automatica
-di una funzione che effettui tali operazioni all'uscita dal programma.
-
-A questo scopo lo standard ANSI C prevede la possibilità di registrare un
-certo numero funzioni che verranno eseguite all'uscita dal programma (sia per
-la chiamata ad \func{exit} che per il ritorno di \func{main}). La prima
-funzione che si può utilizzare a tal fine è:
+Un'esigenza comune che si incontra nella programmazione è quella di dover
+effettuare una serie di operazioni di pulizia (ad esempio salvare dei dati,
+ripristinare delle impostazioni, eliminare dei file temporanei, ecc.) prima
+della conclusione di un programma. In genere queste operazioni vengono fatte
+in un'apposita sezione del programma, ma quando si realizza una libreria
+diventa antipatico dover richiedere una chiamata esplicita ad una funzione di
+pulizia al programmatore che la utilizza.
+
+È invece molto meno soggetto ad errori, e completamente trasparente
+all'utente, avere la possibilità di effettuare automaticamente la chiamata ad
+una funzione che effettui tali operazioni all'uscita dal programma. A questo
+scopo lo standard ANSI C prevede la possibilità di registrare un certo numero
+di funzioni che verranno eseguite all'uscita dal programma (sia per la
+chiamata ad \func{exit} che per il ritorno di \func{main}). La prima funzione
+che si può utilizzare a tal fine è \funcd{atexit} il cui prototipo è:
\begin{prototype}{stdlib.h}{void atexit(void (*function)(void))}
- Registra la funzione \param{function} per essere chiamata all'uscita dal
- programma.
+ Registra la funzione \param{function} per la chiamata all'uscita dal
+ programma.
- \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
- fallimento, \var{errno} non viene settata.}
+ \bodydesc{La funzione restituisce $0$ in caso di successo e $-1$ in caso di
+ fallimento, \var{errno} non viene modificata.}
\end{prototype}
-
-La funzione richiede come argomento l'indirizzo della opportuna da chiamare
-all'uscita che non deve prendere argomenti e non deve ritornare niente. Una
-estensione di \func{atexit} è la funzione \func{on\_exit} (che la glibc
-include per compatibilità con SunOS e che non è detto sia definita su altri
-sistemi), il cui prototipo è:
+\noindent la funzione richiede come argomento l'indirizzo di una opportuna
+funzione di pulizia da chiamare all'uscita del programma, che non deve
+prendere argomenti e non deve ritornare niente (deve essere cioè definita come
+\code{void function(void)}).
+
+Un'estensione di \func{atexit} è la funzione \funcd{on\_exit}, che le
+\acr{glibc} includono per compatibilità con SunOS, ma che non è detto sia
+definita su altri sistemi; il suo prototipo è:
\begin{prototype}{stdlib.h}
-{void on\_exit(void (*function)(int status, void *arg), void *arg)}
- Registra la funzione \var{function} per essere chiamata all'uscita dal
- programma. Tutte le funzioni registrate vengono chiamate in ordine inverso
- rispetto a quello di registrazione.
+{void on\_exit(void (*function)(int , void *), void *arg)}
+ Registra la funzione \param{function} per la chiamata all'uscita dal
+ programma.
- \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
- fallimento, \var{errno} non viene settata.}
+ \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
+ fallimento, \var{errno} non viene modificata.}
\end{prototype}
-In questo caso la funzione da chiamare prende due parametri, il primo dei
-quali sarà inizializzato allo stato di uscita con cui è stata chiamata
-\func{exit} ed il secondo al puntatore generico specificato come secondo
-argomento nella chiamata di \func{on\_exit}.
+In questo caso la funzione da chiamare all'uscita prende i due argomenti
+specificati nel prototipo, dovrà cioè essere definita come \code{void
+ function(int status, void *argp)}. Il primo argomento sarà inizializzato
+allo stato di uscita con cui è stata chiamata \func{exit} ed il secondo al
+puntatore \param{arg} passato come secondo argomento di \func{on\_exit}. Così
+diventa possibile passare dei dati alla funzione di chiusura.
Nella sequenza di chiusura tutte le funzioni registrate verranno chiamate in
ordine inverso rispetto a quello di registrazione (ed una stessa funzione
\label{sec:proc_term_conclusion}
Data l'importanza dell'argomento è opportuno sottolineare ancora una volta che
-in un sistema unix l'unico modo in cui un programma può essere eseguito dal
-kernel è attraverso la chiamata alla system call \func{execve} (in genere
-attraverso una delle funzioni \func{exec} che vedremo in
-\secref{sec:proc_exec}).
+in un sistema Unix l'unico modo in cui un programma può essere eseguito dal
+kernel è attraverso la chiamata alla system call \func{execve} (o attraverso
+una delle funzioni della famiglia \func{exec} che vedremo in
+sez.~\ref{sec:proc_exec}).
Allo stesso modo l'unico modo in cui un programma può concludere
volontariamente la sua esecuzione è attraverso una chiamata alla system call
-\func{\_exit} sia esplicitamente o che in maniera indiretta attraverso l'uso
-di \func{exit} o il ritorno della funzione \func{main}.
+\func{\_exit}, o esplicitamente, o in maniera indiretta attraverso l'uso di
+\func{exit} o il ritorno di \func{main}.
-Lo schema delle modalità con cui si avvia e conclude normalmente un programma
-è riportato in \nfig.
+Uno schema riassuntivo che illustra le modalità con cui si avvia e conclude
+normalmente un programma è riportato in fig.~\ref{fig:proc_prog_start_stop}.
\begin{figure}[htb]
\centering
- \includegraphics[width=12cm]{img/proc_beginend}
+% \includegraphics[width=9cm]{img/proc_beginend}
+ \begin{tikzpicture}[>=stealth]
+ \filldraw[fill=black!35] (-0.3,0) rectangle (12,1);
+ \draw(5.5,0.5) node {\large{kernel}};
+
+ \filldraw[fill=black!15] (1.5,2) rectangle (4,3);
+ \draw (2.75,2.5) node {\texttt{ld-linux.so}};
+ \draw [->] (2.75,1) -- (2.75,2);
+ \draw (2.75,1.5) node [anchor=west]{\texttt{exec}};
+
+ \filldraw[fill=black!15,rounded corners] (1.5,4) rectangle (4,5);
+ \draw (2.75,4.5) node {\texttt{main}};
+
+ \draw [<->, dashed] (2.75,3) -- (2.75,4);
+ \draw [->] (1.5,4.5) -- (0.3,4.5) -- (0.3,1);
+ \draw (0.9,4.5) node [anchor=south] {\texttt{\_exit}};
+
+ \filldraw[fill=black!15,rounded corners] (1.5,6) rectangle (4,7);
+ \draw (2.75,6.5) node {\texttt{funzione}};
+
+ \draw [<->, dashed] (2.75,5) -- (2.75,6);
+ \draw [->] (1.5,6.5) -- (0.05,6.5) -- (0.05,1);
+ \draw (0.9,6.5) node [anchor=south] {\texttt{\_exit}};
+
+ \draw (6.75,4.5) node (exit) [rectangle,fill=black!15,minimum width=2.5cm,minimum height=1cm,rounded corners, draw]{\texttt{exit}};
+
+ \draw[->] (4,6.5) -- node[anchor=south west]{\texttt{exit}} (exit);
+ \draw[->] (4,4.5) -- node[anchor=south]{\texttt{exit}} (exit);
+ \draw[->] (exit) -- node[anchor=east]{\texttt{\_exit}}(6.75,1);
+
+ \draw (10,4.5) node (exithandler1) [rectangle,fill=black!15,rounded corners, draw]{exit handler};
+ \draw (10,5.5) node (exithandler2) [rectangle,fill=black!15,rounded corners, draw]{exit handler};
+ \draw (10,3.5) node (stream) [rectangle,fill=black!15,rounded corners, draw]{chiusura stream};
+
+ \draw[<->, dashed] (exithandler1) -- (exit);
+ \draw[<->, dashed] (exithandler2) -- (exit);
+ \draw[<->, dashed] (stream) -- (exit);
+ \end{tikzpicture}
\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
-\curfig); torneremo su questo aspetto in \secref{cha:signals}.
+fig.~\ref{fig:proc_prog_start_stop}); tratteremo nei dettagli i segnali e la
+loro gestione nel capitolo \ref{cha:signals}.
\section{I processi e l'uso della memoria}
\label{sec:proc_memory}
-Una delle risorse base che ciascun processo ha a disposizione è la memoria, ed
-uno degli aspetti più complessi di un sistema unix (ed in particolar modo di
-Linux) è appunto la gestione della memoria. Qui ci occuperemo però di come la
-memoria viene vista dal punto di vista di un programma in esecuzione in un
-processo.
+Una delle risorse base che ciascun processo ha a disposizione è la memoria, e
+la gestione della memoria è appunto uno degli aspetti più complessi di un
+sistema unix-like. In questa sezione, dopo una breve introduzione ai concetti
+base, esamineremo come la memoria viene vista da parte di un programma in
+esecuzione, e le varie funzioni utilizzabili per la sua gestione.
\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 in maniera diretta dall'architettura
-dell'hardware), ma quello più tipico, usato da unix (e da Linux) è quello di
-assegnare ad ogni processo uno spazio virtuale di indirizzamento lineare in
-cui gli indirizzi vanno da zero ad un qualche valore massimo (nel caso di
-Linux fino al kernel 2.2 detto massimo era per macchine a 32bit di 2Gb, con il
-kernel 2.4 il limite è stato esteso).
-
-Come accennato nell'introduzione questo spazio di indirizzi è virtuale e non
-corrisponde all'effettiva posizione dei dati nella RAM del computer; in genere
-detto spazio non è neanche continuo (cioè non tutti gli indirizzi sono
-utilizzabili e/o utilizzati).
-
-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), 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 hanno detta funzione nel loro codice.
-
-La corrispondenza fra le pagine della memoria virtuale e quelle della memoria
-fisica della macchina viene gestita in maniera trasparente dall'hardware di
-gestione della memoria (la \textit{Memory Management Unit} del processore),
-ma poiché in genere quest'ultima è solo una piccola frazione della memoria
-virtuale è necessario un meccanismo che permetta di trasferire le pagine
-virtuali che servono dal supporto su cui si trovano in memoria, eliminando
-quelle che non servono. Questo meccanismo è detto \textit{paging}, ed è uno
-dei compiti principali del kernel.
+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 \index{memoria~virtuale} \textsl{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
+computer; in genere detto spazio non è neppure continuo (cioè non tutti gli
+indirizzi possibili sono utilizzabili, e quelli usabili non sono
+necessariamente adiacenti).
+
+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 ciascun
+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 \index{paginazione} \textsl{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}; l'hardware di
-gestione della memoria (la MMU del processore) genera una 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 disponibili
-in memoria. L'unica differenza avvertibile è quella dei tempi di esecuzione,
-che passano dai pochi nanosecondi necessari per l'accesso a tempi molto più
-lunghi, dovuti all'intervento del kernel. Normalmente questo è il prezzo da
-pagare per avere un multitasking reale, ed 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 del paging e mantenere fisse delle pagine in memoria (vedi
-\ref{sec:proc_mem_lock}).
+trasparente, e tutto avviene come se tutte le pagine fossero sempre
+disponibili in memoria. L'unica differenza avvertibile è quella dei tempi di
+esecuzione, che passano dai pochi nanosecondi necessari per l'accesso in RAM,
+a tempi molto più lunghi, dovuti all'intervento del kernel.
+
+Normalmente questo è il prezzo da pagare per avere un multitasking reale, ed
+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 \index{paginazione} paginazione e
+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}
Benché lo spazio di indirizzi virtuali copra un intervallo molto ampio, solo
una parte di essi è effettivamente allocato ed utilizzabile dal processo; il
tentativo di accedere ad un indirizzo non allocato è un tipico errore che si
-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 una associazione della pagina
-virtuale il kernel risponde al relativo \textit{page fault}, mandando un
-segnale \macro{SIGSEGV} al processo, che normalmente ne causa la terminazione
-immediata.
-
-È pertanto importante capire come viene strutturata la 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:
+commette quando si è manipolato male un puntatore e genera quella che viene
+chiamata una \itindex{segment~violation} \textit{segment violation}. 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
+\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 \index{memoria~virtuale}
+\textsl{la 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:
\begin{enumerate}
-\item Il segmento di testo (\textit{text segment}). Contiene il codice
- macchina del programma e le costanti statiche. Normalmente viene condiviso,
- in modo che più processi (anche diversi nel caso di librerie) possano
- utilizzarlo, e viene marcato in sola lettura per evitare sovrascritture
- accidentali (o maliziose) che ne modifichino le istruzioni.
+\item Il \index{segmento!testo} segmento di testo o \textit{text segment}.
+ Contiene il codice del programma, delle funzioni di librerie da esso
+ utilizzate, e le costanti. Normalmente viene condiviso fra tutti i processi
+ che eseguono lo stesso programma (e anche da processi che eseguono altri
+ programmi nel caso delle librerie). Viene marcato in sola lettura per
+ evitare sovrascritture accidentali (o maliziose) che ne modifichino le
+ istruzioni.
Viene allocato da \func{exec} all'avvio del programma e resta invariato
per tutto il tempo dell'esecuzione.
-\item Il segmento dei dati (\textit{data segment}). Contiene le variabili
- globali (cioè quelle definite al di fuori di tutte le funzioni). Di norma è
- diviso in due parti.
+\item Il \index{segmento!dati} segmento dei dati o \textit{data segment}.
+ Contiene le variabili globali (cioè quelle definite al di fuori di tutte le
+ funzioni che compongono il programma) e le variabili statiche (cioè quelle
+ dichiarate con l'attributo \ctyp{static}). Di norma è diviso in due parti.
La prima parte è il segmento dei dati inizializzati, che contiene le
- variabili globali il cui valore è stato assegnato esplicitamente. Ad esempio
+ variabili il cui valore è stato assegnato esplicitamente. Ad esempio
se si definisce:
-\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
- double pi = 3.14;
-\end{lstlisting}
+\includecodesnip{listati/pi.c}
questo valore sarà immagazzinato in questo segmento. La memoria di questo
- segmento viene preallocato dalla \func{exec} e inizializzata ai valori
+ segmento viene preallocata all'avvio del programma e inizializzata ai valori
specificati.
La seconda parte è il segmento dei dati non inizializzati, che contiene le
- variabili globali il cui valore è stato non è assegnato esplicitamente. Ad
- esempio se si definisce:
-\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
- int vect[100];
-\end{lstlisting}
- questo valore sarà immagazzinato in questo segmento. Anch'esso viene
- allocato all'avvio, e tutte le variabili vengono inizializzate a
- zero (ed i puntatori a \macro{NULL}).
-
- Storicamente questo segmento viene chiamato BBS (da \textit{block started by
- symbol}. La sua dimensione è fissa.
+ variabili il cui valore non è stato assegnato esplicitamente. Ad esempio se
+ si definisce:
+\includecodesnip{listati/vect.c}
+ questo vettore sarà immagazzinato in questo segmento. Anch'esso viene
+ allocato all'avvio, e tutte le variabili vengono inizializzate a zero (ed i
+ puntatori a \val{NULL}).\footnote{si ricordi che questo vale solo per le
+ variabili che vanno nel segmento dati, e non è affatto vero in generale.}
+
+ Storicamente questa seconda parte del segmento dati viene chiamata BSS (da
+ \textit{Block Started by Symbol}). La sua dimensione è fissa.
-\item Lo \textit{heap}. Tecnicamente lo si può considerare l'estensione del
- segmento dati, a cui di solito è posto giusto di seguito. È qui che avviene
- l'allocazione dinamica della memoria; può essere ridimensionato allocando e
- disallocando la memoria dinamica con le apposite funzioni (vedi
- \secref{sec:proc_mem_alloc}), ma il suo limite inferiore (quello adiacente
- al segmento dati) ha una posizione fissa.
+\item Lo \itindex{heap} \textit{heap}. Tecnicamente lo si può considerare
+ l'estensione del segmento dati, a cui di solito è posto giusto di seguito. È
+ qui che avviene l'allocazione dinamica della memoria; può essere
+ ridimensionato allocando e disallocando la memoria dinamica con le apposite
+ funzioni (vedi sez.~\ref{sec:proc_mem_alloc}), ma il suo limite inferiore
+ (quello adiacente al segmento dati) ha una posizione fissa.
-\item Il segmento di \textit{stack}, che contiene lo \textit{stack} del
- programma. Tutte le volte che si effettua una chiamata ad una funzione è
- qui che viene salvato l'indirizzo di ritorno e le informazioni dello stato
- del chiamante (tipo il contenuto di alcuni registri della CPU); poi la
- funzione chiamata alloca qui lo spazio per le sue variabili locali, in
+\item Il segmento di \itindex{stack} \textit{stack}, che contiene quello che
+ viene chiamato \textit{stack} del programma. Tutte le volte che si effettua
+ una chiamata ad una funzione è qui che viene salvato l'indirizzo di ritorno
+ e le informazioni dello stato del chiamante (tipo il contenuto di alcuni
+ registri della CPU), poi la funzione chiamata alloca qui lo spazio per le
+ sue variabili locali. Tutti questi dati vengono \textit{impilati} (da questo
+ viene il nome \itindex{stack} \textit{stack}) in sequenza uno sull'altro; in
questo modo le funzioni possono essere chiamate ricorsivamente. Al ritorno
- della funzione lo spazio è automaticamente rilasciato.
-
- La dimensione di questo segmento aumenta seguendo la crescita dello stack
- del programma, ma non viene ridotta quando quest'ultimo si restringe.
+ della funzione lo spazio è automaticamente rilasciato e
+ ``\textsl{ripulito}''.\footnote{il compilatore si incarica di generare
+ automaticamente il codice necessario, seguendo quella che viene chiamata
+ una \textit{calling convention}; quella standard usata con il C ed il C++
+ è detta \textit{cdecl} e prevede che gli argomenti siano caricati nello
+ \textit{stack} dal chiamante da destra a sinistra, e che si il chiamante
+ stesso ad eseguire la ripulitura dello \textit{stack} al ritorno della
+ funzione, se ne possono però utilizzare di alternative (ad esempio nel
+ pascal gli argomenti sono inseriti da sinistra a destra ed è compito del
+ chiamato ripulire lo \textit{stack}), in genere non ci si deve preoccupare
+ di questo fintanto che non si mescolano funzioni scritte con linguaggi
+ diversi.}
+
+ La dimensione di questo segmento aumenta seguendo la crescita dello
+ \itindex{stack} \textit{stack} del programma, ma non viene ridotta quando
+ quest'ultimo si restringe.
\end{enumerate}
\begin{figure}[htb]
\centering
- \includegraphics[width=5cm]{img/memory_layout}
- \caption{Disposizione tipica dei segmenti di memoria di un processo}
+% \includegraphics[height=12cm]{img/memory_layout}
+ \begin{tikzpicture}
+ \draw (0,0) rectangle (4,1);
+ \draw (2,0.5) node {text};
+ \draw (0,1) rectangle (4,2.5);
+ \draw (2,1.75) node {dati inizializzati};
+ \draw (0,2.5) rectangle (4,5);
+ \draw (2,3.75) node {dati non inizializzati};
+ \draw (0,5) rectangle (4,9);
+ \draw[dashed] (0,6) -- (4,6);
+ \draw[dashed] (0,8) -- (4,8);
+ \draw (2,5.5) node {heap};
+ \draw (2,8.5) node {stack};
+ \draw [->] (2,6) -- (2,6.5);
+ \draw [->] (2,8) -- (2,7.5);
+ \draw (0,9) rectangle (4,10);
+ \draw (2,9.5) node {environment};
+ \draw (4,0) node [anchor=west] {\texttt{0x08000000}};
+ \draw (4,5) node [anchor=west] {\texttt{0x08xxxxxx}};
+ \draw (4,9) node [anchor=west] {\texttt{0xC0000000}};
+ \end{tikzpicture}
+ \caption{Disposizione tipica dei segmenti di memoria di un processo.}
\label{fig:proc_mem_layout}
\end{figure}
-Una disposizione tipica di questi segmenti è riportata in \nfig. Usando il
-comando \cmd{size} su un programma se ne può stampare le dimensioni dei
-segmenti di testo e di dati (inizializzati e BSS); il BSS però non è mai
-salvato sul file, in quanto viene inizializzato a zero al caricamento del
-programma.
+Una disposizione tipica dei vari segmenti (testo, \itindex{heap}
+\textit{heap}, \itindex{stack} \textit{stack}, ecc.) è riportata in
+fig.~\ref{fig:proc_mem_layout}. Usando il comando \cmd{size} su un programma
+se ne può stampare le dimensioni dei segmenti di testo e di dati
+(inizializzati e BSS); si tenga presente però che il BSS non è mai salvato sul
+file che contiene l'eseguibile, dato che viene sempre inizializzato a zero al
+caricamento del programma.
\subsection{Allocazione della memoria per i programmi C}
\label{sec:proc_mem_alloc}
-Il C supporta due tipi di allocazione della memoria, l'allocazione statica è
-quella in cui vanno le variabili globali e le variabili statiche (e viene
-effettuata nel segmento dei dati), lo spazio per queste variabili viene
-allocati all'avvio del programma (come parte delle operazioni svolte da
-\func{exec}) e non viene liberato fino alla sua conclusione.
+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
+mantenuto per tutta la durata del programma. Come accennato queste variabili
+vengono allocate nel \index{segmento!dati} segmento dei dati all'avvio del
+programma (come parte delle operazioni svolte da \func{exec}) e lo spazio da
+loro occupato non viene liberato fino alla sua conclusione.
-L'allocazione automatica è quella che avviene per le cosiddette variabili
-automatiche, cioè gli argomenti delle funzioni o le variabili locali. Lo
-spazio per queste variabili viene allocato nello stack quando viene eseguito
-comando di invocazione della funzione e liberato quando si esce dalla
-medesima.
+L'\textsl{allocazione automatica} è quella che avviene per gli argomenti di
+una funzione e per le sue variabili locali (le cosiddette \textsl{variabili
+ automatiche}), che esistono solo per la durata della funzione. Lo spazio
+per queste variabili viene allocato nello \itindex{stack} \textit{stack} quando
+viene eseguita la funzione e liberato quando si esce dalla medesima.
-Esiste però un terzo tipo di allocazione, che non è prevista dal linguaggio C,
-che è l'allocazione dinamica della memoria, necessaria quando il quantitativo
-di memoria che serve è determinabile solo in corso di esecuzione del
-programma.
+Esiste però un terzo tipo di allocazione, l'\textsl{allocazione dinamica}
+della memoria, che non è prevista direttamente all'interno del linguaggio C,
+ma che è necessaria quando il quantitativo di memoria che serve è
+determinabile solo durante il corso dell'esecuzione del programma.
Il C non consente di usare variabili allocate dinamicamente, non è possibile
cioè definire in fase di programmazione una variabile le cui dimensioni
-possano essere modificate durante l'esecuzione del programma; però le librerie
-del C forniscono una serie opportuna di funzioni per permettere l'allocazione
-dinamica di spazio in memoria (in genere nello heap, usando la system call
-\func{sbrk}), solo che a questo punto detto spazio sarà accessibile solo in
-maniera indiretta attraverso dei puntatori.
+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 \itindex{heap}
+\textit{heap}).
+Le variabili il cui contenuto è allocato in questo modo non potranno essere
+usate direttamente come le altre (quelle nello \itindex{stack}
+\textit{stack}), 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
- \func{free}}
-\label{sec:proc_mem_malloc}
Le funzioni previste dallo standard ANSI C per la gestione della memoria sono
-quattro, i prototipi sono i seguenti:
+quattro: \funcd{malloc}, \funcd{calloc}, \funcd{realloc} e \funcd{free}, i
+loro prototipi sono i seguenti:
\begin{functions}
\headdecl{stdlib.h}
-\funcdecl{void *calloc(size\_t size)}
- Alloca \var{size} byte nello heap. La memoria viene inizializzata a 0.
+\funcdecl{void *calloc(size\_t nmemb, size\_t size)}
+ Alloca nello \textit{heap} un'area di memoria per un vettore di
+ \param{nmemb} membri di \param{size} byte di dimensione. La memoria viene
+ inizializzata a 0.
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}.
+ di successo e \val{NULL} in caso di fallimento, nel qual caso
+ \var{errno} assumerà il valore \errval{ENOMEM}.
\funcdecl{void *malloc(size\_t size)}
- Alloca \var{size} byte nello heap. La memoria non viene inizializzata.
+ Alloca \param{size} byte nello \textit{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}.
+ di successo e \val{NULL} in caso di fallimento, nel qual caso
+ \var{errno} assumerà il valore \errval{ENOMEM}.
\funcdecl{void *realloc(void *ptr, size\_t size)}
- Cambia la dimensione del blocco allocato all'indirizzo \var{ptr}
- portandola a \var{size}.
+ Cambia la dimensione del blocco allocato all'indirizzo \param{ptr}
+ portandola a \param{size}.
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}.
+ di successo e \val{NULL} in caso di fallimento, nel qual caso
+ \var{errno} assumerà il valore \errval{ENOMEM}.
\funcdecl{void free(void *ptr)}
- Disalloca lo spazio di memoria puntato da \var{ptr}.
+ Disalloca lo spazio di memoria puntato da \param{ptr}.
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
-macchine a 32 bit in genere è allineato a multipli di 4 byte e sulle macchine
-a 64 bit a multipli di 8 byte.
-
-In genere su usano le funzioni \func{malloc} e \func{calloc} per allocare
-dinamicamente la memoria necessaria al programma, siccome i puntatori
-ritornati sono di tipo generico non è necessario effettuare un cast per
-assegnarli a puntatori al tipo di variabile per la quale si effettua la
-allocazione.
+Il puntatore ritornato dalle funzioni di allocazione è garantito essere sempre
+allineato correttamente per tutti i tipi di dati; ad esempio sulle macchine a
+32 bit in genere è allineato a multipli di 4 byte e sulle macchine a 64 bit a
+multipli di 8 byte.
+
+In genere si usano le funzioni \func{malloc} e \func{calloc} per allocare
+dinamicamente la quantità di memoria necessaria al programma indicata da
+\param{size},\footnote{queste funzioni presentano un comportamento diverso fra
+ le \acr{glibc} e le \acr{uClib} quando il valore di \param{size} è nullo.
+ Nel primo caso viene comunque restituito un puntatore valido, anche se non è
+ chiaro a cosa esso possa fare riferimento, nel secondo caso viene restituito
+ \val{NULL}. Il comportamento è analogo con \code{realloc(NULL, 0)}.} e
+siccome i puntatori ritornati sono di tipo generico non è necessario
+effettuare un cast per assegnarli a puntatori al tipo di variabile per la
+quale si effettua l'allocazione.
La memoria allocata dinamicamente deve essere esplicitamente rilasciata usando
\func{free}\footnote{le glibc provvedono anche una funzione \func{cfree}
- definita per compatibilità con SunOS, che è deprecata} una volta che non
-sia più necessaria. Questa funzione vuole come parametro un puntatore
+ definita per compatibilità con SunOS, che è deprecata.} una volta che non
+sia più necessaria. Questa funzione vuole come argomento un puntatore
restituito da una precedente chiamata a una qualunque delle funzioni di
-allocazione e che non sia già stato liberato da un'altra chiamata a
-\func{free}, in caso contrario il comportamento della funzione è indefinito.
-
-La funzione \func{realloc} si usa invece per cambiare (in genere aumentare)
-la dimensione di un'area di memoria precedentemente allocata, la funzione
-vuole in ingresso il puntatore restituito dalla precedente chiamata ad una
-\func{malloc} (se è passato un valore \macro{NULL} allora la funzione si
-comporta come \func{malloc}\footnote{questo è vero per Linux e
+allocazione che non sia già stato liberato da un'altra chiamata a \func{free},
+in caso contrario il comportamento della funzione è indefinito.
+
+La funzione \func{realloc} si usa invece per cambiare (in genere aumentare) la
+dimensione di un'area di memoria precedentemente allocata, la funzione vuole
+in ingresso il puntatore restituito dalla precedente chiamata ad una
+\func{malloc} (se è passato un valore \val{NULL} allora la funzione si
+comporta come \func{malloc})\footnote{questo è vero per Linux e
l'implementazione secondo lo standard ANSI C, ma non è vero per alcune
vecchie implementazioni, inoltre alcune versioni delle librerie del C
consentivano di usare \func{realloc} anche per un puntatore liberato con
- \func{free} purché non ci fossero state altre chiamate a funzioni di
- allocazione, questa funzionalità è totalmente deprecata e non è consentita
- 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 aggiunto non viene
-inizializzato.
+ \func{free} purché non ci fossero state nel frattempo altre chiamate a
+ funzioni di allocazione, questa funzionalità è totalmente deprecata e non è
+ consentita 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 aggiunto non viene inizializzato.
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
-stesso puntatore; per evitare questo problema una soluzione di ripiego è
-quella di assegnare sempre a \macro{NULL} ogni puntatore liberato con
-\func{free}, dato che, quando il parametro è un puntatore nullo,
-\func{free} non esegue nessuna operazione.
-
-Linux e le glibc hanno una implementazione delle routine di allocazione che è
+da \func{realloc} può non essere un'estensione di quello che gli si è passato
+in ingresso; per questo si dovrà \emph{sempre} eseguire la riassegnazione di
+\param{ptr} al valore di ritorno della funzione, e reinizializzare o provvedere
+ad un adeguato aggiornamento di tutti gli altri puntatori all'interno del
+blocco di dati ridimensionato.
+
+Un errore abbastanza frequente (specie se si ha a che fare con vettori di
+puntatori) è quello di chiamare \func{free} più di una volta sullo stesso
+puntatore; per evitare questo problema una soluzione di ripiego è quella di
+assegnare sempre a \val{NULL} ogni puntatore liberato con \func{free}, dato
+che, quando l'argomento è un puntatore nullo, \func{free} non esegue nessuna
+operazione.
+
+Le \acr{glibc} hanno un'implementazione delle funzioni di allocazione che è
controllabile dall'utente attraverso alcune variabili di ambiente, in
particolare diventa possibile tracciare questo tipo di errori usando la
-variabile \macro{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 \func{free}; in
-particolare:
-\begin{itemize*}
-\item se la variabile è posta a zero gli errori vengono ignorati.
+variabile di ambiente \val{MALLOC\_CHECK\_} che quando viene definita mette in
+uso una versione meno efficiente delle funzioni suddette, che però è più
+tollerante nei confronti di piccoli errori come quello di chiamate doppie a
+\func{free}. In particolare:
+\begin{itemize}
+\item se la variabile è posta a zero gli errori vengono ignorati;
\item se è posta ad 1 viene stampato un avviso sullo \textit{standard error}
- (vedi \secref{sec:file_std_stream}).
+ (vedi sez.~\ref{sec:file_std_stream});
\item se è posta a 2 viene chiamata \func{abort}, che in genere causa
l'immediata conclusione del programma.
-\end{itemize*}
+\end{itemize}
-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 subroutine causeranno a lungo andare un esaurimento della memoria
-disponibile, con un conseguente crash dell'applicazione che può avvenire in
-qualunque momento, e 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:xxx_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).
-
-
-\subsection{La funzione \func{alloca}}
-\label{sec:proc_mem_alloca}
-
-Una alternativa possibile all'uso di \func{malloc}, che non soffre del tipo
-di problemi di memory leak descritti in precedenza è la funzione
-\func{alloca} che invece che allocare la memoria nello heap usa lo il
-segmento di stack della funzione corrente. La sintassi è identica:
+Il problema più comune e più difficile da risolvere che si incontra con le
+funzioni di allocazione è quando non viene opportunamente liberata la memoria
+non più utilizzata, quello che in inglese viene chiamato \itindex{memory~leak}
+\textit{memory leak}, cioè una \textsl{perdita di memoria}.
+
+Un caso tipico che illustra il problema è quello in cui in una subroutine si
+alloca della memoria per uso locale senza liberarla prima di uscire. La
+memoria resta così allocata fino alla terminazione del processo. Chiamate
+ripetute alla stessa subroutine continueranno ad effettuare altre allocazioni,
+causando a lungo andare un esaurimento della memoria disponibile (e la
+probabile impossibilità di proseguire l'esecuzione del programma).
+
+Il problema è che l'esaurimento della memoria può avvenire in qualunque
+momento, in corrispondenza ad una qualunque chiamata di \func{malloc} che può
+essere in una sezione del codice che non ha alcuna relazione con la subroutine
+che contiene l'errore. Per questo motivo è sempre molto difficile trovare un
+\itindex{memory~leak} \textit{memory leak}.
+
+In C e C++ il problema è particolarmente sentito. In C++, per mezzo della
+programmazione ad oggetti, il problema dei \itindex{memory~leak}
+\textit{memory leak} è notevolmente ridimensionato attraverso l'uso accurato
+di appositi oggetti come gli \textit{smartpointers}. Questo però in genere va
+a scapito delle prestazioni dell'applicazione in esecuzione.
+
+% TODO decidere cosa fare di questo che segue
+% In altri linguaggi come il java e recentemente il C\# il problema non si pone
+% nemmeno perché la gestione della memoria viene fatta totalmente in maniera
+% automatica, ovvero il programmatore non deve minimamente preoccuparsi di
+% liberare la memoria allocata precedentemente quando non serve più, poiché
+% l'infrastruttura del linguaggio gestisce automaticamente la cosiddetta
+% \index{\textit{garbage~collection}} \textit{garbage collection}. In tal caso,
+% attraverso meccanismi simili a quelli del \textit{reference counting}, quando
+% una zona di memoria precedentemente allocata non è più riferita da nessuna
+% parte del codice in esecuzione, può essere deallocata automaticamente in
+% qualunque momento dall'infrastruttura.
+
+% Anche questo va a scapito delle prestazioni dell'applicazione in esecuzione
+% (inoltre le applicazioni sviluppate con tali linguaggi di solito non sono
+% eseguibili compilati, come avviene invece per il C ed il C++, ed è necessaria
+% la presenza di una infrastruttura per la loro interpretazione e pertanto hanno
+% di per sé delle prestazioni più scadenti rispetto alle stesse applicazioni
+% compilate direttamente). Questo comporta però il problema della non
+% predicibilità del momento in cui viene deallocata la memoria precedentemente
+% allocata da un oggetto.
+
+Per limitare l'impatto di questi problemi, e semplificare la ricerca di
+eventuali errori, l'implementazione delle funzioni di allocazione delle
+\acr{glibc} mette a disposizione una serie di funzionalità che permettono di
+tracciare le allocazioni e le disallocazioni, e definisce anche una serie di
+possibili \textit{hook} (\textsl{ganci}) che permettono di sostituire alle
+funzioni di libreria una propria versione (che può essere più o meno
+specializzata per il debugging). Esistono varie librerie che forniscono dei
+sostituti opportuni delle funzioni di allocazione in grado, senza neanche
+ricompilare il programma,\footnote{esempi sono \textit{Dmalloc}
+ \href{http://dmalloc.com/}{\textsf{http://dmalloc.com/}} di Gray Watson ed
+ \textit{Electric Fence} di Bruce Perens.} di eseguire diagnostiche anche
+molto complesse riguardo l'allocazione della memoria. Vedremo alcune delle
+funzionalità di ausilio presenti nelle \acr{glibc} in
+sez.~\ref{sec:proc_memory_adv_management}.
+
+Una possibile alternativa all'uso di \func{malloc}, che non soffre dei
+problemi di \itindex{memory~leak} \textit{memory leak} descritti in
+precedenza, è la funzione \funcd{alloca}, che invece di allocare la memoria
+nello \itindex{heap} \textit{heap} usa il segmento di \itindex{stack}
+\textit{stack} della funzione corrente. La sintassi è identica a quella di
+\func{malloc}, il suo prototipo è:
\begin{prototype}{stdlib.h}{void *alloca(size\_t size)}
- 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 \macro{NULL} in caso di fallimento, nel qual caso
- \var{errno} viene settata a \macro{ENOMEM}.
+ Alloca \param{size} byte nello \textit{stack}.
+
+ \bodydesc{La funzione restituisce il puntatore alla zona di memoria
+ allocata.}
\end{prototype}
-\noindent ma in questo caso non è più necessario liberare la memoria in quanto
-questa viene rilasciata automaticamente al ritorno della funzione.
-
-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 \func{longjmp} 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 i problemi di frammentazione di quest'ultimo che
-comportano inefficienze sia nell'allocazione della memoria che nell'esecuzione
-della funzione.
-
-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. Questo è lo stesso problema potenziale che si può
-avere con le variabili automatiche, su cui torneremo in
-\secref{sec:proc_auto_var}.
-
-
-\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 funzioni sono:
-\begin{functions}
- \headdecl{unistd.h}
- \funcdecl{int brk(void * end\_data\_segment)}
- Sposta la fine del segmento dei dati all'indirizzo specificato da
- \var{end\_data\_segment}.
+
+La funzione alloca la quantità di memoria (non inizializzata) richiesta
+dall'argomento \param{size} nel segmento di \itindex{stack} \textit{stack}
+della funzione chiamante. Con questa funzione non è più necessario liberare
+la memoria allocata (e quindi non esiste un analogo della \func{free}) in
+quanto essa viene rilasciata automaticamente al ritorno della funzione.
+
+Come è evidente questa funzione ha molti vantaggi, anzitutto permette di
+evitare alla radice i problemi di \itindex{memory~leak} \textit{memory leak},
+dato che non serve più la deallocazione esplicita; inoltre la deallocazione
+automatica funziona anche quando si usa \func{longjmp} per uscire da una
+subroutine con un salto non locale da una funzione (vedi
+sez.~\ref{sec:proc_longjmp}).
+
+Un altro vantaggio è che in Linux la funzione è molto più veloce di
+\func{malloc} e non viene sprecato spazio, infatti non è necessario gestire un
+pool di memoria da riservare e si evitano così anche i problemi di
+frammentazione di quest'ultimo, che comportano inefficienze sia
+nell'allocazione della memoria che nell'esecuzione dell'allocazione.
+
+Gli svantaggi sono che questa funzione non è disponibile su tutti gli Unix, e
+non è inserita né nello standard POSIX né in SUSv3 (ma è presente in BSD), il
+suo utilizzo quindi limita la portabilità dei programmi. Inoltre la funzione
+non può essere usata nella lista degli argomenti di una funzione, perché lo
+spazio verrebbe allocato nel mezzo degli stessi.
+
+Inoltre non è chiaramente possibile usare \func{alloca} per allocare memoria
+che deve poi essere usata anche al di fuori della funzione in cui essa viene
+chiamata, dato che all'uscita dalla funzione lo spazio allocato diventerebbe
+libero, e potrebbe essere sovrascritto all'invocazione di nuove funzioni.
+Questo è lo stesso problema che si può avere con le variabili automatiche, su
+cui torneremo in sez.~\ref{sec:proc_auto_var}.
+
+Infine non esiste un modo di sapere se l'allocazione ha avuto successo, la
+funzione infatti viene realizzata inserendo del codice \textit{inline} nel
+programma\footnote{questo comporta anche il fatto che non è possibile
+ sostituirla con una propria versione o modificarne il comportamento
+ collegando il proprio programma con un'altra libreria.} che si limita a
+modificare il puntatore nello \itindex{stack} \textit{stack} e non c'è modo di
+sapere se se ne sono superate le dimensioni, per cui in caso di fallimento
+nell'allocazione il comportamento del programma può risultare indefinito,
+dando luogo ad una \itindex{segment~violation} \textit{segment violation} la
+prima volta che cercherà di accedere alla memoria non effettivamente
+disponibile.
+
+Le due funzioni seguenti\footnote{le due funzioni sono state definite con BSD
+ 4.3, sono marcate obsolete in SUSv2 e non fanno parte delle librerie
+ standard del C e mentre sono state esplicitamente rimosse dallo standard
+ POSIX/1-2001.} vengono utilizzate soltanto quando è necessario effettuare
+direttamente la gestione della memoria associata allo spazio dati di un
+processo, ad esempio qualora si debba implementare la propria versione delle
+funzioni di allocazione della memoria. Per poterle utilizzare è necessario
+definire una della macro di funzionalità (vedi
+sez.~\ref{sec:intro_gcc_glibc_std}) fra \macro{\_BSD\_SOURCE},
+\macro{\_SVID\_SOURCE} e \macro{\_XOPEN\_SOURCE} (ad un valore maggiore o
+ugiale di 500). La prima funzione è \funcd{brk}, ed il suo prototipo è:
+\begin{prototype}{unistd.h}{int brk(void *end\_data\_segment)}
+ Sposta la fine del segmento dei dati.
- La funzione restituisce 0 in caso di successo e -1 in caso di
- fallimento, nel qual caso \var{errno} viene settata a \macro{ENOMEM}.
+ \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
+ fallimento, nel qual caso \var{errno} assumerà il valore \errval{ENOMEM}.}
+\end{prototype}
- \funcdecl{void * sbrk(ptrdiff\_t increment)} Incrementa lo spazio dati di un
- programma di \var{increment}. Un valore zero restituisce l'attuale posizione
- della fine del segmento dati.
+La funzione è un'interfaccia all'omonima system call ed imposta l'indirizzo
+finale del \index{segmento!dati} segmento dati di un processo all'indirizzo
+specificato da \param{end\_data\_segment}. Quest'ultimo deve essere un valore
+ragionevole, ed inoltre la dimensione totale del segmento non deve comunque
+eccedere un eventuale limite (si veda sez.~\ref{sec:sys_resource_limit})
+imposto sulle dimensioni massime dello spazio dati del processo.
+
+Il valore di ritorno della funzione fa riferimento alla versione fornita dalle
+\acr{glibc}, in realtà in Linux la \textit{system call} corrispondente
+restituisce come valore di ritorno il nuovo valore della fine del
+\index{segmento!dati} segmento dati in caso di successo e quello corrente in
+caso di fallimento, è la funzione di interfaccia usata dalle \acr{glibc} che
+fornisce i valori di ritorno appena descritti, questo può non accadere se si
+usano librerie diverse.
+
+Una seconda funzione per la manipolazione diretta delle dimensioni
+\index{segmento!dati} del segmento dati\footnote{in questo caso si tratta
+ soltanto di una funzione di libreria, e non di una system call.} è
+\funcd{sbrk}, ed il suo prototipo è:
+\begin{prototype}{unistd.h}{void *sbrk(ptrdiff\_t increment)}
+ Incrementa la dimensione dello spazio dati.
- La funzione restituisce il puntatore all'inizio della nuova zona di memoria
- allocata in caso di successo e \macro{NULL} in caso di fallimento, nel qual
- caso \macro{errno} viene settata a \macro{ENOMEM}.
-\end{functions}
+ \bodydesc{La funzione restituisce il puntatore all'inizio della nuova zona
+ di memoria allocata in caso di successo e \val{NULL} in caso di
+ fallimento, nel qual caso \var{errno} assumerà il valore \errval{ENOMEM}.}
+\end{prototype}
+\noindent la funzione incrementa la dimensione lo spazio dati di un programma
+di \param{increment} byte, restituendo il nuovo indirizzo finale dello stesso.
+Un valore nullo permette di ottenere l'attuale posizione della fine del
+\index{segmento!dati} segmento dati.
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
-\func{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}
+per i programmi normali è sempre opportuno usare le funzioni di allocazione
+standard descritte in precedenza, che sono costruite su di esse.
\subsection{Il controllo della memoria virtuale}
\label{sec:proc_mem_lock}
-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.
+\index{memoria~virtuale|(}
+
+Come spiegato in sez.~\ref{sec:proc_mem_gen} il kernel gestisce la memoria
+virtuale 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 si attivi il meccanismo dello \textit{swapping}, in generale i
-motivi per cui si possono avere queste necessità sono sostanzialmente due:
+meccanismo della \index{paginazione} paginazione riporta in RAM, ed in maniera
+trasparente, tutte le pagine che gli occorrono; esistono però esigenze
+particolari in cui non si vuole che questo meccanismo si attivi. In generale i
+motivi per cui si possono avere di queste necessità sono 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.
-
+\item \textsl{La velocità}. Il processo della \index{paginazione} paginazione
+ è trasparente solo se il programma in esecuzione non è sensibile al tempo
+ che occorre a riportare la pagina in memoria; per questo motivo processi
+ critici che hanno esigenze di tempo reale o tolleranze critiche nelle
+ 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}).
+ anche un aumento delle priorità in esecuzione del processo (vedi
+ sez.~\ref{sec:proc_real_time}).
-\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 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.
+\item \textsl{La sicurezza}. Se si hanno password o chiavi segrete in chiaro
+ in memoria queste possono essere portate su disco dal meccanismo della
+ \index{paginazione} paginazione. Questo rende più lungo il periodo di tempo
+ in cui detti segreti sono presenti in chiaro e più complessa la loro
+ cancellazione (un processo può cancellare la memoria su cui scrive le sue
+ variabili, ma non può toccare lo spazio disco su cui una pagina di memoria
+ 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
-un processo è chiamato \textit{memory locking} (blocco della memoria), il
-blocco è sempre associato alle pagine della memoria virtuale del processo, non
-con il segmento reale di RAM su cui essa viene mantenuta.
+Per ottenere informazioni sulle modalità in cui un programma sta usando la
+memoria virtuale è disponibile una apposita funzione, \funcd{mincore}, che
+però non è standardizzata da POSIX e pertanto non è disponibile su tutte le
+versioni di kernel unix-like;\footnote{nel caso di Linux devono essere
+ comunque definite le macro \macro{\_BSD\_SOURCE} e \macro{\_SVID\_SOURCE}.}
+il suo prototipo è:
+\begin{functions}
+ \headdecl{unistd.h}
+ \headdecl{sys/mman.h}
+
+ \funcdecl{int mincore(void *addr, size\_t length, unsigned char *vec)}
+ Ritorna lo stato delle pagine di memoria occupate da un processo.
+
+ \bodydesc{La funzione ritorna 0 in caso di successo e $-1$ in caso di
+ errore, nel qual caso \var{errno} assumerà uno dei valori seguenti:
+ \begin{errlist}
+ \item[\errcode{ENOMEM}] o \param{addr} + \param{lenght} eccede la dimensione
+ della memoria usata dal processo o l'intervallo di indirizzi specificato
+ non è mappato.
+ \item[\errcode{EINVAL}] \param{addr} non è un multiplo delle dimensioni di
+ una pagina.
+ \item[\errcode{EFAULT}] \param{vec} punta ad un indirizzo non valido.
+ \item[\errcode{EAGAIN}] il kernel è temporaneamente non in grado di fornire
+ una risposta.
+ \end{errlist}
+}
+\end{functions}
-La regola è che se un segmento di RAM fa da supporto ad almeno una pagina
-bloccata allora esso viene escluso dal meccanismo della paginazione. I blocchi
-non si accumulano, se si blocca due volte la stessa pagina non è necessario
-sbloccarla due volte, una pagina o è bloccata o no.
+La funzione permette di ottenere le informazioni sullo stato della mappatura
+della memoria per il processo chiamante, specificando l'intervallo da
+esaminare con l'indirizzo iniziale (indicato con l'argomento \param{addr}) e
+la lunghezza (indicata con l'argomento \param{length}). L'indirizzo iniziale
+deve essere un multiplo delle dimensioni di una pagina, mentre la lunghezza
+può essere qualunque, fintanto che si resta nello spazio di indirizzi del
+processo,\footnote{in caso contrario si avrà un errore di \errcode{ENOMEM};
+ fino al kernel 2.6.11 in questo caso veniva invece restituito
+ \errcode{EINVAL}, in considerazione che il caso più comune in cui si
+ verifica questo errore è quando si usa per sbaglio un valore negativo
+ di \param{length}, che nel caso verrebbe interpretato come un intero
+ positivo di grandi dimensioni.} ma il risultato verrà comunque fornito per
+l'intervallo compreso fino al multiplo successivo.
+
+I risultati della funzione vengono forniti nel vettore puntato da \param{vec},
+che deve essere allocato preventivamente e deve essere di dimensione
+sufficiente a contenere tanti byte quante sono le pagine contenute
+nell'intervallo di indirizzi specificato.\footnote{la dimensione cioè deve
+ essere almeno pari a \code{(length+PAGE\_SIZE-1)/PAGE\_SIZE}. } Al ritorno
+della funzione il bit meno significativo di ciascun byte del vettore sarà
+acceso se la pagina di memoria corrispondente è al momento residente in
+memoria, o cancellato altrimenti. Il comportamento sugli altri bit è
+indefinito, essendo questi al momento riservati per usi futuri. Per questo
+motivo in genere è comunque opportuno inizializzare a zero il contenuto del
+vettore, così che le pagine attualmente residenti in memoria saranno indicata
+da un valore non nullo del byte corrispondente.
+
+Dato che lo stato della memoria di un processo può cambiare continuamente, il
+risultato di \func{mincore} è assolutamente provvisorio e lo stato delle
+pagine potrebbe essere già cambiato al ritorno stesso della funzione, a meno
+che, come vedremo ora, non si sia attivato il meccanismo che forza il
+mantenimento di una pagina sulla memoria.
+
+\itindbeg{memory~locking}
+
+Il meccanismo che previene la \index{paginazione} paginazione di parte della
+memoria virtuale di un processo è chiamato \textit{memory locking} (o
+\textsl{blocco della memoria}). Il blocco è sempre associato alle pagine della
+memoria virtuale del processo, e non al segmento reale di RAM su cui essa
+viene mantenuta. La regola è che se un segmento di RAM fa da supporto ad
+almeno una pagina bloccata allora esso viene escluso dal meccanismo della
+\index{paginazione} paginazione. I blocchi non si accumulano, se si blocca due
+volte la stessa pagina non è necessario sbloccarla due volte, una pagina o è
+bloccata oppure no.
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 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 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.
-
+tutti i suoi \textit{memory lock}. Infine i \textit{memory lock} non sono
+ereditati dai processi figli,\footnote{ma siccome Linux usa il
+ \itindex{copy~on~write} \textit{copy on write} (vedi
+ sez.~\ref{sec:proc_fork}) 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 del \textit{memory lock} del padre.} e
+vengono automaticamente rimossi se si pone in esecuzione un altro programma
+con \func{exec} (vedi sez.~\ref{sec:proc_exec}).
+
+Siccome la richiesta di un \textit{memory lock} da parte di un processo riduce
+la memoria fisica disponibile nel sistema, questo ha un evidente impatto su
+tutti gli altri processi, per cui fino al kernel 2.6.9 solo un processo con i
+privilegi opportuni (la \itindex{capabilities} \textit{capability}
+\const{CAP\_IPC\_LOCK}, vedi sez.~\ref{sec:proc_capabilities}) aveva la
+capacità di bloccare una pagina.
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
+essere bloccata e al totale di memoria fisica che si può dedicare a questo, lo
+standard POSIX.1 richiede che sia definita in \file{unistd.h} la macro
\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:
+\textit{memory locking}. Inoltre in alcuni sistemi è definita la costante
+\const{PAGE\_SIZE} in \file{limits.h} per indicare la dimensione di una pagina
+in byte.\footnote{con Linux questo non avviene e si deve ricorrere alla
+ funzione \func{getpagesize}, vedi sez.~\ref{sec:sys_memory_res}.}
+
+A partire dal kernel 2.6.9 anche un processo normale può bloccare la propria
+memoria\footnote{la funzionalità è stata introdotta per non essere costretti a
+ dare privilegi eccessivi a programmi di crittografia, che necessitano di
+ questa funzionalità, ma che devono essere usati da utenti normali.} ma
+mentre un processo privilegiato non ha limiti sulla quantità di memoria che
+può bloccare, un processo normale è soggetto al limite della risorsa
+\const{RLIMIT\_MEMLOCK} (vedi sez.~\ref{sec:sys_resource_limit}). In generale
+poi ogni processo può sbloccare le pagine relative alla propria memoria, se
+però diversi processi bloccano la stessa pagina questa resterà bloccata
+fintanto che ci sarà almeno un processo che la blocca.
+
+Le funzioni per bloccare e sbloccare la \index{paginazione} paginazione di
+singole sezioni di memoria sono \funcd{mlock} e \funcd{munlock}; i loro
+prototipi sono:
\begin{functions}
\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.
+ Blocca la paginazione su un intervallo di memoria.
\funcdecl{int munlock(const void *addr, size\_t len)}
- Sblocca l'intervallo di memoria da \var{addr} per \var{len} byte.
-
+ Rimuove il blocco della paginazione su un intervallo di memoria.
- \bodydesc{Entrambe le funzioni ritornano 0 in caso di successo e -1 in
- caso di errore, nel qual caso \var{errno} è settata ad uno dei
+ \bodydesc{Entrambe le funzioni ritornano 0 in caso di successo e $-1$ in
+ caso di errore, nel qual caso \var{errno} assumerà uno dei
valori seguenti:
\begin{errlist}
- \item[\macro{ENOMEM}] alcuni indirizzi dell'intervallo specificato non
+ \item[\errcode{ENOMEM}] alcuni indirizzi dell'intervallo specificato non
corrispondono allo spazio di indirizzi del processo o si è ecceduto
il numero massimo consentito di pagine bloccate.
- \item[\macro{EINVAL}] \var{len} non è un valore positivo.
+ \item[\errcode{EINVAL}] \param{len} non è un valore positivo.
+ \item[\errcode{EPERM}] con un kernel successivo al 2.6.9 il processo non è
+ privilegiato e si un limite nullo per \const{RLIMIT\_MEMLOCK}.
\end{errlist}
- e, per \func{mlock}, anche \macro{EPERM} quando il processo non ha i
+ e, per \func{mlock}, anche \errval{EPERM} quando il processo non ha i
privilegi richiesti per l'operazione.}
\end{functions}
-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:
-
+Le due funzioni permettono rispettivamente di bloccare e sbloccare la
+\index{paginazione} paginazione per l'intervallo di memoria specificato dagli
+argomenti, che ne indicano nell'ordine l'indirizzo iniziale e la lunghezza.
+Tutte le pagine che contengono una parte dell'intervallo bloccato sono
+mantenute in RAM per tutta la durata del blocco.\footnote{con altri kernel si
+ può ottenere un errore di \errcode{EINVAL} se \param{addr} non è un multiplo
+ della dimensione delle pagine di memoria.}
+
+Altre due funzioni, \funcd{mlockall} e \funcd{munlockall}, consentono di
+bloccare genericamente la \index{paginazione} paginazione per l'intero spazio
+di indirizzi di un processo. I prototipi di queste funzioni sono:
\begin{functions}
\headdecl{sys/mman.h}
\funcdecl{int munlockall(void)}
Sblocca la paginazione per lo spazio di indirizzi del processo corrente.
- \bodydesc{Codici di ritorno ed errori sono gli stessi di \func{mlock}
- e \func{munlock}.}
+ \bodydesc{Codici di ritorno ed errori sono gli stessi di \func{mlock} e
+ \func{munlock}, con un kernel successivo al 2.6.9 l'uso di
+ \func{munlockall} senza la \itindex{capabilities} \textit{capability}
+\const{CAP\_IPC\_LOCK} genera un errore di \errcode{EPERM}.}
\end{functions}
-Il parametro \var{flags} di \func{mlockall} permette di controllarne il
+L'argomento \param{flags} di \func{mlockall} permette di controllarne il
comportamento; esso può essere specificato come l'OR aritmetico delle due
costanti:
\begin{basedescript}{\desclabelwidth{2.5cm}}
-\item[\macro{MCL\_CURRENT}] blocca tutte le pagine correntemente mappate nello
+\item[\const{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
+\item[\const{MCL\_FUTURE}] blocca tutte le pagine che verranno mappate nello
spazio di indirizzi del processo.
\end{basedescript}
-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.
+Con \func{mlockall} si possono bloccare tutte le pagine mappate nello spazio
+di indirizzi del processo, sia che comprendano il \index{segmento!dati}
+\index{segmento!testo} segmento di testo, di dati, lo \itindex{stack}
+\textit{stack}, lo \itindex{heap} \textit{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
+\index{sezione~critica} sezione critica deve provvedere a riservare memoria
+sufficiente prima dell'ingresso, per scongiurare l'occorrenza di un eventuale
+\itindex{page~fault} \textit{page fault} causato dal meccanismo di
+\itindex{copy~on~write} \textit{copy on write}. Infatti se nella
+\index{sezione~critica} sezione critica si va ad utilizzare memoria che non è
+ancora stata riportata in RAM si potrebbe avere un \itindex{page~fault}
+\textit{page fault} durante l'esecuzione della stessa, con conseguente
+rallentamento (probabilmente inaccettabile) dei tempi di esecuzione.
+
+In genere si ovvia a questa problematica chiamando una funzione che ha
+allocato una quantità sufficientemente ampia di variabili automatiche, in modo
+che esse vengano mappate in RAM dallo \itindex{stack} \textit{stack}, dopo di
+che, per essere sicuri che esse siano state effettivamente portate in memoria,
+ci si scrive sopra.
+
+\itindend{memory~locking}
+
+\index{memoria~virtuale|)}
+
+
+\subsection{Gestione avanzata dell'allocazione della memoria}
+\label{sec:proc_memory_adv_management}
+
+La trattazione delle funzioni di allocazione di sez.~\ref{sec:proc_mem_alloc}
+si è limitata a coprire le esigenze generiche di un programma, in cui non si
+hanno dei requisiti specifici e si lascia il controllo delle modalità di
+allocazione alle funzioni di libreria. Tuttavia esistono una serie di casi in
+cui può essere necessario avere un controllo più dettagliato delle modalità
+con cui la memoria viene allocata; nel qual caso potranno venire in aiuto le
+funzioni trattate in questa sezione.
+
+Le prime funzioni che tratteremo sono quelle che consentono di richiedere di
+allocare un blocco di memoria ``\textsl{allineato}'' ad un multiplo una certa
+dimensione. Questo tipo di esigenza emerge usualmente quando si devono
+allocare dei buffer da utilizzare per eseguire dell'I/O diretto su dispositivi
+a blocchi. In questo caso infatti il trasferimento di dati viene eseguito per
+blocchi di dimensione fissa, ed è richiesto che l'indirizzo di partenza del
+buffer sia un multiplo intero di questa dimensione, usualmente 512 byte. In
+tal caso l'uso di \func{malloc} non è sufficiente, ed occorre utilizzare una
+funzione specifica.
+
+Tradizionalmente per rispondere a questa esigenza sono state crate due
+funzioni diverse, \funcd{memalign} e \funcd{valloc}, oggi obsolete; i
+rispettivi prototipi sono:
+\begin{functions}
+ \headdecl{malloc.h}
-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.
+ \funcdecl{void *valloc(size\_t size)} Alloca un blocco di memoria allineato
+ alla dimensione di una pagina di memoria.
+ \funcdecl{void *memalign(size\_t boundary, size\_t size)}
+ Alloca un blocco di memoria allineato ad un multiplo di \param{boundary}.
+
+ \bodydesc{Entrambe le funzioni ritornano un puntatore al blocco di memoria
+ allocato in caso di successo e \val{NULL} in caso di errore, nel qual
+ caso \var{errno} assumerà uno dei valori seguenti:
+ \begin{errlist}
+ \item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'allocazione.
+ \item[\errcode{EINVAL}] \param{boundary} non è multiplo di due.
+ \end{errlist}
+}
+\end{functions}
-\section{La gestione di parametri e opzioni}
+Le funzioni restituiscono il puntatore al buffer di memoria allocata, che per
+\func{memalign} sarà un multiplo di \param{boundary} mentre per \func{valloc}
+un multiplo della dimensione di una pagina di memoria. Nel caso della versione
+fornita dalle \acr{glibc} la memoria allocata con queste funzioni deve essere
+liberata con \func{free}, cosa che non è detto accada con altre
+implementazioni.
+
+Nessuna delle due funzioni ha una chiara standardizzazione (nessuna delle due
+compare in POSIX.1), ed inoltre ci sono indicazioni discordi sui file che ne
+contengono la definizione;\footnote{secondo SUSv2 \func{valloc} è definita in
+ \texttt{stdlib.h}, mentre sia le \acr{glibc} che le precedenti \acr{libc4} e
+ \acr{lic5} la dichiarano in \texttt{malloc.h}, lo stesso vale per
+ \func{memalign} che in alcuni sistemi è dichiarata in \texttt{stdlib.h}.}
+per questo motivo il loro uso è sconsigliato, essendo state sostituite dalla
+nuova \funcd{posix\_memalign}, che è stata standardizzata in POSIX.1d; il suo
+prototipo è:
+\begin{prototype}{stdlib.h}{posix\_memalign(void **memptr, size\_t alignment,
+ size\_t size) }
+ Alloca un buffer di memoria allineato ad un multiplo di \param{alignment}.
+
+ \bodydesc{La funzione restituisce 0 in caso di successo e \val{NULL} in caso
+ di fallimento, o uno dei due codici di errore \errcode{ENOMEM} o
+ \errcode{EINVAL}; \var{errno} non viene impostata.}
+\end{prototype}
+
+La funzione restituisce il puntatore al buffer allocato all'indirizzo indicato
+da \param{memptr}. La funzione fallisce nelle stesse condizioni delle due
+funzioni precedenti, ma a differenza di \func{memalign} restituisce un codice
+di errore \errcode{EINVAL} anche se \param{alignment} non è un multiplo della
+la dimensione di \code{sizeof(void *)}. Come per le precedenti la memoria
+allocata con \func{posix\_memalign} può essere disallocata con
+\func{free}.\footnote{che in caso questo caso è quanto richiesto dallo
+ standard.}
+
+Un secondo caso in cui risulta estremamente utile poter avere un maggior
+controllo delle modalità di allocazione della memoria è quello in cui cercano
+errori di programmazione. Esempi di questi errori sono chiamate doppie alla
+funzione \func{free} con lo stesso puntatore, o i cosiddetti
+\itindex{buffer~overrun} \textit{buffer overrun}, cioè le scritture su un buffer
+oltre le dimensioni della sua allocazione,\footnote{entrambe queste operazioni
+ causano in genere la corruzione dei dati di controllo delle funzioni di
+ allocazione, che vengono anch'essi mantenuti nello \itindex{heap}
+ \textit{heap} per tenere traccia delle zone di memoria allocata.} o i
+classici \itindex{memory~leak} \textit{memory leak}.
+
+Una prima funzionalità di ausilio nella ricerca di questi errori viene fornita
+dalla \acr{glibc} tramite l'uso della variabile di ambiente (vedi
+sez.~\ref{sec:proc_environ}) \var{MALLOC\_CHECK\_}. Quando questa viene
+definita al posto della versione ordinaria delle funzioni di allocazione
+(\func{malloc}, \func{calloc}, \func{realloc}, e \func{free}) viene usata una
+versione meno efficiente ma in grado di rilevare (e tollerare) alcuni degli
+errori più semplici, come le doppie chiamate a \func{free} o i
+\itindex{buffer~overrun} \textit{buffer overrun} di un byte.\footnote{uno
+ degli errori più comuni, causato ad esempio dalla scrittura di una stringa
+ di dimensione pari a quella del buffer, in cui ci si dimentica dello zero di
+ terminazione finale.}
+
+In questo caso a seconda del valore assegnato a \var{MALLOC\_CHECK\_} si
+avranno diversi comportamenti: con 0 l'errore sarà ignorato, con 1 verrà
+stampato un messaggio sullo \textit{standard error} (vedi
+sez.~\ref{sec:file_std_stream}), con 2 verrà invocata la funzione \func{abort}
+(vedi sez.~\ref{sec:sig_alarm_abort}) che termina il programma, con 3 viene
+sia stampato il messaggio d'errore che abortito il programma. In genere è
+opportuno definire la variabile ad un valore diverso da zero che consente di
+rilevare un errore nel momento in cui avviene.
+
+% TODO: trattare le altre funzionalità avanzate di \func{malloc}, mallopt,
+% mtrace, muntrace, mcheck, mallinfo e gli hook
+% con le glibc 2.10 c'è pure malloc_info a sostituire mallinfo
+
+\section{Argomenti, opzioni ed ambiente di un processo}
\label{sec:proc_options}
-Il passaggio dei parametri e delle variabili di ambiente dalla riga di comando
-al singolo programma quando viene lanciato è effettuato attraverso le
-variabili \var{argc}, \var{argv} che vengono passate al programma
-come argomenti della funzione principale.
+Tutti i programmi hanno la possibilità di ricevere argomenti e opzioni quando
+vengono lanciati. Il passaggio degli argomenti è effettuato attraverso gli
+argomenti \param{argc} e \param{argv} della funzione \func{main}, che vengono
+passati al programma dalla shell (o dal processo che esegue la \func{exec},
+secondo le modalità che vedremo in sez.~\ref{sec:proc_exec}) quando questo
+viene messo in esecuzione.
+
+Oltre al passaggio degli argomenti, un'altra modalità che permette di passare
+delle informazioni che modifichino il comportamento di un programma è quello
+dell'uso del cosiddetto \textit{environment} (cioè l'uso delle
+\textsl{variabili di ambiente}). In questa sezione esamineremo le funzioni che
+permettono di gestire argomenti ed opzioni, e quelle che consentono di
+manipolare ed utilizzare le variabili di ambiente.
+
-\subsection{Il formato dei parametri}
+\subsection{Il formato degli argomenti}
\label{sec:proc_par_format}
-In genere passaggio dei parametri al programma viene effettuato dalla shell,
-che si incarica di leggere la linea di comando e di effettuarne la scansione
-(il cosiddetto \textit{parsing}) per individuare le parole che la compongono,
-ciascuna delle quali viene considerata un parametro; di default per
-individuare le parole viene usato come separatore lo spazio (comportamento
-modificabile attraverso il settaggio della variabile di ambiente IFS).
-
-Nella scansione viene costruito il vettore di puntatori \var{argv} inserendo
-in successione il puntatore alla stringa costituente l'$n$-simo parametro; la
-variabile \var{argc} viene inizializzata al numero di parametri trovati, in
-questo modo il primo parametro è sempre il nome del programma (vedi \nfig).
+
+In genere il passaggio degli argomenti ad un programma viene effettuato dalla
+shell, che si incarica di leggere la linea di comando e di effettuarne la
+scansione (il cosiddetto \textit{parsing}) per individuare le parole che la
+compongono, ciascuna delle quali viene considerata un argomento. Di norma per
+individuare le parole che andranno a costituire la lista degli argomenti viene
+usato come carattere di separazione lo spazio o il tabulatore, ma la cosa
+dipende ovviamente dalle modalità con cui si effettua il lancio.
+
+\begin{figure}[htb]
+ \centering
+ \includegraphics[width=13cm]{img/argv_argc}
+ \caption{Esempio dei valori di \param{argv} e \param{argc} generati nella
+ scansione di una riga di comando.}
+ \label{fig:proc_argv_argc}
+\end{figure}
+
+Indipendentemente da come viene eseguita, il risultato della scansione deve
+essere la costruzione del vettore di puntatori \param{argv} in cui si devono
+inserire in successione i puntatori alle stringhe costituenti i vari
+argomenti, e della variabile \param{argc} che deve essere inizializzata al
+numero di argomenti trovati. Nel caso della shell questo comporta che il primo
+argomento sia sempre il nome del programma; un esempio di questo meccanismo è
+mostrato in fig.~\ref{fig:proc_argv_argc}.
+
\subsection{La gestione delle opzioni}
\label{sec:proc_opt_handling}
-In generale un programma unix riceve da linea di comando sia gli argomenti che
+In generale un programma Unix riceve da linea di comando sia gli argomenti che
le opzioni, queste ultime sono standardizzate per essere riconosciute come
-tali: un elemento di \var{argv} che inizia con \texttt{-} e che non sia un
-singolo \texttt{-} o \texttt{--} viene considerato un'opzione. In in genere
-le opzioni sono costituite da una lettera preceduta dal meno e possono avere o
-no un parametro associato; un comando tipico può essere cioè qualcosa del
-tipo:
-\begin{verbatim}
-touch -r riferimento.txt -m questofile.txt
-\end{verbatim}
-ed in questo caso le opzioni sono \texttt{m} ed \texttt{r}.
+tali: un elemento di \param{argv} che inizia con il carattere \texttt{'-'} e
+che non sia un singolo \texttt{'-'} o un \texttt{'-{}-'} viene considerato
+un'opzione. In genere le opzioni sono costituite da una lettera singola
+(preceduta dal carattere \cmd{'-'}) e possono avere o no un parametro
+associato; un comando tipico può essere quello mostrato in
+fig.~\ref{fig:proc_argv_argc}. In quel caso le opzioni sono \cmd{-r} e \cmd{-m}
+e la prima vuole un parametro mentre la seconda no (\cmd{questofile.txt} è un
+argomento del programma, non un parametro di \cmd{-m}).
Per gestire le opzioni all'interno dei argomenti a linea di comando passati in
-\func{argv} le librerie standard del C forniscono la funzione \func{getopt}
+\param{argv} le librerie standard del C forniscono la funzione \funcd{getopt},
che ha il seguente prototipo:
\begin{prototype}{unistd.h}
-{int getopt(int argc, char * const argv[], const char * optstring)}
+{int getopt(int argc, char *const argv[], const char *optstring)}
Esegue il parsing degli argomenti passati da linea di comando
-riconoscendo le possibili opzioni segnalate con \var{optstring}.
+riconoscendo le possibili opzioni segnalate con \param{optstring}.
\bodydesc{Ritorna il carattere che segue l'opzione, \cmd{':'} se manca un
- parametro all'opzione, \cmd{'?'} se l'opzione è sconosciuta, e -1 se non
+ parametro all'opzione, \cmd{'?'} se l'opzione è sconosciuta, e $-1$ se non
esistono altre opzioni.}
\end{prototype}
-Questa funzione prende come argomenti le due variabili \var{argc} e \var{argv}
-passate a \func{main} (vedi \secref{sec:proc_main}) ed una stringa che indica
-quali sono le opzioni valide; la funzione effettua la scansione della lista
-degli argomenti ricercando ogni stringa che comincia con \cmd{-} e ritorna ogni
-volta che trova una opzione valida.
+Questa funzione prende come argomenti le due variabili \param{argc} e
+\param{argv} passate a \func{main} ed una stringa che indica quali sono le
+opzioni valide; la funzione effettua la scansione della lista degli argomenti
+ricercando ogni stringa che comincia con \cmd{-} e ritorna ogni volta che
+trova un'opzione valida.
-La stringa \var{optstring} indica quali sono le opzioni riconosciute ed è
+La stringa \param{optstring} indica quali sono le opzioni riconosciute ed è
costituita da tutti i caratteri usati per identificare le singole opzioni, se
l'opzione ha un parametro al carattere deve essere fatto seguire un segno di
-due punti \var{':'}, nel caso appena accennato ad esempio la stringa di
-opzioni sarebbe \var{"r:m"}.
+due punti \texttt{':'}; nel caso di fig.~\ref{fig:proc_argv_argc} ad esempio la
+stringa di opzioni avrebbe dovuto contenere \texttt{"r:m"}.
La modalità di uso di \func{getopt} è pertanto quella di chiamare più volte la
-funzione all'interno di un ciclo fintanto che essa non ritorna il valore -1
+funzione all'interno di un ciclo, fintanto che essa non ritorna il valore $-1$
che indica che non ci sono più opzioni. Nel caso si incontri un'opzione non
-dichiarata in \var{optstring} viene ritornato il carattere \texttt{'?'}
-mentre se un opzione che lo richiede non è seguita da un parametro viene
+dichiarata in \param{optstring} viene ritornato il carattere \texttt{'?'}
+mentre se un'opzione che lo richiede non è seguita da un parametro viene
ritornato il carattere \texttt{':'}, infine se viene incontrato il valore
-\cmd{--} la scansione viene considerata conclusa, anche se vi sono altri
-elementi di \var{argv} che cominciano con il carattere \texttt{'-'}.
+\texttt{'-{}-'} la scansione viene considerata conclusa, anche se vi sono altri
+elementi di \param{argv} che cominciano con il carattere \texttt{'-'}.
\begin{figure}[htb]
- \footnotesize
- \begin{lstlisting}{}
- opterr = 0; /* don't want writing to stderr */
- while ( (i = getopt(argc, argv, "hp:c:e:")) != -1) {
- switch (i) {
- /*
- * Handling options
- */
- case 'h': /* help option */
- printf("Wrong -h option use\n");
- usage();
- return -1;
- break;
- case 'c': /* take wait time for childen */
- wait_child = strtol(optarg, NULL, 10); /* convert input */
- break;
- case 'p': /* take wait time for childen */
- wait_parent = strtol(optarg, NULL, 10); /* convert input */
- break;
- case 'e': /* take wait before parent exit */
- wait_end = strtol(optarg, NULL, 10); /* convert input */
- break;
- case '?': /* unrecognized options */
- printf("Unrecognized options -%c\n",optopt);
- usage();
- default: /* should not reached */
- usage();
- }
- }
- debug("Optind %d, argc %d\n",optind,argc);
- \end{lstlisting}
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/option_code.c}
+ \end{minipage}
+ \normalsize
\caption{Esempio di codice per la gestione delle opzioni.}
\label{fig:proc_options_code}
\end{figure}
Quando la funzione trova un'opzione essa ritorna il valore numerico del
-carattere, in questo modo si possono prendere le azioni relative usando uno
-\func{switch}; la funzione inizializza inoltre alcune variabili globali:
+carattere, in questo modo si possono eseguire azioni specifiche usando uno
+\code{switch}; \func{getopt} inoltre inizializza alcune variabili globali:
\begin{itemize*}
-\item \var{char * optarg} contiene il puntatore alla stringa parametro
+\item \var{char *optarg} contiene il puntatore alla stringa parametro
dell'opzione.
\item \var{int optind} alla fine della scansione restituisce l'indice del
- primo elemento di \var{argv} che non è un'opzione.
+ primo elemento di \param{argv} che non è un'opzione.
\item \var{int opterr} previene, se posto a zero, la stampa di un messaggio
di errore in caso di riconoscimento di opzioni non definite.
\item \var{int optopt} contiene il carattere dell'opzione non riconosciuta.
\end{itemize*}
-In \figref{fig:proc_options_code} è mostrata la sezione del programma
+In fig.~\ref{fig:proc_options_code} è mostrata la sezione del programma
\file{ForkTest.c} (che useremo nel prossimo capitolo per effettuare dei test
sulla creazione dei processi) deputata alla decodifica delle opzioni a riga di
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
-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
- 15-17} e \texttt{\small 18-20}). Completato il ciclo troveremo in
-\var{optind} l'indice in \var{argv[]} del primo degli argomenti a linea di
-comando restanti.
-
-Normalmente \func{getopt} compie una permutazione degli elementi di \var{argv}
-così che alla fine della scansione gli elementi che non sono opzioni sono
-spostati in coda al vettore. Oltre a questa esistono altre due modalità di
-gestire gli elementi di \var{argv}; se \var{optstring} inizia con il carattere
-\texttt{'+'} (o è settata la variabile di ambiente \macro{POSIXLY\_CORRECT})
-la scansione viene fermata non appena si incontra un elemento che non è
-un'opzione. L'ultima modalità, usata quando un programma può gestire la
-mescolanza fra opzioni e argomenti, ma se li aspetta in un ordine definito, si
-attiva quando \var{optstring} inizia con il carattere \texttt{'-'}. In questo
-caso ogni elemento che non è un'opzione viene considerato comunque un'opzione
-e associato ad un valore di ritorno pari ad 1, questo permette di identificare
-gli elementi che non sono opzioni, ma non effettua il riordinamento del
-vettore \var{argv}.
-
-
-\subsection{Opzioni in formato esteso}
-\label{sec:proc_opt_extended}
-
-Un'estensione di questo schema è costituito dalle cosiddette
-\textit{long-options} espresse nella forma \cmd{--option=parameter}, anche la
-gestione di queste ultime è stata standardizzata attraverso l'uso di una
-versione estesa di \func{getopt}.
-
-(NdA: da finire).
+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 opzioni
+possibili si è poi provveduto ad un'azione opportuna, 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 15-17} e
+\texttt{\small 18-20}). Completato il ciclo troveremo in \var{optind} l'indice
+in \code{argv[]} del primo degli argomenti rimanenti nella linea di comando.
+
+Normalmente \func{getopt} compie una permutazione degli elementi di
+\param{argv} cosicché alla fine della scansione gli elementi che non sono
+opzioni sono spostati in coda al vettore. Oltre a questa esistono altre due
+modalità di gestire gli elementi di \param{argv}; se \param{optstring} inizia
+con il carattere \texttt{'+'} (o è impostata la variabile di ambiente
+\macro{POSIXLY\_CORRECT}) la scansione viene fermata non appena si incontra un
+elemento che non è un'opzione. L'ultima modalità, usata quando un programma
+può gestire la mescolanza fra opzioni e argomenti, ma se li aspetta in un
+ordine definito, si attiva quando \param{optstring} inizia con il carattere
+\texttt{'-'}. In questo caso ogni elemento che non è un'opzione viene
+considerato comunque un'opzione e associato ad un valore di ritorno pari ad 1,
+questo permette di identificare gli elementi che non sono opzioni, ma non
+effettua il riordinamento del vettore \param{argv}.
\subsection{Le variabili di ambiente}
\label{sec:proc_environ}
Oltre agli argomenti passati a linea di comando ogni processo riceve dal
-sistema un \textsl{ambiente}, nella forma di una lista di variabili
-(\textit{environment list}) messa a disposizione dal processo, e costruita
+sistema un \textsl{ambiente}, nella forma di una lista di variabili (detta
+\textit{environment list}) messa a disposizione dal processo, e costruita
nella chiamata alla funzione \func{exec} quando questo viene lanciato.
-Come per la lista dei parametri anche questa lista è un array di puntatori a
-caratteri, ciascuno dei quali punta ad una stringa (terminata da un
-\macro{NULL}). A differenza di \var{argv[]} però in questo caso non si ha una
-lunghezza dell'array data da un equivalente di \var{argc}, ma la lista è
+Come per la lista degli argomenti anche questa lista è un vettore di puntatori
+a caratteri, ciascuno dei quali punta ad una stringa, terminata da un
+\val{NULL}. A differenza di \code{argv[]} in questo caso non si ha una
+lunghezza del vettore data da un equivalente di \param{argc}, ma la lista è
terminata da un puntatore nullo.
L'indirizzo della lista delle variabili di ambiente è passato attraverso la
variabile globale \var{environ}, a cui si può accedere attraverso una semplice
dichiarazione del tipo:
-\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
-extern char ** environ;
-\end{lstlisting}
-un esempio del contenuto dell'ambiente, in si è riportato un estratto delle
-variabili che normalmente sono definite dal sistema, è riportato in \nfig.
+\includecodesnip{listati/env_ptr.c}
+un esempio della struttura di questa lista, contenente alcune delle variabili
+più comuni che normalmente sono definite dal sistema, è riportato in
+fig.~\ref{fig:proc_envirno_list}.
\begin{figure}[htb]
\centering
- \includegraphics[width=11cm]{img/environ_var}
+ \includegraphics[width=13cm]{img/environ_var}
\caption{Esempio di lista delle variabili di ambiente.}
\label{fig:proc_envirno_list}
\end{figure}
Per convenzione le stringhe che definiscono l'ambiente sono tutte del tipo
-\textsl{\texttt{nome=valore}}. Inoltre alcune variabili, come quelle elencate
-in \curfig, sono definite dal sistema per essere usate da diversi programmi e
-funzioni: per queste c'è l'ulteriore convenzione di usare nomi espressi in
-caratteri maiuscoli.
+\textsl{\texttt{nome=valore}}. Inoltre alcune variabili, come quelle elencate
+in fig.~\ref{fig:proc_envirno_list}, sono definite dal sistema per essere usate
+da diversi programmi e funzioni: per queste c'è l'ulteriore convenzione di
+usare nomi espressi in caratteri maiuscoli.\footnote{la convenzione vuole che
+ si usino dei nomi maiuscoli per le variabili di ambiente di uso generico, i
+ nomi minuscoli sono in genere riservati alle variabili interne degli script
+ di shell.}
Il kernel non usa mai queste variabili, il loro uso e la loro interpretazione è
riservata alle applicazioni e ad alcune funzioni di libreria; in genere esse
costituiscono un modo comodo per definire un comportamento specifico senza
dover ricorrere all'uso di opzioni a linea di comando o di file di
-configurazione.
-
-La shell ad esempio ne usa molte per il suo funzionamento (come \var{PATH} per
-la ricerca dei comandi), e alcune di esse (come \var{HOME}, \var{USER}, etc.)
-sono definite al login. In genere è cura dell'amministratore definire le
-opportune variabili di ambiente in uno script di avvio. Alcune servono poi
-come riferimento generico per molti programmi (come \var{EDITOR} che indica
-l'editor preferito da invocare in caso di necessità).
+configurazione. É di norma cura della shell, quando esegue un comando, passare
+queste variabili al programma messo in esecuzione attraverso un uso opportuno
+delle relative chiamate (si veda sez.~\ref{sec:proc_exec}).
+
+La shell ad esempio ne usa molte per il suo funzionamento (come \texttt{PATH}
+per la ricerca dei comandi, o \texttt{IFS} per la scansione degli argomenti),
+e alcune di esse (come \texttt{HOME}, \texttt{USER}, ecc.) sono definite al
+login (per i dettagli si veda sez.~\ref{sec:sess_login}). In genere è cura
+dell'amministratore definire le opportune variabili di ambiente in uno script
+di avvio. Alcune servono poi come riferimento generico per molti programmi
+(come \texttt{EDITOR} che indica l'editor preferito da invocare in caso di
+necessità).
Gli standard POSIX e XPG3 definiscono alcune di queste variabili (le più
-comuni), come riportato in \ntab. GNU/Linux le supporta tutte e ne definisce
-anche altre: per una lista più completa si può controllare \cmd{man environ}.
+comuni), come riportato in tab.~\ref{tab:proc_env_var}. GNU/Linux le supporta
+tutte e ne definisce anche altre: per una lista più completa si può
+controllare \cmd{man 5 environ}.
\begin{table}[htb]
\centering
+ \footnotesize
\begin{tabular}[c]{|l|c|c|c|p{7cm}|}
\hline
\textbf{Variabile} & \textbf{POSIX} & \textbf{XPG3}
& \textbf{Linux} & \textbf{Descrizione} \\
\hline
\hline
- \macro{USER} & $\bullet$ & $\bullet$ & $\bullet$ & Nome utente\\
- \macro{LOGNAME} & $\bullet$ & $\bullet$ & $\bullet$ & Nome utente\\
- \macro{HOME} & $\bullet$ & $\bullet$ & $\bullet$ &
- Directory base dell'utente\\
- \macro{LANG} & $\bullet$ & $\bullet$ & $\bullet$ & Localizzazione\\
- \macro{PATH} & $\bullet$ & $\bullet$ & $\bullet$ & Elenco delle directory
- dei programmi\\
- \macro{PWD} & $\bullet$ & $\bullet$ & $\bullet$ & Directory corrente\\
- \macro{SHELL} & $\bullet$ & $\bullet$ & $\bullet$ & Shell in uso\\
- \macro{TERM} & $\bullet$ & $\bullet$ & $\bullet$ & Tipo di terminale\\
- \macro{PAGER} & $\bullet$ & $\bullet$ & $\bullet$ & Programma per vedere i
- testi\\
- \macro{EDITOR} & $\bullet$ & $\bullet$ & $\bullet$ & Editor di default\\
- \macro{BROWSER} & $\bullet$ & $\bullet$ & $\bullet$ & Browser di default\\
+ \texttt{USER} &$\bullet$&$\bullet$&$\bullet$& Nome utente\\
+ \texttt{LOGNAME}&$\bullet$&$\bullet$&$\bullet$& Nome di login\\
+ \texttt{HOME} &$\bullet$&$\bullet$&$\bullet$& Directory base
+ dell'utente\\
+ \texttt{LANG} &$\bullet$&$\bullet$&$\bullet$& Localizzazione\\
+ \texttt{PATH} &$\bullet$&$\bullet$&$\bullet$& Elenco delle directory
+ dei programmi\\
+ \texttt{PWD} &$\bullet$&$\bullet$&$\bullet$& Directory corrente\\
+ \texttt{SHELL} &$\bullet$&$\bullet$&$\bullet$& Shell in uso\\
+ \texttt{TERM} &$\bullet$&$\bullet$&$\bullet$& Tipo di terminale\\
+ \texttt{PAGER} &$\bullet$&$\bullet$&$\bullet$& Programma per vedere i
+ testi\\
+ \texttt{EDITOR} &$\bullet$&$\bullet$&$\bullet$& Editor preferito\\
+ \texttt{BROWSER}&$\bullet$&$\bullet$&$\bullet$& Browser preferito\\
+ \texttt{TMPDIR} &$\bullet$&$\bullet$&$\bullet$& Directory dei file
+ temporanei\\
\hline
\end{tabular}
- \caption{Variabile di ambiente più comuni definite da vari standard}
+ \caption{Esempi delle variabili di ambiente più comuni definite da vari
+ standard.}
\label{tab:proc_env_var}
\end{table}
-Lo standard ANSI C, pur non entrando nelle specifiche di come sono strutturati
-i contenuti, definisce la funzione \func{getenv} che permette di ottenere i
-valori delle variabili di ambiente, il suo prototipo è:
+Lo standard ANSI C prevede l'esistenza di un ambiente, e pur non entrando
+nelle specifiche di come sono strutturati i contenuti, definisce la funzione
+\funcd{getenv} che permette di ottenere i valori delle variabili di ambiente;
+il suo prototipo è:
\begin{prototype}{stdlib.h}{char *getenv(const char *name)}
Esamina l'ambiente del processo cercando una stringa che corrisponda a
quella specificata da \param{name}.
- \bodydesc{La funzione ritorna \macro{NULL} se non trova nulla, o il
+ \bodydesc{La funzione ritorna \val{NULL} se non trova nulla, o il
puntatore alla stringa che corrisponde (di solito nella forma
- \texttt{NOME=valore}).}
+ \cmd{NOME=valore}).}
\end{prototype}
Oltre a questa funzione di lettura, che è l'unica definita dallo standard ANSI
-C, in seguito sono state proposte altre da utilizzare per settare e per
-cancellare le variabili di ambiente presenti; uno schema delle funzioni
-previste nei vari standard unix e disponibili in Linux è riportato in \ntab.
+C, nell'evoluzione dei sistemi Unix ne sono state proposte altre, da
+utilizzare per impostare e per cancellare le variabili di ambiente. Uno schema
+delle funzioni previste nei vari standard e disponibili in Linux è riportato
+in tab.~\ref{tab:proc_env_func}.
\begin{table}[htb]
\centering
+ \footnotesize
\begin{tabular}[c]{|l|c|c|c|c|c|c|}
\hline
\textbf{Funzione} & \textbf{ANSI C} & \textbf{POSIX.1} & \textbf{XPG3} &
\textbf{SVr4} & \textbf{BSD} & \textbf{Linux} \\
\hline
\hline
- \func{getenv} & $\bullet$ & $\bullet$ & $\bullet$ &
- $\bullet$ & $\bullet$ & $\bullet$ \\
- \func{setenv} & & & &
- & $\bullet$ & $\bullet$ \\
- \func{unsetenv} & & & &
- & $\bullet$ & $\bullet$ \\
- \func{putenv} & & opz. & $\bullet$ &
- & $\bullet$ & $\bullet$ \\
- \func{clearenv} & & opz. & &
- & & \\
+ \func{getenv} & $\bullet$ & $\bullet$ & $\bullet$
+ & $\bullet$ & $\bullet$ & $\bullet$ \\
+ \func{setenv} & -- & -- & --
+ & -- & $\bullet$ & $\bullet$ \\
+ \func{unsetenv}& -- & -- & --
+ & -- & $\bullet$ & $\bullet$ \\
+ \func{putenv} & -- & opz. & $\bullet$
+ & -- & $\bullet$ & $\bullet$ \\
+ \func{clearenv}& -- & opz. & --
+ & -- & -- & $\bullet$ \\
\hline
\end{tabular}
\caption{Funzioni per la gestione delle variabili di ambiente.}
\label{tab:proc_env_func}
\end{table}
-In Linux solo le prime quattro funzioni di \curtab\ sono definite; delle tre
-restanti le prime due, \func{putenv} e \func{setenv} servono per assegnare
-nuove variabili di ambiente, i loro prototipi sono i seguenti:
+In Linux\footnote{in realtà nelle libc4 e libc5 sono definite solo le prime
+ quattro, \func{clearenv} è stata introdotta con le \acr{glibc} 2.0.} sono
+definite tutte le funzioni elencate in tab.~\ref{tab:proc_env_func}. La prima,
+\func{getenv}, l'abbiamo appena esaminata; delle restanti le prime due,
+\funcd{putenv} e \funcd{setenv}, servono per assegnare nuove variabili di
+ambiente, i loro prototipi sono i seguenti:
\begin{functions}
\headdecl{stdlib.h}
+ \funcdecl{int setenv(const char *name, const char *value, int overwrite)}
+ Imposta la variabile di ambiente \param{name} al valore \param{value}.
+
\funcdecl{int putenv(char *string)} Aggiunge la stringa \param{string}
all'ambiente.
- \funcdecl{int setenv(const char *name, const char *value, int overwrite)}
- Setta la variabile di ambiente \param{name} al valore \param{value}.
-
- \bodydesc{Entrambe le funzioni ritornano 0 in caso di successo e -1 per un
- errore, che è sempre \macro{ENOMEM}.}
+ \bodydesc{Entrambe le funzioni ritornano 0 in caso di successo e $-1$ per un
+ errore, che è sempre \errval{ENOMEM}.}
\end{functions}
-\noindent la terza è:
+\noindent la terza, \funcd{unsetenv}, serve a cancellare una variabile di
+ambiente; il suo prototipo è:
\begin{functions}
\headdecl{stdlib.h}
- \funcdecl{void unsetenv(const char *name)}
- Rimuove la variabile di ambiente \param{name}.
+ \funcdecl{void unsetenv(const char *name)} Rimuove la variabile di ambiente
+ \param{name}.
\end{functions}
+\noindent questa funzione elimina ogni occorrenza della variabile specificata;
+se essa non esiste non succede nulla. Non è prevista (dato che la funzione è
+\ctyp{void}) nessuna segnalazione di errore.
-Per cancellare una variabile di ambiente si usa \func{unsetenv}, che elimina
-ogni occorrenza della variabile, se la variabile specificata non esiste non
-succede nulla, e non è previsto (dato che la funzione è \type{void}) nessuna
-segnalazione di errore.
-
-Per modificare o aggiungere una variabile di ambiente si possono usare le
-funzioni \func{setenv} e \func{putenv}. La prima permette di specificare
+Per modificare o aggiungere una variabile di ambiente si possono usare sia
+\func{setenv} che \func{putenv}. La prima permette di specificare
separatamente nome e valore della variabile di ambiente, inoltre il valore di
\param{overwrite} specifica il comportamento della funzione nel caso la
variabile esista già, sovrascrivendola se diverso da zero, lasciandola
immutata se uguale a zero.
-La seconda funzione prende come parametro una stringa analoga quella
-restituita da \func{getenv}, e sempre nella forma \texttt{NOME=valore}. Se la
+La seconda funzione prende come argomento una stringa analoga a quella
+restituita da \func{getenv}, e sempre nella forma \code{NOME=valore}. Se la
variabile specificata non esiste la stringa sarà aggiunta all'ambiente, se
-invece esiste il suo valore sarà settato a quello specificato da
-\func{string}. Si tenga presente che, seguendo lo standard SUSv2, le
+invece esiste il suo valore sarà impostato a quello specificato da
+\param{string}. Si tenga presente che, seguendo lo standard SUSv2, le
\acr{glibc} successive alla versione 2.1.2 aggiungono\footnote{il
comportamento è lo stesso delle vecchie \acr{libc4} e \acr{libc5}; nelle
\acr{glibc}, dalla versione 2.0 alla 2.1.1, veniva invece fatta una copia,
seguendo il comportamento di BSD4.4; dato che questo può dar luogo a perdite
- di memoria e non rispetta lo standard il comportamento è stato modificato a
+ di memoria e non rispetta lo standard. Il comportamento è stato modificato a
partire dalle 2.1.2, eliminando anche, sempre in conformità a SUSv2,
- l'attributo \type{const} dal prototipo.} \func{string} alla lista delle
+ l'attributo \direct{const} dal prototipo.} \param{string} alla lista delle
variabili di ambiente; pertanto ogni cambiamento alla stringa in questione si
-riflette automaticamente sull'ambiente, e quindi si deve evitare di passare
-alla funzione variabili automatiche (per evitare i problemi esposti in
-\secref{sec:proc_auto_var}).
+riflette automaticamente sull'ambiente, e quindi si deve evitare di passare a
+questa funzione una variabile automatica (per evitare i problemi esposti in
+sez.~\ref{sec:proc_auto_var}).
Si tenga infine presente che se si passa a \func{putenv} solo il nome di una
variabile (cioè \param{string} è nella forma \texttt{NAME} e non contiene un
-\var{=}) allora questa viene cancellata dall'ambiente. Infine se la chiamata
-di \func{putenv} comporta la necessità di allocare una nuova versione del
-vettore \var{environ} questo sarà allocato, ma la versione corrente sarà
-deallocata solo se anch'essa risultante da una allocazione fatta in precedenza
-da un'altra \func{putenv}, il vettore originale (in genere piazzato al di
-sopra dello stack, vedi \figref{fig:proc_mem_layout}), o la memoria associata
-alle variabili di ambiente eliminate non viene comunque liberata.
+carattere \texttt{'='}) allora questa viene cancellata dall'ambiente. Infine
+se la chiamata di \func{putenv} comporta la necessità di allocare una nuova
+versione del vettore \var{environ} questo sarà allocato, ma la versione
+corrente sarà deallocata solo se anch'essa è risultante da un'allocazione
+fatta in precedenza da un'altra \func{putenv}. Questo perché il vettore delle
+variabili di ambiente iniziale, creato dalla chiamata ad \func{exec} (vedi
+sez.~\ref{sec:proc_exec}) è piazzato al di sopra dello \itindex{stack} stack,
+(vedi fig.~\ref{fig:proc_mem_layout}) e non nello \itindex{heap} \textit{heap}
+e non può essere deallocato. Inoltre la memoria associata alle variabili di
+ambiente eliminate non viene liberata.
+
+L'ultima funzione è \funcd{clearenv}, che viene usata per cancellare
+completamente tutto l'ambiente; il suo prototipo è:
+\begin{functions}
+ \headdecl{stdlib.h}
+
+ \funcdecl{int clearenv(void)}
+ Cancella tutto l'ambiente.
+
+ \bodydesc{la funzione restituisce 0 in caso di successo e un valore diverso
+ da zero per un errore.}
+\end{functions}
+
+In genere si usa questa funzione in maniera precauzionale per evitare i
+problemi di sicurezza connessi nel trasmettere ai programmi che si invocano un
+ambiente che può contenere dei dati non controllati. In tal caso si provvede
+alla cancellazione di tutto l'ambiente per costruirne una versione
+``\textsl{sicura}'' da zero.
+
+
+\subsection{Opzioni in formato esteso}
+\label{sec:proc_opt_extended}
+Oltre alla modalità ordinaria di gestione delle opzioni trattata in
+sez.~\ref{sec:proc_opt_handling} le \acr{glibc} forniscono una modalità
+alternativa costituita dalle cosiddette \textit{long-options}, che consente di
+esprimere le opzioni in una forma più descrittiva che nel caso più generale è
+qualcosa del tipo di ``\texttt{-{}-option-name=parameter}''.
+
+(NdA: questa parte verrà inserita in seguito).
+
+% TODO opzioni in formato esteso
\section{Problematiche di programmazione generica}
\label{sec:proc_gen_prog}
precauzioni o accorgimenti occorre prendere per risolverle. Queste
problematiche non sono specifiche di sistemi unix-like o multitasking, ma
avendo trattato in questo capitolo il comportamento dei processi visti come
-entità a se stanti, le riportiamo qui.
+entità a sé stanti, le riportiamo qui.
\subsection{Il passaggio delle variabili e dei valori di ritorno}
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
variabile, copia che la subroutine potrà modificare a piacere, senza che il
-valore originale nella routine chiamante venga toccato. In questo modo non
+valore originale nella funzione chiamante venga toccato. In questo modo non
occorre preoccuparsi di eventuali effetti delle operazioni della subroutine
-sulla variabile passata come parametro.
+sulla variabile passata come argomento.
Questo però va inteso nella maniera corretta. Il passaggio \textit{by value}
vale per qualunque variabile, puntatori compresi; quando però in una
subroutine si usano dei puntatori (ad esempio per scrivere in un buffer) in
realtà si va a modificare la zona di memoria a cui essi puntano, per cui anche
se i puntatori sono copie, i dati a cui essi puntano sono sempre gli stessi, e
-le eventuali modifiche avranno effetto e saranno visibili anche nella routine
+le eventuali modifiche avranno effetto e saranno visibili anche nella funzione
chiamante.
Nella maggior parte delle funzioni di libreria e delle system call i puntatori
vengono usati per scambiare dati (attraverso buffer o strutture) e le
-variabili semplici vengono usate per specificare parametri; in genere le
-informazioni a riguardo dei risultati vengono passate alla routine chiamante
+variabili semplici vengono usate per specificare argomenti; in genere le
+informazioni a riguardo dei risultati vengono passate alla funzione chiamante
attraverso il valore di ritorno. È buona norma seguire questa pratica anche
nella programmazione normale.
-
Talvolta però è necessario che la funzione possa restituire indietro alla
-funzione chiamante un valore relativo ad uno dei suoi parametri. Per far
-questo si usa il cosiddetto \textit{value result argument}, si passa cioè,
-invece di una normale variabile un puntatore; vedremo alcuni esempi di questa
-modalità nelle funzioni che gestiscono i socket (in
-\secref{sec:TCPel_functions}) in cui, per permettere al kernel di restituire
-informazioni sulle dimensioni delle strutture degli indirizzi utilizzate,
-viene usato questo meccanismo.
+funzione chiamante un valore relativo ad uno dei suoi argomenti. Per far
+questo si usa il cosiddetto \itindex{value~result~argument} \textit{value
+ result argument}, si passa cioè, invece di una normale variabile, un
+puntatore alla stessa; vedremo alcuni esempi di questa modalità nelle funzioni
+che gestiscono i socket (in sez.~\ref{sec:TCP_functions}), in cui, per
+permettere al kernel di restituire informazioni sulle dimensioni delle
+strutture degli indirizzi utilizzate, viene usato questo meccanismo.
+
+
+\subsection{Il passaggio di un numero variabile di argomenti}
+\label{sec:proc_variadic}
+
+Come vedremo nei capitoli successivi, non sempre è possibile specificare un
+numero fisso di argomenti per una funzione. Lo standard ISO C prevede nella
+sua sintassi la possibilità di definire delle \index{variadic}
+\textit{variadic function} che abbiano un numero variabile di argomenti,
+attraverso l'uso nella dichiarazione della funzione dello speciale costrutto
+``\texttt{\textellipsis}'', che viene chiamato \textit{ellipsis}.
+
+Lo standard però non provvede a livello di linguaggio alcun meccanismo con cui
+dette funzioni possono accedere ai loro argomenti. L'accesso viene pertanto
+realizzato a livello delle librerie standard del C che provvedono gli
+strumenti adeguati. L'uso di una \textit{variadic function} prevede quindi
+tre punti:
+\begin{itemize}
+\item \textsl{Dichiarare} la funzione come \textit{variadic} usando un
+ prototipo che contenga una \textit{ellipsis}.
+\item \textsl{Definire} la funzione come \textit{variadic} usando la stessa
+ \textit{ellipsis}, ed utilizzare le apposite macro che consentono la
+ gestione di un numero variabile di argomenti.
+\item \textsl{Invocare} la funzione specificando prima gli argomenti fissi, ed
+ a seguire quelli addizionali.
+\end{itemize}
+
+Lo standard ISO C prevede che una \index{variadic} \textit{variadic function}
+abbia sempre almeno un argomento fisso; prima di effettuare la dichiarazione
+deve essere incluso l'apposito header file \file{stdarg.h}; un esempio di
+dichiarazione è il prototipo della funzione \func{execl} che vedremo in
+sez.~\ref{sec:proc_exec}:
+\includecodesnip{listati/exec_sample.c}
+in questo caso la funzione prende due argomenti fissi ed un numero variabile
+di altri argomenti (che verranno a costituire gli elementi successivi al primo
+del vettore \param{argv} passato al nuovo processo). Lo standard ISO C
+richiede inoltre che l'ultimo degli argomenti fissi sia di tipo
+\textit{self-promoting}\footnote{il linguaggio C prevede che quando si
+ mescolano vari tipi di dati, alcuni di essi possano essere \textsl{promossi}
+ per compatibilità; ad esempio i tipi \ctyp{float} vengono convertiti
+ automaticamente a \ctyp{double} ed i \ctyp{char} e gli \ctyp{short} ad
+ \ctyp{int}. Un tipo \textit{self-promoting} è un tipo che verrebbe promosso
+ a sé stesso.} il che esclude vettori, puntatori a funzioni e interi di tipo
+\ctyp{char} o \ctyp{short} (con segno o meno). Una restrizione ulteriore di
+alcuni compilatori è di non dichiarare l'ultimo argomento fisso come
+\direct{register}.
+
+Una volta dichiarata la funzione il secondo passo è accedere ai vari argomenti
+quando la si va a definire. Gli argomenti fissi infatti hanno un loro nome, ma
+quelli variabili vengono indicati in maniera generica dalla \textit{ellipsis}.
+
+L'unica modalità in cui essi possono essere recuperati è pertanto quella
+sequenziale; essi verranno estratti dallo \itindex{stack} \textit{stack}
+secondo l'ordine in cui sono stati scritti. Per fare questo in \file{stdarg.h}
+sono definite delle apposite macro; la procedura da seguire è la seguente:
+\begin{enumerate}
+\item Inizializzare un puntatore alla lista degli argomenti di tipo
+ \macro{va\_list} attraverso la macro \macro{va\_start}.
+\item Accedere ai vari argomenti opzionali con chiamate successive alla macro
+ \macro{va\_arg}, la prima chiamata restituirà il primo argomento, la seconda
+ il secondo e così via.
+\item Dichiarare la conclusione dell'estrazione degli argomenti invocando la
+ macro \macro{va\_end}.
+\end{enumerate}
+In generale è perfettamente legittimo richiedere meno argomenti di quelli che
+potrebbero essere stati effettivamente forniti, e nella esecuzione delle
+\macro{va\_arg} ci si può fermare in qualunque momento ed i restanti argomenti
+saranno ignorati; se invece si richiedono più argomenti di quelli forniti si
+otterranno dei valori indefiniti. Nel caso del \cmd{gcc} l'uso della macro
+\macro{va\_end} è inutile, ma si consiglia di usarlo ugualmente per
+compatibilità.
+
+Le definizioni delle tre macro sono le seguenti:
+\begin{functions}
+ \headdecl{stdarg.h}
+
+ \funcdecl{void va\_start(va\_list ap, last)} Inizializza il puntatore alla
+ lista di argomenti \param{ap}; il parametro \param{last} \emph{deve} essere
+ l'ultimo degli argomenti fissi.
+
+ \funcdecl{type va\_arg(va\_list ap, type)} Restituisce il valore del
+ successivo argomento opzionale, modificando opportunamente \param{ap}; la
+ macro richiede che si specifichi il tipo dell'argomento attraverso il
+ parametro \param{type} che deve essere il nome del tipo dell'argomento in
+ questione. Il tipo deve essere \textit{self-promoting}.
+
+ \funcdecl{void va\_end(va\_list ap)} Conclude l'uso di \param{ap}.
+\end{functions}
+
+In generale si possono avere più puntatori alla lista degli argomenti,
+ciascuno andrà inizializzato con \macro{va\_start} e letto con \macro{va\_arg}
+e ciascuno potrà scandire la lista degli argomenti per conto suo.
+
+Dopo l'uso di \macro{va\_end} la variabile \param{ap} diventa indefinita e
+successive chiamate a \macro{va\_arg} non funzioneranno. Si avranno risultati
+indefiniti anche chiamando \macro{va\_arg} specificando un tipo che non
+corrisponde a quello dell'argomento.
+
+Un altro limite delle macro è che i passi 1) e 3) devono essere eseguiti nel
+corpo principale della funzione, il passo 2) invece può essere eseguito anche
+in una subroutine passandole il puntatore alla lista di argomenti; in questo
+caso però si richiede che al ritorno della funzione il puntatore non venga più
+usato (lo standard richiederebbe la chiamata esplicita di \macro{va\_end}),
+dato che il valore di \param{ap} risulterebbe indefinito.
+
+Esistono dei casi in cui è necessario eseguire più volte la scansione degli
+argomenti e poter memorizzare una posizione durante la stessa. La cosa più
+naturale in questo caso sembrerebbe quella di copiarsi il puntatore alla lista
+degli argomenti con una semplice assegnazione. Dato che una delle
+realizzazioni più comuni di \macro{va\_list} è quella di un puntatore nello
+\itindex{stack} \textit{stack} all'indirizzo dove sono stati salvati gli
+argomenti, è assolutamente normale pensare di poter effettuare questa
+operazione.
+
+In generale però possono esistere anche realizzazioni diverse, per questo
+motivo \macro{va\_list} è definito come \index{tipo!opaco} \textsl{tipo opaco}
+e non può essere assegnato direttamente ad un'altra variabile dello stesso
+tipo. Per risolvere questo problema lo standard ISO C99\footnote{alcuni
+ sistemi che non hanno questa macro provvedono al suo posto
+ \macro{\_\_va\_copy} che era il nome proposto in una bozza dello standard.}
+ha previsto una macro ulteriore che permette di eseguire la copia di un
+puntatore alla lista degli argomenti:
+\begin{prototype}{stdarg.h}{void va\_copy(va\_list dest, va\_list src)}
+ Copia l'attuale valore \param{src} del puntatore alla lista degli argomenti
+ su \param{dest}.
+\end{prototype}
+\noindent anche in questo caso è buona norma chiudere ogni esecuzione di una
+\macro{va\_copy} con una corrispondente \macro{va\_end} sul nuovo puntatore
+alla lista degli argomenti.
+
+La chiamata di una funzione con un numero variabile di argomenti, posto che la
+si sia dichiarata e definita come tale, non prevede nulla di particolare;
+l'invocazione è identica alle altre, con gli argomenti, sia quelli fissi che
+quelli opzionali, separati da virgole. Quello che però è necessario tenere
+presente è come verranno convertiti gli argomenti variabili.
+
+In Linux gli argomenti dello stesso tipo sono passati allo stesso modo, sia
+che siano fissi sia che siano opzionali (alcuni sistemi trattano diversamente
+gli opzionali), ma dato che il prototipo non può specificare il tipo degli
+argomenti opzionali, questi verranno sempre promossi, pertanto nella ricezione
+dei medesimi occorrerà tenerne conto (ad esempio un \ctyp{char} verrà visto da
+\macro{va\_arg} come \ctyp{int}).
+
+Uno dei problemi che si devono affrontare con le funzioni con un numero
+variabile di argomenti è che non esiste un modo generico che permetta di
+stabilire quanti sono gli argomenti passati effettivamente in una chiamata.
+
+Esistono varie modalità per affrontare questo problema; una delle più
+immediate è quella di specificare il numero degli argomenti opzionali come uno
+degli argomenti fissi. Una variazione di questo metodo è l'uso di un argomento
+per specificare anche il tipo degli argomenti (come fa la stringa di formato
+per \func{printf}).
+
+Una modalità diversa, che può essere applicata solo quando il tipo degli
+argomenti lo rende possibile, è quella che prevede di usare un valore speciale
+come ultimo argomento (come fa ad esempio \func{execl} che usa un puntatore
+\val{NULL} per indicare la fine della lista degli argomenti).
\subsection{Potenziali problemi con le variabili automatiche}
Uno dei possibili problemi che si possono avere con le subroutine è quello di
restituire alla funzione chiamante dei dati che sono contenuti in una
variabile automatica. Ovviamente quando la subroutine ritorna la sezione
-dello stack che conteneva la variabile automatica potrà essere riutilizzata da
-una nuova funzione, con le conseguenze immaginabili di sovrapposizione.
+dello \itindex{stack} \textit{stack} che conteneva la variabile automatica
+potrà essere riutilizzata da una nuova funzione, con le immaginabili
+conseguenze di sovrapposizione e sovrascrittura dei dati.
Per questo una delle regole fondamentali della programmazione in C è che
-all'uscita di una funzione non deve restare nessun riferimento a variabili
-locali di quella funzione; qualora necessiti di utilizzare variabili che
-possano essere viste anche dalla funzione chiamante queste devono essere
-allocate esplicitamente, o in maniera statica (usando variabili di tipo
-\type{static} o \type{extern}), o dinamicamente con una delle funzioni della
-famiglia \func{malloc}.
+all'uscita di una funzione non deve restare nessun riferimento alle variabili
+locali; qualora sia necessario utilizzare variabili che possano essere viste
+anche dalla funzione chiamante queste devono essere allocate esplicitamente, o
+in maniera statica (usando variabili di tipo \ctyp{static} o \ctyp{extern}), o
+dinamicamente con una delle funzioni della famiglia \func{malloc}.
+
\subsection{Il controllo di flusso non locale}
\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
-\func{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 occorre usare quello che viene chiamato un salto \textsl{non-locale};
-questo viene fatto usando salvando il contesto dello stack nel punto in cui si
-vuole tornare in caso di errore, e ripristinandolo quando l'occorrenza capita.
-
-
-La funzione che permette di salvare il contesto dello stack è \func{setjmp},
-il cui prototipo è:
-
+varie istruzioni del linguaggio C; fra queste la più bistrattata è il
+\code{goto}, che viene deprecato in favore dei costrutti della programmazione
+strutturata, che rendono il codice più leggibile e mantenibile. Esiste però un
+caso in cui l'uso di questa istruzione porta all'implementazione più
+efficiente e più chiara anche dal punto di vista della struttura del
+programma: quello dell'uscita in caso di errore.
+
+\index{salto~non-locale|(}
+
+Il C però non consente di effettuare un salto ad una etichetta definita in
+un'altra funzione, per cui se l'errore avviene in una funzione, e la sua
+gestione ordinaria è in un'altra, occorre usare quello che viene chiamato un
+\textsl{salto non-locale}. Il caso classico in cui si ha questa necessità,
+citato sia in \cite{APUE} che in \cite{glibc}, è quello di un programma nel
+cui corpo principale vengono letti dei dati in ingresso sui quali viene
+eseguita, tramite una serie di funzioni di analisi, una scansione dei
+contenuti, da cui si ottengono le indicazioni per l'esecuzione di opportune
+operazioni.
+
+Dato che l'analisi può risultare molto complessa, ed opportunamente suddivisa
+in fasi diverse, la rilevazione di un errore nei dati in ingresso può accadere
+all'interno di funzioni profondamente annidate l'una nell'altra. In questo
+caso si dovrebbe gestire, per ciascuna fase, tutta la casistica del passaggio
+all'indietro di tutti gli errori rilevabili dalle funzioni usate nelle fasi
+successive. Questo comporterebbe una notevole complessità, mentre sarebbe
+molto più comodo poter tornare direttamente al ciclo di lettura principale,
+scartando l'input come errato.\footnote{a meno che, come precisa \cite{glibc},
+ alla chiusura di ciascuna fase non siano associate operazioni di pulizia
+ specifiche (come deallocazioni, chiusure di file, ecc.), che non potrebbero
+ essere eseguite con un salto non-locale.}
+
+Tutto ciò può essere realizzato proprio con un salto non-locale; questo di
+norma viene realizzato salvando il contesto dello \itindex{stack}
+\textit{stack} nel punto in cui si vuole tornare in caso di errore, e
+ripristinandolo, in modo da tornare nella funzione da cui si era partiti,
+quando serve. La funzione che permette di salvare il contesto dello
+\itindex{stack} \textit{stack} è \funcd{setjmp}, il cui prototipo è:
\begin{functions}
\headdecl{setjmp.h}
- \funcdecl{void setjmp(jmp\_buf env)}
-
- Salva il contesto dello stack in \param{env} per un successivo uso da parte
- di \func{longjmp}. Il contesto viene invalidato se la routine che ha
- chiamato \func{setjmp} ritorna.
+ \funcdecl{int setjmp(jmp\_buf env)}
+ Salva il contesto dello stack.
+
\bodydesc{La funzione ritorna zero quando è chiamata direttamente e un
valore diverso da zero quando ritorna da una chiamata di \func{longjmp}
che usa il contesto salvato in precedenza.}
\end{functions}
-
-
-Per poter effettuare un salto non locale si usa la funzione \func{longjmp}; il
-suo prototipo è:
+
+Quando si esegue la funzione il contesto corrente dello \itindex{stack}
+\textit{stack} viene salvato nell'argomento \param{env}, una variabile di tipo
+\type{jmp\_buf}\footnote{questo è un classico esempio di variabile di
+ \index{tipo!opaco} \textsl{tipo opaco}. Si definiscono così strutture ed
+ altri oggetti usati da una libreria, la cui struttura interna non deve
+ essere vista dal programma chiamante (da cui il nome) che li devono
+ utilizzare solo attraverso dalle opportune funzioni di gestione.} che deve
+essere stata definita in precedenza. In genere le variabili di tipo
+\type{jmp\_buf} vengono definite come variabili globali in modo da poter
+essere viste in tutte le funzioni del programma.
+
+Quando viene eseguita direttamente la funzione ritorna sempre zero, un valore
+diverso da zero viene restituito solo quando il ritorno è dovuto ad una
+chiamata di \func{longjmp} in un'altra parte del programma che ripristina lo
+\itindex{stack} \textit{stack} effettuando il salto non-locale. Si tenga conto
+che il contesto salvato in \param{env} viene invalidato se la funzione che ha
+chiamato \func{setjmp} ritorna, nel qual caso un successivo uso di
+\func{longjmp} può comportare conseguenze imprevedibili (e di norma fatali)
+per il processo.
+
+Come accennato per effettuare un salto non-locale ad
+un punto precedentemente stabilito con \func{setjmp} si usa la funzione
+\funcd{longjmp}; il suo prototipo è:
\begin{functions}
\headdecl{setjmp.h}
\funcdecl{void longjmp(jmp\_buf env, int val)}
- Ripristina il contesto dello stack salvato dall'ultima chiamata di
- \func{setjmp} con l'argomento \param{env}. Il programma prosegue dal ritorno
- di \func{setjmp} con un valore \param{val}. Il valore di \param{val} deve
- essere diverso da zero, se viene specificato 0 sarà usato 1 al suo posto.
-
+ Ripristina il contesto dello stack.
+
\bodydesc{La funzione non ritorna.}
\end{functions}
+La funzione ripristina il contesto dello \itindex{stack} \textit{stack}
+salvato da una chiamata a \func{setjmp} nell'argomento \param{env}. Dopo
+l'esecuzione della funzione il programma prosegue nel codice successivo al
+ritorno della \func{setjmp} con cui si era salvato \param{env}, che restituirà
+il valore
+\param{val} invece di zero. Il valore di \param{val} specificato nella
+chiamata deve essere diverso da zero, se si è specificato 0 sarà comunque
+restituito 1 al suo posto.
+
+In sostanza un \func{longjmp} è analogo ad un \code{return}, solo che invece
+di ritornare alla riga successiva della funzione chiamante, il programma
+ritorna alla posizione della relativa \func{setjmp}, l'altra differenza è che
+il ritorno può essere effettuato anche attraverso diversi livelli di funzioni
+annidate.
+
+L'implementazione di queste funzioni comporta alcune restrizioni dato che esse
+interagiscono direttamente con la gestione dello \itindex{stack}
+\textit{stack} ed il funzionamento del compilatore stesso. In particolare
+\func{setjmp} è implementata con una macro, pertanto non si può cercare di
+ottenerne l'indirizzo, ed inoltre delle chiamate a questa funzione sono sicure
+solo in uno dei seguenti casi:
+\begin{itemize}
+\item come espressione di controllo in un comando condizionale, di selezione
+ o di iterazione (come \code{if}, \code{switch} o \code{while});
+\item come operando per un operatore di uguaglianza o confronto in una
+ espressione di controllo di un comando condizionale, di selezione o di
+ iterazione;
+\item come operando per l'operatore di negazione (\code{!}) in una espressione
+ di controllo di un comando condizionale, di selezione o di iterazione;
+\item come espressione a sé stante.
+\end{itemize}
+
+In generale, dato che l'unica differenza fra la chiamata diretta e quella
+ottenuta da un \func{longjmp} è costituita dal valore di ritorno di
+\func{setjmp}, essa è usualmente chiamata all'interno di un comando \code{if}.
+
+Uno dei punti critici dei salti non-locali è quello del valore delle
+variabili, ed in particolare quello delle variabili automatiche della funzione
+a cui si ritorna. In generale le variabili globali e statiche mantengono i
+valori che avevano al momento della chiamata di \func{longjmp}, ma quelli
+delle variabili automatiche (o di quelle dichiarate
+\direct{register}\footnote{la direttiva \direct{register} del compilatore
+ chiede che la variabile dichiarata tale sia mantenuta, nei limiti del
+ possibile, all'interno di un registro del processore. Questa direttiva è
+ originaria dell'epoca dai primi compilatori, quando stava al programmatore
+ scrivere codice ottimizzato, riservando esplicitamente alle variabili più
+ usate l'uso dei registri del processore. Oggi questa direttiva è in disuso
+ dato che tutti i compilatori sono normalmente in grado di valutare con
+ maggior efficacia degli stessi programmatori quando sia il caso di eseguire
+ questa ottimizzazione.}) sono in genere indeterminati.
+
+Quello che succede infatti è che i valori delle variabili che sono tenute in
+memoria manterranno il valore avuto al momento della chiamata di
+\func{longjmp}, mentre quelli tenuti nei registri del processore (che nella
+chiamata ad un'altra funzione vengono salvati nel contesto nello
+\itindex{stack} \textit{stack}) torneranno al valore avuto al momento della
+chiamata di \func{setjmp}; per questo quando si vuole avere un comportamento
+coerente si può bloccare l'ottimizzazione che porta le variabili nei registri
+dichiarandole tutte come \direct{volatile}.\footnote{la direttiva
+ \direct{volatile} informa il compilatore che la variabile che è dichiarata
+ può essere modificata, durante l'esecuzione del nostro, da altri programmi.
+ Per questo motivo occorre dire al compilatore che non deve essere mai
+ utilizzata l'ottimizzazione per cui quanto opportuno essa viene mantenuta in
+ un registro, poiché in questo modo si perderebbero le eventuali modifiche
+ fatte dagli altri programmi (che avvengono solo in una copia posta in
+ memoria).}
+
+\index{salto~non-locale|)}
+
+
+
+% LocalWords: like exec kernel thread main ld linux static linker char envp Gb
+% LocalWords: sez POSIX exit system call cap abort shell diff errno stdlib int
+% LocalWords: SUCCESS FAILURE void atexit stream fclose unistd descriptor init
+% LocalWords: SIGCHLD wait function glibc SunOS arg argp execve fig high kb Mb
+% LocalWords: memory alpha swap table printf Unit MMU paging fault SIGSEGV BSS
+% LocalWords: multitasking text segment NULL Block Started Symbol
+% LocalWords: heap stack calling convention size malloc calloc realloc nmemb
+% LocalWords: ENOMEM ptr uClib cfree error leak smartpointers hook Dmalloc brk
+% LocalWords: Gray Watson Electric Fence Bruce Perens sbrk longjmp SUSv BSD ap
+% LocalWords: ptrdiff increment locking lock copy write capabilities IPC mlock
+% LocalWords: capability MEMLOCK limits getpagesize RLIMIT munlock sys const
+% LocalWords: addr len EINVAL EPERM mlockall munlockall flags l'OR CURRENT IFS
+% LocalWords: argc argv parsing questofile txt getopt optstring switch optarg
+% LocalWords: optind opterr optopt ForkTest POSIXLY CORRECT long options NdA
+% LocalWords: option parameter list environ PATH HOME XPG tab LOGNAME LANG PWD
+% LocalWords: TERM PAGER TMPDIR getenv name SVr setenv unsetenv putenv opz gcc
+% LocalWords: clearenv libc value overwrite string reference result argument
+% LocalWords: socket variadic ellipsis header stdarg execl self promoting last
+% LocalWords: float double short register type dest src extern setjmp jmp buf
+% LocalWords: env return if while Di page cdecl
+% LocalWords: environment
+
+%%% Local Variables:
+%%% mode: latex
+%%% TeX-master: "gapil"
+%%% End: