Aggiunti zombie, orfani, e lupi mannari.
[gapil.git] / prochand.tex
index 3b40d63c067da2a040a08f67c9b246d53227329b..b74cacdfa791b708e52caae0748dba8264e131ae 100644 (file)
@@ -16,165 +16,678 @@ funzioni a questo connesse.
 
 Partiremo con una introduzione generale ai concetti che stanno alla base della
 gestione dei processi in unix. Introdurremo in questa sezione l'architettura
-della gestione dei processi e le sue principali caratteristiche.  
+della gestione dei processi e le sue principali caratteristiche, e daremo una
+panoramica sull'uso delle principali funzioni per la gestione dei processi.
 
 \subsection{La gerarchia dei processi}
 \label{sec:proc_hierarchy}
 
-Una delle caratteristiche essenziali di unix (che esamineremo in dettaglio più
-avanti) è che ogni processo può a sua volta generare altri processi figli
-(\textit{child}): questo è ad esempio quello che fa la shell quando mette in
-esecuzione il programma che gli indichiamo nella linea di comando.
-
-Una seconda caratteristica di unix è che ogni processo viene sempre generato
-in tale modo da un processo genitore (\textit{parent}) attraverso una apposita
-system call. Questo vale per tutti i processi, tranne per un processo
-speciale, che normalmente è \file{/sbin/init}, che invece viene lanciato dal
-kernel finita la fase di avvio e che quindi non è figlio di nessuno.
-
-Tutto ciò significa che, come per i file su disco, i processi sono organizzati
-gerarchicamente dalla relazione fra genitori e figli; alla base dell'albero in
-questo caso c'è \file{init} che è progenitore di ogni altro processo.
-
-
-\subsection{La gestione dei processi}
+A differenza di quanto avviene in altri sistemi (ad esempio nel VMS la
+generazione di nuovi processi è un'operazione privilegiata) una delle
+caratteristiche di unix (che esamineremo in dettaglio più avanti) è che
+qualunque processo può a sua volta generarne altri, detti processi figli
+(\textit{child process}). Ogni processo è identificato presso il sistema da un
+numero unico, il \acr{pid} (da \textit{process identifier}).
+
+Una seconda caratteristica è che la generazione di un processo è una
+operazione separata rispetto al lancio di un programma. In genere la sequenza
+è sempre quella di creare un nuovo processo, il quale si eseguirà, in un passo
+successivo, il programma voluto: questo è ad esempio quello che fa la shell
+quando mette in esecuzione il programma che gli indichiamo nella linea di
+comando.
+
+Una terza caratteristica è che ogni processo viene sempre generato da un altro
+che viene chiamato processo genitore (\textit{parent process}). Questo vale
+per tutti i processi, con una eccezione (dato che ci deve essere un punto di
+partenza), esiste sempre infatti un processo speciale, che normalmente è
+\cmd{/sbin/init}, che viene lanciato dal kernel quando questo ha finito la
+fase di avvio, esso essendo il primo processo lanciato ha sempre il \acr{pid}
+uguale a 1 e non è figlio di nessuno.
+
+Questo è ovviamente un processo speciale, che in genere si occupa di far
+partire tutti gli processi altri necessari al funzionamento del sistema,
+inoltre \cmd{init} è essenziale per svolgere una serie di compiti
+amministrativi nelle operazioni ordinarie del sistema (torneremo si alcuni di
+essi in \secref{}) e non può mai essere terminato. La struttura del sistema
+comunque consente di lanciare al posto di \cmd{init} qualunque altro programma
+(e in casi di emergenza, ad esempio se il file di \cmd{init} si fosse
+corrotto, è possibile farlo ad esempio passando la riga \cmd{init=/bin/sh}
+all'avvio).
+
+Dato che tutti i processi successivi sono comunque generati da \cmd{init} o da
+suoi figli tutto ciò comporta che, i processi sono organizzati gerarchicamente
+dalla relazione fra genitori e figli, in maniera analoga a come i file sono
+organizzati in un albero di directory con alla base \file{/} (si veda
+\secref{sec:file_file_struct}); in questo caso alla base dell'albero c'è il
+processo \cmd{init} che è progenitore di ogni altro processo\footnote{in
+  realtà questo non è del tutto vero, in Linux ci sono alcuni processi che pur
+  comparendo come figli di init (ad esempio in \cmd{pstree}) sono generati
+  direttamente dal kernel, come \cmd{keventd}, \cmd{kswapd}, etc.}.
+
+
+\subsection{Una panoramica sulle funzioni di gestione}
 \label{sec:proc_handling_intro}
 
-I processi vengono creati dalla funzione \texttt{fork}; in genere questa è una
-system call, ma Linux però usa un'altra nomenclatura, e la funzione fork è
-basata a sua volta sulla system call \texttt{clone}, che viene usata anche per
-generare i \textit{thread}.  Il processo figlio creato dalla \textit{fork} è
-una copia identica del processo processo padre, solo che ha un suo pid
-proprio.
+I processi vengono creati dalla funzione \func{fork}; in molti unix questa è
+una system call, Linux però usa un'altra nomenclatura, e la funzione fork è
+basata a sua volta sulla system call \func{\_\_clone}, che viene usata anche
+per generare i \textit{thread}.  Il processo figlio creato dalla \func{fork} è
+una copia identica del processo processo padre, ma ha nuovo \acr{pid} e viene
+eseguito in maniera indipendente (le differenze fra padre e figlio sono
+affrontate in dettaglio in \secref{sec:proc_fork}).
 
 Se si vuole che il processo padre si fermi fino alla conclusione del processo
-figlio questo deve essere specificato subito dopo la fork chiamando la
-funzione \texttt{wait} o la funzione \texttt{waitpid}, che restituiscono anche
-una informazione abbastanza limitata (il codice di uscita) sulle cause della
-terminazione del processo.
+figlio questo deve essere specificato subito dopo la \func{fork} chiamando la
+funzione \func{wait} o la funzione \func{waitpid} (si veda
+\secref{sec:proc_wait}); queste funzioni restituiscono anche una informazione
+abbastanza limitata (lo stato di terminazione) sulle cause della terminazione
+del processo.
 
 Quando un processo ha concluso il suo compito o ha incontrato un errore non
-risolvibile esso può essere terminato con la funzione \texttt{exit} (si veda
-quanto discusso in \secref{sec:proc_termination}). La vita del processo
-però termina solo quando viene chiamata la quando la sua conclusione viene
-ricevuta dal processo padre, a quel punto tutte le risorse allocate nel
-sistema ad esso associate vengono rilasciate.
+risolvibile esso può essere terminato con la funzione \func{exit} (si veda
+quanto discusso in \secref{sec:proc_termination}). La vita del processo però
+termina solo quando la notifica della sua conclusione viene ricevuta dal
+processo padre, a quel punto tutte le risorse allocate nel sistema ad esso
+associate vengono rilasciate.
 
 Avere due processi che eseguono esattamente lo stesso codice non è molto
-utile, normalmente si genera un secondo processo per affidargli l'esecuzione di
-un compito specifico (ad esempio gestire una connessione dopo che questa è
+utile, normalmente si genera un secondo processo per affidargli l'esecuzione
+di un compito specifico (ad esempio gestire una connessione dopo che questa è
 stata stabilita), o fargli eseguire (come fa la shell) un altro programma. Per
-questo si usa la seconda funzione fondamentale per programmazione coi processi
-che è la \texttt{exec}.
+quest'ultimo caso si usa la seconda funzione fondamentale per programmazione
+coi processi che è la \func{exec}.
 
-Il programma che un processo sta eseguendo si chiama immagine del processo
-(\textit{process image}), le funzioni della famiglia \func{exec} permettono
-di caricare un'altro programma da disco sostituendo quest'ultimo alla process
-image corrente, questo fa si che la precedente immagine venga completamente
-cancellata e quando il nuovo programma esce anche il processo termina, senza
-ritornare alla precedente immagine.
+Il programma che un processo sta eseguendo si chiama immagine del processo (o
+\textit{process image}), le funzioni della famiglia \func{exec} permettono di
+caricare un'altro programma da disco sostituendo quest'ultimo all'immagine
+corrente; questo fa si che l'immagine precedente venga completamente
+cancellata. Questo significa che quando il nuovo programma esce anche il
+processo termina, e non si può tornare alla precedente immagine.
 
 Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto
 particolari con caratteristiche uniche rispetto a tutte le altre, infatti la
 prima ritorna due volte (nel processo padre e nel figlio) mentre la seconda
 non ritorna mai (in quanto con essa viene eseguito un altro programma).
 
-I processi vengono creati dalla funzione \texttt{fork}; in genere questa è una
-system call, ma Linux però usa un'altra nomenclatura, e la funzione fork è
-basata a sua volta sulla system call \texttt{clone}, che viene usata anche per
-generare i \textit{thread}.  Il processo figlio creato dalla \textit{fork} è
-una copia identica del processo processo padre, solo che ha un suo pid
-proprio.
 
-Se si vuole che il processo padre si fermi fino alla conclusione del processo
-figlio questo deve essere specificato subito dopo la fork chiamando la
-funzione \texttt{wait} o la funzione \texttt{waitpid}, che restituiscono anche
-una informazione abbastanza limitata (il codice di uscita) sulle cause della
-terminazione del processo.
 
-Quando un processo ha concluso il suo compito o ha incontrato un errore non
-risolvibile esso può essere terminato con la funzione \texttt{exit} (si veda
-quanto discusso in \secref{sec:proc_termination}). La vita del processo
-però termina solo quando viene chiamata la quando la sua conclusione viene
-ricevuta dal processo padre, a quel punto tutte le risorse allocate nel
-sistema ad esso associate vengono rilasciate.
+\section{La gestione dei processi}
+\label{sec:proc_handling}
 
-Avere due processi che eseguono esattamente lo stesso codice non è molto
-utile, normalmente si genera un secondo processo per affidargli l'esecuzione di
-un compito specifico (ad esempio gestire una connessione dopo che questa è
-stata stabilita), o fargli eseguire (come fa la shell) un altro programma. Per
-questo si usa la seconda funzione fondamentale per programmazione coi processi
-che è la \texttt{exec}.
+In questa sezione tratteremo le funzioni per la gestione dei processi, a
+partire dalle funzioni elementari che permettono di leggerne gli
+identificatori, alle varie funzioni di manipolazione dei processi, che
+riguardano la loro creazione, terminazione, e la messa in esecuzione di altri
+programmi.
 
-Il programma che un processo sta eseguendo si chiama immagine del processo
-(\textit{process image}), le funzioni della famiglia \func{exec} permettono
-di caricare un'altro programma da disco sostituendo quest'ultimo alla process
-image corrente, questo fa si che la precedente immagine venga completamente
-cancellata e quando il nuovo programma esce anche il processo termina, senza
-ritornare alla precedente immagine.
-
-Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto
-particolari con caratteristiche uniche rispetto a tutte le altre, infatti la
-prima ritorna due volte (nel processo padre e nel figlio) mentre la seconda
-non ritorna mai (in quanto con essa viene eseguito un altro programma).
-
-
-
-\section{Il controllo dei processi}
-\label{sec:proc_control}
-
-Esamineremo in questa sezione le varie funzioni per il controllo dei processi:
-la loro creazione, la terminazione, l'esecuzione di altri programmi. Prima di
-trattare in dettaglio le singole funzioni, 
 
 \subsection{Gli identificatori dei processi}
-\label{sec:proc_id}
-
-Ogni processo viene identificato dal sistema da un numero identificativo
-unico, il \textit{process id} o \acr{pid}. Questo viene assegnato in forma
-progressiva ogni volta che un nuovo processo viene creato, fino ad un limite
-massimo (in genere essendo detto numero memorizzato in un intero a 16 bit si
-arriva a 32767) oltre il quale si riparte dal numero più basso disponibile
-(FIXME: verificare, non sono sicuro).  Per questo motivo processo il processo
-di avvio (init) ha sempre il pid uguale a uno.
-
-Ogni processo è identificato univocamente dal sistema per il suo pid;
-quest'ultimo è un tipo di dato standard, il \texttt{pid\_t} che in genere è un
-intero con segno (nel caso di Linux e delle glibc il tipo usato è
-\texttt{int}).
-
-Tutti i processi inoltre portano traccia del pid del genitore, chiamato in
-genere \textit{ppid} (da \textit{Parente Process Id}). Questi identificativi
-possono essere ottenuti da un programma usando le funzioni:
+\label{sec:proc_pid}
+
+Come accennato nell'introduzione ogni processo viene identificato dal sistema
+da un numero identificativo unico, il \textit{process id} o \acr{pid};
+quest'ultimo è un tipo di dato standard, il \type{pid\_t} che in genere è un
+intero con segno (nel caso di Linux e delle glibc il tipo usato è \type{int}).
+
+Il \acr{pid} viene assegnato in forma progressiva ogni volta che un nuovo
+processo viene creato, fino ad un limite massimo (in genere essendo detto
+numero memorizzato in un intero a 16 bit si arriva a 32767) oltre il quale si
+riparte dal numero più basso disponibile (FIXME: verificare, non sono sicuro).
+Per questo motivo processo il processo di avvio (\cmd{init}) ha sempre il
+\acr{pid} uguale a uno. 
+
+Tutti i processi inoltre memorizzano anche il \acr{pid} del genitore da cui
+sono stati creati, questo viene chiamato in genere \acr{ppid} (da
+\textit{parent process id}) ed è normalmente utilizzato per il controllo di
+sessione.  Questi due identificativi possono essere ottenuti da programma
+usando le funzioni:
 \begin{functions}
 \headdecl{sys/types.h}
 \headdecl{unistd.h}
 \funcdecl{pid\_t getpid(void)} restituisce il pid del processo corrente.
 \funcdecl{pid\_t getppid(void)} restituisce il pid del padre del processo
     corrente.
+Entrambe le funzioni non riportano condizioni di errore. 
 \end{functions}
+esempi dell'uso di queste funzioni sono riportati in
+\figref{fig:proc_fork_code}, nel programma di esempio \file{ForkTest.c}.
+
+Il fatto che il \acr{pid} sia un numero univoco per il sistema lo rende il
+candidato ideale per generare ulteriori indicatori associati al processo di
+cui diventa possibile garantire l'unicità: ad esempio la funzione
+\func{tmpname} (si veda \secref{sec:file_temp_file}) usa il \acr{pid} per
+generare un pathname univoco, che non potrà essere replicato da un'altro
+processo che usi la stessa funzione. 
 
+Tutti i processi figli dello stesso processo padre sono detti
+\textit{sibling}, questa è un'altra delle relazioni usate nel controllo di
+sessione, in cui si raggruppano tutti i processi creati su uno stesso
+terminale una volta che si è effettuato il login. Torneremo su questo
+argomento in \secref{cap:terminal}, dove esamineremo tutti gli altri
+identificativi associati ad un processo relativi al controllo di sessione.
 
 
 \subsection{La funzione \func{fork}}
 \label{sec:proc_fork}
 
-La funzione \func{fork} 
+La funzione \func{fork} è la funzione fondamentale della gestione dei
+processi: come si è detto l'unico modo di creare un nuovo processo è
+attraverso l'uso di questa funzione, essa quindi riveste un ruolo centrale
+tutte le volte che si devono scrivere programmi che usano il multitasking.  Il
+prototipo della funzione è:
 
+\begin{functions}
+  \headdecl{sys/types.h} 
+  \headdecl{unistd.h} 
+  \funcdecl{pid\_t fork(void)} 
+  Restituisce zero al padre e il \acr{pid} al figlio in caso di successo,
+  ritorna -1 al padre (senza creare il figlio) in caso di errore;
+  \texttt{errno} può assumere i valori:
+  \begin{errlist}
+  \item \macro{EAGAIN} non ci sono risorse sufficienti per creare un'altro
+    processo (per allocare la tabella delle pagine e le strutture del task) o
+    si è esaurito il numero di processi disponibili.
+  \item \macro{ENOMEM} non è stato possibile allocare la memoria per le
+    strutture necessarie al kernel per creare il nuovo processo.
+  \end{errlist}
+\end{functions}
 
-Dopo l'esecuzione di una fork sia il processo padre che il processo figlio
-continuano ad essere eseguiti normalmente, ed il processo figlio esegue
-esattamente lo stesso codice del padre. La sola differenza è che nel processo
-padre il valore di ritorno della funzione fork è il pid del processo figlio,
+Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che
+il processo figlio continuano ad essere eseguiti normalmente alla istruzione
+seguente la \func{fork}; il processo figlio è però una copia del padre, e
+riceve una copia dei segmenti di testo, stack e dati (vedi
+\secref{sec:proc_mem_layout}), ed esegue esattamente lo stesso codice del
+padre, ma la memoria è copiata, non condivisa\footnote{In generale il segmento
+  di testo, che è identico, è condiviso e tenuto in read-only, Linux poi
+  utilizza la tecnica del \textit{copy-on-write}, per cui la memoria degli
+  altri segmenti viene copiata dal kernel per il nuovo processo solo in caso
+  di scrittura, rendendo molto più efficiente il meccanismo} pertanto padre e
+figlio vedono variabili diverse.
+
+La differenza che si ha nei due processi è che nel processo padre il valore di
+ritorno della funzione fork è il \acr{pid} del processo figlio, mentre nel
+figlio è zero; in questo modo il programma può identificare se viene eseguito
+dal padre o dal figlio.
+Si noti come la funzione \func{fork} ritorni \textbf{due} volte: una nel padre
+e una nel figlio. La sola differenza che si ha nei due processi è il valore di
+ritorno restituito dalla funzione, che nel padre è il \acr{pid} del figlio
 mentre nel figlio è zero; in questo modo il programma può identificare se
 viene eseguito dal padre o dal figlio. 
 
-
+La scelta di questi valori non è casuale, un processo infatti può avere più
+figli, ed il valore di ritorno di \func{fork} è l'unico modo che permette di
+identificare quello appena creato; al contrario un figlio ha sempre un solo
+padre (il cui \acr{pid} può sempre essere ottenuto con \func{getppid}, vedi
+\secref{sec:proc_pid}) e si usa il valore nullo, che non può essere il
+\acr{pid} di nessun processo.
+
+\begin{figure}[!htb]
+  \footnotesize
+  \begin{lstlisting}{}
+#include <errno.h>       /* error definitions and routines */ 
+#include <stdlib.h>      /* C standard library */
+#include <unistd.h>      /* unix standard library */
+#include <stdio.h>       /* standard I/O library */
+#include <string.h>      /* string functions */
+
+/* Help printing routine */
+void usage(void);
+
+int main(int argc, char *argv[])
+{
+/* 
+ * Variables definition  
+ */
+    int nchild, i;
+    pid_t pid;
+    int wait_child  = 0;
+    int wait_parent = 0;
+    int wait_end    = 0;
+    ...        /* handling options */
+    nchild = atoi(argv[optind]);
+    printf("Test for forking %d child\n", nchild);
+    /* loop to fork children */
+    for (i=0; i<nchild; i++) {
+        if ( (pid = fork()) < 0) { 
+            /* on error exit */ 
+            printf("Error on %d child creation, %s\n", i+1, strerror(errno));
+            exit(-1); 
+        }
+        if (pid == 0) {   /* child */
+            printf("Child %d successfully executing\n", ++i);
+            if (wait_child) sleep(wait_child);
+            printf("Child %d, parent %d, exiting\n", i, getppid());
+            exit(0);
+        } else {          /* parent */
+            printf("Spawned %d child, pid %d \n", i+1, pid);
+            if (wait_parent) sleep(wait_parent);
+            printf("Go to next child \n");
+        }
+    }
+    /* normal exit */
+    if (wait_end) sleep(wait_end);
+    return 0;
+}
+  \end{lstlisting}
+  \caption{Esempio di codice per la creazione di nuovi processi.}
+  \label{fig:proc_fork_code}
+\end{figure}
+
+Normalmente la chiamata a \func{fork} può fallire solo per due ragioni, o ci
+sono già troppi processi nel sistema (il che di solito è sintomo che
+qualcos'altro non sta andando per il verso giusto) o si è ecceduto il limite
+sul numero totale di processi permessi all'utente (il valore della costante
+\macro{CHILD\_MAX} definito in \file{limits.h}, che fa riferimento ai processo
+con lo stesso \textit{real user id}).
+
+L'uso di \func{fork} avviene secondo due modalità principali; la prima è
+quella in cui all'interno di un programma si creano processi figli per
+affidargli l'esecuzione di una certa sezione di codice, mentre il processo
+padre ne esegue un'altra. È il caso tipico dei server di rete in cui il padre
+riceve ed accetta le richieste da parte dei client, per ciascuna delle quali
+pone in esecuzione un figlio che è incaricato di fornire il servizio.
+
+La seconda modalità è quella in cui il processo vuole eseguire un altro
+programma; questo è ad esempio il caso della shell. In questo caso il processo
+crea un figlio la cui unica operazione è quella fare una \func{exec} (di cui
+parleremo in \secref{sec:proc_exec}) subito dopo la \func{fork}.
+
+Alcuni sistemi operativi (il VMS ad esempio) combinano le operazioni di questa
+seconda modalità (una \func{fork} seguita da una \func{exec}) in un'unica
+operazione che viene chiamata \textit{spawn}. Nei sistemi unix-like è stato
+scelto di mantenere questa separazione, dato che, come visto per la prima
+modalità d'uso, esistono numerosi scenari in cui si può usare una \func{fork}
+senza bisogno di una \func{exec}. Inoltre anche nel caso della seconda
+modalità di operazioni, avere le due funzioni separate permette al figlio di
+cambiare gli attributi del processo (maschera dei segnali, redirezione
+dell'output, \textit{user id}) prima della \func{exec}, rendendo molto più
+flessibile la possibilità di modificare gli attributi del nuovo processo.
+
+In \curfig\ si è riportato il corpo del codice del programma di esempio
+\cmd{forktest}, che ci permette di illustrare molte caratteristiche dell'uso
+della funzione \func{fork}. Il programma permette di creare un numero di figli
+specificato a linea di comando, e prende anche alcune opzioni per indicare
+degli eventuali tempi di attesa in secondi (eseguiti tramite la funzione
+\func{sleep}) per il padre ed il figlio (con \cmd{forktest -h} si ottiene la
+descrizione delle opzioni); il codice completo, compresa la parte che gestisce
+le opzioni a riga di comando, è disponibile nel file \file{ForkTest.c}.
+
+Decifrato il numero di figli da creare, il ciclo principale del programma
+(\texttt{\small 28--40}) esegue in successione la creazione dei processi figli
+controllando il successo della chiamata a \func{fork} (\texttt{\small
+  29--31}); ciascun figlio (\texttt{\small 29--31}) si limita a stampare il
+suo numero di successione, eventualmente attendere il numero di secondi
+specificato e scrivere un messaggio prima di uscire. Il processo padre invece
+(\texttt{\small 29--31}) stampa un messaggio di creazione, eventualmente
+attende il numero di secondi specificato, e procede nell'esecuzione del ciclo;
+alla conclusione del ciclo, prima di uscire, può essere specificato un altro
+periodo di attesa.
+
+Se eseguiamo il comando senza specificare attese (come si può notare in
+\texttt{\small 17--19} i valori di default specificano di non attendere),
+otterremo come output sul terminale:
+\begin{verbatim}
+[piccardi@selidor sources]$ ./forktest 3
+Test for forking 3 child
+Spawned 1 child, pid 1964 
+Child 1 successfully executing
+Child 1, parent 1963, exiting
+Go to next child 
+Spawned 2 child, pid 1965 
+Child 2 successfully executing
+Child 2, parent 1963, exiting
+Go to next child 
+Child 3 successfully executing
+Child 3, parent 1963, exiting
+Spawned 3 child, pid 1966 
+Go to next child 
+\end{verbatim} %$
+
+Esaminiamo questo risultato: una prima conclusione che si può trarre è non si
+può dire quale processo fra il padre ed il figlio venga eseguito per
+primo\footnote{anche se nel kernel 2.4.x era stato introdotto un meccanismo
+  che metteva in esecuzione sempre il xxx per primo (TODO recuperare le
+  informazioni esatte)} dopo la chiamata a \func{fork}; dall'esempio si può
+notare infatti come nei primi due cicli sia stato eseguito per primo il padre
+(con la stampa del \acr{pid} del nuovo processo) per poi passare
+all'esecuzione del figlio (completata con i due avvisi di esecuzione ed
+uscita), e tornare all'esecuzione del padre (con la stampa del passaggio al
+ciclo successivo), mentre la terza volta è stato prima eseguito il figlio
+(fino alla conclusione) e poi il padre.
+
+In generale l'ordine di esecuzione dipenderà, oltre che dall'algoritmo di
+scheduling usato dal kernel, dalla particolare situazione in si trova la
+macchina al momento della chiamata, risultando del tutto impredicibile.
+Eseguendo più volte il programma di prova e producendo un numero diverso di
+figli, si sono ottenute situazioni completamente diverse, compreso il caso in
+cui il processo padre ha eseguito più di una \func{fork} prima che uno dei
+figli venisse messo in esecuzione.
+
+Pertanto non si può fare nessuna assunzione sulla sequenza di esecuzione delle
+istruzioni del codice fra padre e figli, nè sull'ordine in cui questi potranno
+essere messi in esecuzione, e se è necessaria una qualche forma di precedenza
+occorrerà provvedere ad espliciti meccanismi di sincronizzazione, pena il
+rischio di incorrere nelle cosiddette \textit{race conditions}.
+
+Si noti inoltre che, come accennato, essendo i segmenti di memoria utilizzati
+dai singoli processi completamente separati, le modifiche delle variabili nei
+processi figli (come l'incremento di \var{i} in \texttt{\small 33}) sono
+visibili solo al loro interno, e non hanno alcun effetto sul valore che le
+stesse variabili hanno nel processo padre (ed in eventuali altri processi
+figli che eseguano lo stesso codice).
+
+Un secondo aspetto molto importante nella creazione dei processi figli è
+quello dell'interazione dei vari processi con i file; per illustrarlo meglio
+proviamo a redirigere su un file l'output del nostro programma di test, quello
+che otterremo è:
+\begin{verbatim}
+[piccardi@selidor sources]$ ./forktest 3 > output
+[piccardi@selidor sources]$ cat output
+Test for forking 3 child
+Child 1 successfully executing
+Child 1, parent 1967, exiting
+Test for forking 3 child
+Spawned 1 child, pid 1968 
+Go to next child 
+Child 2 successfully executing
+Child 2, parent 1967, exiting
+Test for forking 3 child
+Spawned 1 child, pid 1968 
+Go to next child 
+Spawned 2 child, pid 1969 
+Go to next child 
+Child 3 successfully executing
+Child 3, parent 1967, exiting
+Test for forking 3 child
+Spawned 1 child, pid 1968 
+Go to next child 
+Spawned 2 child, pid 1969 
+Go to next child 
+Spawned 3 child, pid 1970 
+Go to next child 
+\end{verbatim}
+che come si vede è completamente diverso da quanto ottenevamo sul terminale.
+
+Il comportamento delle varie funzioni di interfaccia con i file è analizzato
+in gran dettaglio in \capref{cha:file_unix_interface} e in
+\secref{cha:files_std_interface}. Qui basta accennare che si sono usate le
+funzioni standard della libreria del C che prevedono l'output bufferizzato; e
+questa bufferizzazione varia a seconda che si tratti di un file su disco (in
+cui il buffer viene scaricato su disco solo quando necessario) o di un
+terminale (nel qual caso il buffer viene scaricato ad ogni a capo).
+
+Nel primo esempio allora avevamo che ad ogni chiamata a \func{printf} il
+buffer veniva scaricato, e le singole righe erano stampate a video subito dopo
+l'esecuzione della \func{printf}. Ma con la redirezione su file la scrittura
+non avviene più alla fine di ogni riga e l'output resta nel buffer, per questo
+motivo, dato che ogni figlio riceve una copia della memoria del padre, esso
+riceverà anche quanto c'è nel buffer delle funzioni di I/O, comprese le linee
+scritte dal padre fino allora. Così quando all'uscita del figlio il buffer
+viene scritto su disco, troveremo nel file anche tutto quello che il processo
+padre aveva scritto prima della sua creazione.  E alla fine del file, dato che
+in questo caso il padre esce per ultimo, troviamo anche l'output del padre.
+
+Ma l'esempio ci mostra un'altro aspetto fondamentale dell'interazione con i
+file, che era valido anche per l'esempio precedente, ma meno evidente; il
+fatto cioè che non solo processi diversi possono scrivere in contemporanea
+sullo stesso file (l'argomento della condivisione dei file in unix è trattato
+in dettaglio in \secref{sec:file_sharing}), ma anche che, a differenza di
+quanto avviene per le variabili, la posizione corrente sul file è condivisa 
+fra il padre e tutti i processi figli. 
+
+Quello che succede è che quando lo standard output del padre viene rediretto,
+lo stesso avviene anche per tutti i figli; la funzione \func{fork} infatti ha
+la caratteristica di duplicare (allo stesso modo in cui lo fa la funzione
+\func{dup}, trattata in \secref{sec:file_dup}) nei figli tutti i file
+descriptor aperti nel padre, il che comporta che padre e figli condividono
+le stesse voci della file table (per la spiegazione di questi termini si veda
+\secref{sec:file_sharing} e referenza a figura da fare) e quindi anche
+l'offset corrente nel file.
+
+In questo modo se un processo scrive sul file aggiornerà l'offset sulla file
+table, e tutti gli altri processi che condividono la file table vedranno il
+nuovo valore; in questo modo si evita, in casi come quello appena mostrato in
+cui diversi processi scrivono sullo stesso file, che l'output successivo di un
+processo vada a sovrapporsi a quello dei precedenti (l'output potrà risultare
+mescolato, ma non ci saranno parti perdute per via di una sovrascrittura).
+
+Questo tipo di comportamento è essenziale in tutti quei casi in cui il padre
+crea un figlio ed attende la sua conclusione per proseguire, ed entrambi
+scrivono sullo stesso file, ad esempio lo standard output (un caso tipico è la
+shell). Se l'output viene rediretto con questo comportamento avremo che il
+padre potrà continuare a scrivere automaticamente in coda a quanto scritto dal
+figlio; se così non fosse ottenere questo comportamento sarebbe estremamente
+complesso necessitando di una qualche forma di comunicazione fra i due
+processi.
+
+In generale comunque non è buona norma far scrivere più processi sullo stesso
+file senza una qualche forma di sincronizzazione in quanto, come visto con il
+nostro esempio, le varie scritture risulteranno mescolate fra loro in una
+sequenza impredicibile. Le modalità con cui in genere si usano i file dopo una
+\func{fork} sono sostanzialmente due:
+\begin{enumerate}
+\item Il processo padre aspetta la conclusione del figlio. In questo caso non
+  è necessaria nessuna azione riguardo ai file, in quanto la sincronizzazione
+  degli offset dopo eventuali operazioni di lettura e scrittura effettuate dal
+  figlio è automatica.
+\item L'esecuzione di padre e figlio procede indipendentemente. In questo caso
+  ciascuno dei due deve chiudere i file che non gli servono una volta che la
+  \func{fork} è stata eseguita, per evitare ogni forma di interferenza.
+\end{enumerate}
+
+Oltre ai file aperti i processi figli ereditano dal padre una serie di altre
+proprietà comuni; in dettaglio avremo che dopo l'esecuzione di una \func{fork}
+padre e figlio avranno in comune:
+\begin{itemize}
+\item i file aperti (e gli eventuali flag di \textit{close-on-exec} se
+  settati).
+\item gli identificatori per il controllo di accesso: il \textit{real user
+    id}, il \textit{real group id}, l'\textit{effective user id},
+  l'\textit{effective group id} e i \textit{supplementary group id} (vedi
+  \secref{tab:proc_uid_gid}).
+\item gli identificatori per il controllo di sessione: il \textit{process
+    group id} e il \textit{session id} e il terminale di controllo.
+\item i flag \acr{suid} e \acr{suid} (vedi \secref{sec:file_suid_sgid}).
+\item la directory di lavoro e la directory radice (vedi
+  \secref{sec:file_work_dir}).
+\item la maschera dei permessi di creazione (vedi \secref{sec:file_umask}).
+\item la maschera dei segnali.
+\item i segmenti di memoria condivisa agganciati al processo. 
+\item i limiti sulle risorse
+\item le variabili di ambiente (vedi \secref{sec:proc_environ}).
+\end{itemize}
+le differenze invece sono:
+\begin{itemize}
+\item il valore di ritorno di \func{fork}.
+\item il \textit{process id}. 
+\item il \textit{parent process id} (quello del figlio viene settato al
+  \acr{pid} del padre).
+\item i valori dei tempi di esecuzione (\var{tms\_utime}, \var{tms\_stime},
+  \var{tms\_cutime}, \var{tms\_uetime}) che nel figlio sono posti a zero.
+\item i \textit{file lock}, che non vengono ereditati dal figlio.
+\item gli allarmi pendenti, che per il figlio vengono cancellati.
+\end{itemize}
+
+
+\subsection{La funzione \func{vfork}}
+\label{sec:proc_vfork}
+
+La funzione \func{vfork} è esattamente identica a \func{fork} ed ha la stessa
+semantica e gli stessi errori; la sola differenza è che non viene creata la
+tabella delle pagine né la struttura dei task per il nuovo processo. Il
+processo padre è posto in attesa fintanto che il figlio non ha eseguito una
+\func{execve} o non è uscito con una \func{\_exit}. Il figlio condivide la
+memoria del padre (e modifiche possono avere effetti imprevedibili) e non deve
+ritornare o uscire con \func{exit} ma usare esplicitamente \func{\_exit}.
+
+Questa funzione è un rimasuglio dei vecchi tempi in cui eseguire una
+\func{fork} comportava anche la copia completa del segmento dati del processo
+padre, che costituiva un inutile appesantimento in tutti quei casi in cui la
+\func{fork} veniva fatto solo per poi eseguire una \func{exec}. La funzione
+venne introdotta in BSD per migliorare le prestazioni.
+
+Dato che Linux supporta il \textit{copy on write} la perdita di prestazioni è
+assolutamente trascurabile, e l'uso di questa funzione (che resta un caso
+speciale della funzione \func{clone}), è deprecato, per questo eviteremo di
+trattarla ulteriormente.
+
+
+\subsection{La conclusione di un processo.}
+\label{sec:proc_termination}
+
+In \secref{sec:proc_conclusion} abbiamo già affrontato le modalità con cui
+concludere un programma, ma dal punto di vista del programma stesso; avendo a
+che fare con un sistema multitasking occorre adesso affrontare l'argomento dal
+punto di vista generale di come il sistema gestisce la conclusione dei
+processi.
+
+Abbiamo già visto in \secref{sec:proc_conclusion} le tre modalità con cui un
+programma viene terminato in maniera normale: la chiamata di \func{exit} (che
+esegue le funzioni registrate per l'uscita e chiude gli stream), il ritorno
+dalla funzione \func{main} (equivalente alla chiamata di \func{exit}), e la
+chiamata ad \func{\_exit} (che passa direttamente alle operazioni di
+terminazione del processo da parte del kernel).
+
+Ma oltre alla conclusione normale abbiamo accennato che esistono anche delle
+modalità di conclusione anomala; queste sono in sostanza due: il programma può
+chiamare la funzione \func{abort} per invocare una chiusura anomala, o essere
+terminato da un segnale.  In realtà anche la prima modalità si riconduce alla
+seconda, dato che \func{abort} si limita a generare il segnale
+\macro{SIGABRT}.
+
+Qualunque sia la modalità di conclusione di un processo, il kernel esegue
+comunque una serie di operazioni: chiude tutti i file aperti, rilascia la
+memoria che stava usando, e così via; l'elenco completo delle operazioni
+eseguite alla chiusura di un processo è il seguente:
+\begin{itemize}
+\item tutti i descrittori dei file sono chiusi.
+\item viene memorizzato lo stato di terminazione del processo.
+\item ad ogni processo figlio viene assegnato un nuovo padre.
+\item viene inviato il segnale \macro{SIGCHLD} al processo padre.
+\item se il processo è un leader di sessione viene mandato un segnale di
+  \macro{SIGHUP} a tutti i processi in background e il terminale di controllo
+  viene disconnesso.
+\item se la conclusione di un processe rende orfano un \textit{process group}
+  ciascun membro del gruppo viene bloccato, e poi gli vengono inviati in
+  successione i segnali \macro{SIGHUP} e \macro{SIGCONT}.
+\end{itemize}
+ma al di la di queste operazioni è necessario poter disporre di un meccanismo
+ulteriore che consenta di sapere come questa terminazione è avvenuta; dato che
+in un sistema unix-like tutto viene gestito attraverso i processi il
+meccanismo scelto consiste nel riportare lo stato di terminazione
+(\textit{termination status}) di cui sopra al processo padre.
+
+Nel caso di conclusione normale, lo stato di uscita del processo viene
+caratterizzato tremite il valore del cosiddetto \textit{exit status}, cioè il
+valore passato alle funzioni \func{exit} o \func{\_exit} (o dal valore di
+ritorno per \func{main}).  Ma se il processo viene concluso in maniera anomala
+il programma non può specificare nessun \textit{exit status}, ed è il kernel
+che deve generare autonomamente il \textit{termination status} per indicare le
+ragioni della conclusione anomala.  
+
+Si noti la distinzione fra \textit{exit status} e \textit{termination status}:
+quello che contraddistingue lo stato di chiusura del processo e viene
+riportato attraverso le funzioni \func{wait} o \func{waitpid} (vedi
+\secref{sec:proc_wait}) è sempre quest'ultimo; in caso di conclusione normale
+il kernel usa il primo (nel codice eseguito da \func{\_exit}) per produrre il
+secondo.
+
+La scelta di riportare al padre lo stato di terminazione dei figli, pur
+essendo l'unica possibile, comporta comunque alcune complicazioni: infatti se
+alla sua creazione è scontato che ogni nuovo processo ha un padre, non è detto
+che sia così alla sua conclusione, dato che il padre protrebbe essere già
+terminato (si potrebbe avere cioè quello che si chiama un processo
+\textsl{orfano}). 
+
+Questa complicazione viene superata facendo in modo che il processo figlio
+venga \textsl{adottato} da \cmd{init}: come già accennato quando un processo
+termina il kernel controlla se è il padre di altri processi in esecuzione: in
+caso positivo allora il \acr{ppid} di tutti questi processi viene sostituito
+con il \acr{pid} di \cmd{init} (e cioè con 1); in questo modo ogni processo
+avrà sempre un padre (nel caso \textsl{adottivo}) cui riportare il suo stato
+di terminazione.  Come verifica di questo comportamento eseguiamo il comando
+\cmd{forktest -c2 3}, in questo modo ciascun figlio attenderà due secondi
+prima di uscire, il risultato è:
+\begin{verbatim}
+[piccardi@selidor sources]$ ./forktest -c2 3
+Test for forking 3 child
+Spawned 1 child, pid 1973 
+Child 1 successfully executing
+Go to next child 
+Spawned 2 child, pid 1974 
+Child 2 successfully executing
+Go to next child 
+Child 3 successfully executing
+Spawned 3 child, pid 1975 
+Go to next child 
+[piccardi@selidor sources]$ Child 3, parent 1, exiting
+Child 2, parent 1, exiting
+Child 1, parent 1, exiting
+\end{verbatim}
+come si può notare in questo caso il processo padre si conclude prima dei
+figli, tornando alla shell, che stampa il prompt sul terminale: circa due
+secondi dopo viene stampato a video anche l'output dei tre figli che
+terminano, e come si può notare in questo caso, al contrario di quanto visto
+in precedenza, essi riportano 1 come \acr{ppid}.
+
+Altrettanto rilevante è il caso in cui il figlio termina prima del padre,
+questo perché non è detto che il padre possa ricevere immediatamente lo stato
+di terminazione, quindi il kernel deve comunque conservare una certa quantità
+di informazioni riguardo ai processi che sta terminando.
+
+Questo viene fatto mantenendo attiva la voce nella tabella dei processi, e
+memorizzando alcuni dati essenziali, come il \acr{pid}, i tempi di CPU usati
+dal processo (vedi \secref{sec:intro_unix_time}) e lo stato di terminazione
+(NdA verificare esattamente cosa c'è!), mentre la memoria in uso ed i file
+aperti vengono rilasciati immediatamente. I processi che sono terminati, ma il
+cui stato di terminazione non è stato ancora ricevuto dal padre sono chiamati
+\textit{zombie}, essi restano presenti nella tabella dei processi ed in genere
+possono essere identificati dall'output di \cmd{ps} per la presenza di una
+\cmd{Z} nella colonna che ne indica lo stato. Quando il padre effettuarà la
+lettura dello stato di uscita anche questa informazione, non più necessaria,
+verrà scartata e la terminazione potrà dirsi completamente conclusa.
+
+Possiamo utilizzare il nostro programma di prova per analizzare anche questa
+condizione: lanciamo il comando \cmd{forktest -e10 3 &} in background,
+indicando al processo padre di aspettare 10 secondi prima di uscire; in questo
+caso, usando \cmd{ps} sullo stesso terminale (prima dello scadere dei 10
+secondi) otterremo:
+\begin{verbatim}
+[piccardi@selidor sources]$ ps T
+  PID TTY      STAT   TIME COMMAND
+  419 pts/0    S      0:00 bash
+  568 pts/0    S      0:00 ./forktest -e10 3
+  569 pts/0    Z      0:00 [forktest <defunct>]
+  570 pts/0    Z      0:00 [forktest <defunct>]
+  571 pts/0    Z      0:00 [forktest <defunct>]
+  572 pts/0    R      0:00 ps T
+\end{verbatim} %$
+e come si vede, dato che non si è fatto nulla per riceverne lo stato di
+terminazione, i tre processi figli sono ancora presenti pur essendosi
+conclusi, con lo stato di zombie e l'indicazione che sono stati terminati.
+
+La possibilità di avere degli zombie deve essere tenuta presente quando si
+scrive un programma che deve essere mantenuto in esecuzione a lungo e creare
+molti figli. In questo caso si deve sempre avere cura di far leggere
+l'eventuale stato di uscita di tutti i figli (in genere questo si fa
+attraverso un apposito \textit{signal handler}, che chiama la funzione
+\func{wait} vedi \secref{sec:sig_xxx} e \secref{sec:proc_wait}). Questa
+operazione è necessaria perché anche se gli \textit{zombie} non consumano
+risorse di memoria o processore, occupano comunque una voce nella tabella dei
+processi, che a lungo andare potrebbe esaurirsi.
+
+Si noti che quando un processo adottato da \cmd{init} termina esso non diviene
+uno \textit{zombie}, in quanto una delle funzioni di \cmd{init} è appunto
+quella di chiamare \func{wait} per i processi di cui fa da padre. Questo è
+quanto avviene ad esempio nel caso dell'ultimo esempio: scaduti i dieci
+secondi \cmd{forktest} esce, siccome i suoi figli vengono ereditati da
+\cmd{init} il quale provvederà.
 
 
 \subsection{Le funzioni \texttt{wait} e  \texttt{waitpid}}
 \label{sec:proc_wait}
 
+
 \subsection{Le funzioni \texttt{exec}}
 \label{sec:proc_exec}
 
@@ -184,8 +697,79 @@ viene eseguito dal padre o dal figlio.
 \section{Il controllo di accesso}
 \label{sec:proc_perms}
 
-Va messo qui tutta la storia su effective, real, saved uid, e pure le cose di
-Linux come il filesystem uid.
+In questa sezione esamineremo le problematiche relative al controllo di
+accesso dal punto di vista del processi; gli identificativi usati, come questi
+vengono modificati nella creazione e nel lancio di nuovi processi, e le varie
+funzioni per la loro manipolazione diretta.
+
+
+\subsection{Utente e gruppo di un processo}
+\label{sec:proc_user_group}
+
+Abbiamo già accennato in \secref{sec:intro_multiuser} ad ogni utente ed gruppo
+sono associati due identificatori univoci, lo \acr{uid} e il \acr{gid} che li
+contraddistinguono nei confronti del kernel. Questi identificatori stanno alla
+base del sistema di permessi e protezioni di un sistema unix, e vengono usati
+anche nella gestione dei privilegi di accesso dei processi.
+
+In realtà ad ogni processo è associato un certo numero di identificatori, il
+cui elenco è riportato \ntab, in genere questi derivano direttamente
+dall'utente che ha lanciato il processo (attraverso i valori di \acr{uid} e
+\acr{gid}), e vengono usati sia per il controllo di accesso ai file che per la
+gestione dei privilegi associati ai processi stessi.
+\begin{table}[htb]
+  \centering
+  \begin{tabular}[c]{|c|l|l|}
+    \hline
+    Sigla & Significato & Utilizzo \\ 
+    \hline
+    \hline
+    \acr{ruid} & \textit{real user id} & indica l'utente reale che ha lanciato
+    il programma\\ 
+    \acr{rgid} & \textit{real group id} & indica il gruppo reale dell'utente 
+    che ha lanciato il programma \\ 
+    \acr{euid} & \textit{effective user id} & indica l'utente effettivo usato
+    dal programma \\ 
+    \acr{egid} & \textit{effective group id} & indica il gruppo effettivo usato
+    dal programma \\ 
+               & \textit{supplementary group id} & indica i gruppi cui
+    l'utente appartiene  \\ 
+    \acr{suid} & \textit{saved user id} & indica l'utente  \\ 
+    \acr{sgid} & \textit{daved group id} & indica il gruppo  \\ 
+    \acr{fsuid} & \textit{filesystem user id} & indica l'utente effettivo per
+    il filesystem \\ 
+    \acr{fsgid} & \textit{filesystem group id} & indica il gruppo effettivo
+    per il filesystem  \\ 
+    \hline
+  \end{tabular}
+  \caption{Identificatori di utente e gruppo associati a ciascun processo.}
+  \label{tab:proc_uid_gid}
+\end{table}
+
+Il \textit{real user id} e il \textit{real group id} indicano l'utente che ha
+lanciato il processo, e vengono settati al login al valore standard di
+\acr{uid} e \acr{gid} dell'utente letti direttamente da \file{/etc/passwd}.
+Questi non vengono mai cambiati nella creazione di nuovi processi e restano
+sempre gli stessi per tutti i processi avviati in una sessione. In realtà è
+possibile modificarli (vedi \secref{sec:proc_setuid}), ma solo per un processo
+che abbia i privilegi di amministratore (ed è così infatti che \cmd{login},
+che gira con i privilegi di amministratore, li setta ai valori corrispondenti
+all'utente che entra nel sistema).
+
+L'\textit{effective user id}, l'\textit{effective group id} e gli eventuali
+\textit{supplementary group id} sono gli identificativi usati per il controllo
+di accesso ai file secondo quanto descritto in dettaglio in
+\secref{sec:file_perm_overview}. Normalmente sono uguali al \textit{real user
+  id} e al \textit{real group id}, a meno che il file posto in esecuzione non
+abbia i bit \acr{suid} o \acr{sgid} settati, nel qual caso vengono settati
+rispettivamente all'\acr{uid} e \acr{gid} del file.
+
+Il \textit{saved user id} e il \textit{saved group id} sono copie
+dell'\textit{effective user id} e dell'\textit{effective group id} del
+processo padre, e vengono settati all'avvio del processo, prima che
+\textit{effective user id} e \textit{effective group id} vengano modificati
+per tener conto di eventuali \acr{suid} o \acr{sgid}.
+
 
 \subsection{Le funzioni \texttt{setuid} e \texttt{setgid}}
 \label{sec:proc_setuid}