\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
esecuzione.

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
affronteremo l'avvio e il funzionamento di un singolo processo partendo dal
punto di vista del programma posto 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).

\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
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}.

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.


\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
esempio 128 viene usato per indicare l'incapacità di eseguire un altro
programma in un sottoprocesso. Benché anche 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.

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}).


\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:
\begin{prototype}{stdlib.h}{void exit(int status)}
  Causa la conclusione ordinaria del programma restituendo il valore
  \var{status} al processo padre.

  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
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.

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:
\begin{prototype}{unistd.h}{void \_exit(int status)}
  Causa la conclusione immediata del programma restituendo il valore
  \var{status} al processo padre.

  La funzione non ritorna. Il processo viene terminato.
\end{prototype}

La funzione chiude tutti i file descriptor appartenenti al processo (sui 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}).


\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 è:
\begin{prototype}{stdlib.h}{void atexit(void (*function)(void))}
  Registra la funzione \var{function} per essere chiamata all'uscita dal
  programma. 

  La funzione restituisce 0 in caso di successo e -1 in caso di fallimento,
  \texttt{errno} non viene settata.
\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 è:
\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.

  La funzione restituisce 0 in caso di successo e -1 in caso di fallimento,
  \var{errno} non viene settata.
\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}.

Nella sequenza di chiusura tutte le funzioni registrate verranno chiamate in
ordine inverso rispetto a quello di registrazione (ed una stessa funzione
registrata più volte sarà chiamata più volte); poi verranno chiusi tutti gli
stream aperti, infine verrà chiamata \func{\_exit}.


\subsection{Conclusioni}
\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}).

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}.

Lo schema delle modalità con cui si avvia e conclude normalmente un programma
è riportato in \nfig.

\begin{figure}[htb]
  \centering
  
  \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}.



\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.


\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.

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. 

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}).


\subsection{La struttura della memoria di un processo}
\label{sec:proc_mem_layout}

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:

\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.
  
  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.
  
  La prima parte è il segmento dei dati inizializzati, che contiene le
  variabili globali il cui valore è stato assegnato esplicitamente. Ad esempio
  se si definisce:
\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
    double pi = 3.14;
\end{lstlisting}
  questo valore sarà immagazzinato in questo segmento. La memoria di questo
  segmento viene preallocato dalla \func{exec} 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.
  
\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 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
  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.
\end{enumerate}

\begin{figure}[htb]
  \centering
  \includegraphics[width=5cm]{img/memory_layout.eps}
  \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.


\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.

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.

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. 

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.


\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:
\begin{functions}
\headdecl{stdlib.h}
\funcdecl{void *calloc(size\_t size)}
  Alloca \var{size} bytes nello heap. 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}.
\funcdecl{void *malloc(size\_t size)}
  Alloca \var{size} bytes nello heap. La memoria non viene inizializzata.

  La funzione restituisce il puntatore alla zona di memoria allocata in caso
  di successo e \macro{NULL} in caso di fallimento, nel qual caso
  \var{errno} viene settata a \macro{ENOMEM}.
\funcdecl{void *realloc(void *ptr, size\_t size)}
  Cambia la dimensione del blocco allocato all'indirizzo \var{ptr}
  portandola a \var{size}.

  La funzione restituisce il puntatore alla zona di memoria allocata in caso
  di successo e \macro{NULL} in caso di fallimento, nel qual caso
  \var{errno} viene settata a \macro{ENOMEM}.
\funcdecl{void free(void *ptr)}
  Disalloca lo spazio di memoria puntato da \var{ptr}.

  La funzione non ritorna nulla.
\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.

La memoria allocata dinamicamente deve essere esplicitamente rilasciata usando
\func{free}\footnote{le glibc provvedono anche una funzione \func{cfree}
  defininita per compatibilità con SunOS, che è deprecata} una volta che non
sia più necessaria. Questa funzione vuole come parametro 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
  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 in più non viene
inizializzato. 

Il fatto che il blocco di memoria restituito da \func{realloc} possa
cambiare comporta che si deve sempre riassegnare al puntatore passato per il
ridimensionamento il valore di ritorno della funzione, e che non ci devono
essere altri puntatori che puntino all'interno di un'area che si vuole
ridimensionare.

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 è
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.
\item se è posta ad 1 viene stampato un avviso sullo \textit{standard error}
  (vedi \secref{sec:file_stdfiles}).
\item se è posta a 2 viene chiamata \func{abort}, che in genere causa
  l'immediata conclusione del programma.
\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 \texttt{alloca}}  
\label{sec:proc_mem_alloca}

Una alternativa possibile all'uso di \texttt{malloc}, che non soffre del tipo
di problemi di memory leak descritti in precedenza è la funzione
\texttt{alloca} che invece che allocare la memoria nello heap usa lo il
segmento di stack della funzione corrente. La sintassi è identica:
\begin{prototype}{stdlib.h}{void *alloca(size\_t size)}
  Alloca \texttt{size} bytes nel segmento di stack della funzione chiamante.
  La memoria non viene inizializzata.

  La funzione restituisce il puntatore alla zona di memoria allocata in caso
  di successo e \texttt{NULL} in caso di fallimento, nel qual caso
  \texttt{errno} viene settata a \texttt{ENOMEM}.
\end{prototype}
ma in questo caso non è più necessario liberare la memoria in quanto questa
viene rilasciata automaticamente al ritorno della funzione.

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{longjump} per uscire con un salto non locale da una funzione (vedi
\secref{sec:proc_longjmp}),

Un altro vantaggio e che in Linux la funzione è molto veloce e non viene
sprecato spazio, infatti non è necessario gestire un pool di memoria da
riservare e si evitano anche problemi di frammentazione.

Gli svantaggi sono che la funzione non è disponibile su tutti gli unix, quando
non è possibile aumentare le dimensioni dello stack una volta chiamata una
funzione e quindi l'uso limita la portabilità dei programmi, inoltre se si
cerca di allocare troppa memoria non si ottiene un messaggio di errore, ma un
segnale di \textit{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; un errore comune infatti è quello di restituire al chiamante un
puntatore ad una di queste variabili, che sarà automaticamente distrutta
all'uscita della funzione, con gli stessi problemi appena citati per
\func{alloca}.

\subsection{Le funzioni \texttt{brk} e \texttt{sbrk}}  
\label{sec:proc_mem_sbrk}

L'uso di queste funzioni è necessario solo quando si voglia accedere alle
analoghe system call a cui fanno da interfaccia (ad esempio per implementare
una propria versione di \texttt{malloc}. Le  funzione sono:
\begin{prototype}{unistd.h}{int *brk(void end\_data\_segment)}
  Sposta la fine del segmento dei dati all'indirizzo specificato da
  \texttt{end\_data\_segment}.
  
  La funzione restituisce 0 in caso di successo e -1 in caso di fallimento,
  nel qual caso \texttt{errno} viene settata a \texttt{ENOMEM}.
\end{prototype}
\begin{prototype}{unistd.h}{int *sbrk(ptrdiff\_t increment)}
  Incrementa lo spazio dati di un programma di \texttt{increment}. Un valore
  zero restituisce l'attuale posizione della fine del segmento dati.
  
  La funzione restituisce il puntatore all'inizio della nuova zona di memoria
  allocata in caso di successo e \texttt{NULL} in caso di fallimento, nel qual
  caso \texttt{errno} viene settata a \texttt{ENOMEM}.
\end{prototype}

Queste funzioni sono state deliberatamente escluse dallo standard POSIX.1 e
per i programmi normali è opportuno usare le funzioni di allocazione standard
descritte in precedenza, che sono costruite su di esse.  In genere si usa
\texttt{sbrk} con un valore zero per ottenere l'attuale posizione della fine
del segmento dati. 


% \subsection{La personalizzazione delle funzioni di allocazione} 
% \label{sec:proc_mem_malloc_custom}


\subsection{Il controllo della memoria virtuale}  
\label{sec:proc_mem_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. 

Nell'uso comune un processo non deve preoccuparsi di tutto ciò in quanto il
meccanismo della paginazione riporta in RAM, ed in maniera trasparente, tutte
le pagine che gli occorrono; esistono però esigenze particolari in cui non si
vuole che il meccanismo dello \textit{swapping}, in generale i motivi per cui
si possono avere queste necessità sono sostanzialmente due:
\begin{itemize}
\item La velocità. Il processo della paginazione è trasparente solo se il
  programma in esecuzione se non è sensibile al tempo che occorre a riportare
  la pagina in memoria; per questo motivi processi critici che hanno esigenze
  di tempo reale o tolleranze critiche nella risposte (ad esempio processi che
  trattano campionamenti sonori) possono non essere in grado di sopportare
  le variazioni della velocità di accesso dovuta alla paginazione.

  In certi casi poi un programmatore può conoscere meglio dell'algoritmo di
  allocazione delle pagine le esigenze specifiche del suo programma e decidere
  quali pagine di memoria è opportuno che restino in memoria per un aumento
  delle prestazioni. In genere queste sono esigenze particolari e richiedono
  anche un aumento delle priorità in esecuzione (vedi \secref{sec:xxx_xxx}).
  
\item La sicurezza. Se si tengono password o chiavi in memoria queste possono
  essere portate su disco dal meccanismo della paginazione, questo rende più
  lungo il periodo di tempo in cui i segreti sono presenti in chiaro, e
  complessa la loro cancellazione (in genere è possibile cancellare della RAM
  ma altrettanto non vale per il disco su cui la pagina contenente i segreti
  può essere stata salvata). Per questo motivo programmi di crittografia
  richiedono il blocco di alcune pagine di memoria.
\end{itemize}

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.

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.

Il blocco di memoria persiste fintanto che il processo che lo 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 blocchi di memoria.

I 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 usufruiscono dei memory lock di
  questo}. Siccome la presenza di memory lock ha un impatto sugli altri
processi solo root ha la capacità di bloccare una pagina, ogni processo può
però sbloccare le sue pagine. Il sistema pone dei limiti all'ammontare di
memoria di un processo che può essere bloccata e al totale di memoria fisica
che può dedicare a questo.


\section{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 la funzione \func{longjump}. 


\section{La gestione di parametri e opzioni}
\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.

\subsection{Il formato dei parametri}
\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).

\subsection{La gestione delle opzioni}
\label{sec:proc_opt_handling}

In generale un programma unix riceve da linea di comando sia i parametri 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}.

Per gestire le opzioni all'interno dei parametri passati in \func{argv} le
librerie standard del C forniscono la funzione \func{getopt} che ha il
prototipo:
\begin{prototype}{unistd.h}
{int getopt(int argc, char * const argv[], const char * optstring)}
La funzione esegue il parsing degli argomenti passati da linea di comando
riconoscendo le possibili opzioni segnalate con \var{optstring}.

Ritorna il carattere che segue l'opzione, \cmd{:} se manca un paramatro
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} ed una stringa che indica quali sono le opzioni valide; la
funzione effettua la scansione della lista dei parametri ricercando ogni
stringa che comincia con \cmd{-} e ritorna ogni volta che trova una opzione
valida.

La stringa \var{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 \texttt{:} nel caso appena accennato ad esempio la stringa di
opzioni sarebbe \texttt{"r:m"}.

La modalità di uso è pertanto quella di chiamare più volte la funzione
all'interno di un ciclo di while fintanto che essa non ritorna il valore
\texttt{-1} che indica che non ci sono più opzioni. Nel caso si incontri
un'opzione non dichiarata in \texttt{optstring} viene ritornato un \texttt{?}
mentre se l'opzione non è seguita da un parametro viene ritornato un
\texttt{:} infine se viene incontrato il valore \texttt{--} la scansione viene
considerata conclusa, anche se vi sono altri parametri che cominciano con
\texttt{-}.

Quando la funzione trova un'opzione essa ritorna il valore numerico del
carattere, in questo modo si possono prendere le azioni relative usando un
case; la funzione inizializza inoltre alcune variabili globali:
\begin{itemize}
\item \texttt{char * optarg} contiene il puntatore alla stringa argomento
  dell'opzione.
\item \texttt{int optind} alla fine della scansione restituisce l'indice del
  primo argomento che non è un'opzione.
\item \texttt{int opterr} previene, se posto a zero, la stampa di un messaggio
  di errore in caso di riconoscimento di opzioni non definite.
\item \texttt{int optopt} contiene il carattere dell'opzione non riconosciuta.
\end{itemize}

In \nfig\ è mostrato un programma di esempio:
\begin{figure}[htbp]
  \footnotesize
    \begin{lstlisting}{}
    opterr = 0;  /* don't want writing to stderr */
    while ( (i = getopt(argc, argv, "o:a:i:hve")) != -1) {
        switch (i) {
        case 'i':   /* input file */
            in_file=open(optarg,O_RDONLY);
            if (in_file<0) {
                perror("Cannot open input file");
                exit(1);
            }
            break;
        case 'o':   /* output file (overwrite) */
            out_file=open(optarg,O_WRONLY|O_CREAT);
            if (out_file<0) {
                perror("Cannot open output file");
                exit(1);
            }
            break;
        case 'a':   /* output file (append) */
            out_file=open(optarg,O_WRONLY|O_CREAT|O_APPEND);
            break;
        case 'h':   /* print help usage */
            usage();
            break;
        case 'v':   /* set verbose mode */
            debug("Option -v active\n");
            verbose=1;
            break;
        case '?':   /* unrecognized options */
            printf("Unrecognized options -%c\n",optopt);
            usage();
        default:    /* should not reached */
            debug("default option\n");
            usage();
        }
    }
    debug("Optind %d, argc %d\n",optind,argc);
  \end{lstlisting}
  \caption{Esempio di codice per la gestione delle opzioni.}
  \label{fig:proc_options_code}
\end{figure}


\subsection{Opzioni in formato esteso}
\label{sec:proc_opt_extended}

Un'estensione di questo schema è costituito dalle cosiddette
\textit{long-options} espresse nella forma \texttt{--option=parameter}, anche
la gestione di queste ultime è stata standardizzata attraverso l'uso di una
versione estesa di \func{getopt}.

(NdA: da finire).


\subsection{Le variabili di ambiente}
\label{sec:proc_environ}

Oltre ai parametri passati da 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 costruita nella
chiamata ad \func{exec} che lo ha 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 null). A
differenza di \var{argv[]} però in questo caso non si ha la lunghezza
dell'array dato da un equivalente di \var{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.
\begin{figure}[htb]
  \centering
  \includegraphics[width=11cm]{img/environ_var.eps}
  \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 queste c'è la convezione di usare
nomi espressi in caratteri maiuscoli.

Il kernel non usa mai queste variabili, il loro uso e la loro intepretazione è
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à).

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 parziale si può controllare \cmd{man environ}.







