Aggiunti zombie, orfani, e lupi mannari.
[gapil.git] / prochand.tex
index 22da5c0194b660c71df60ba5a39403221ef5c9c3..b74cacdfa791b708e52caae0748dba8264e131ae 100644 (file)
@@ -78,9 +78,10 @@ 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 \func{fork} chiamando la
-funzione \func{wait} o la funzione \func{waitpid}; queste funzioni
-restituiscono anche una informazione abbastanza limitata (il codice di uscita)
-sulle cause della terminazione del processo.
+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 \func{exit} (si veda
@@ -116,7 +117,7 @@ non ritorna mai (in quanto con essa viene eseguito un altro programma).
 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 lore creazione, terminazione, e la messa in esecuzione di altri
+riguardano la loro creazione, terminazione, e la messa in esecuzione di altri
 programmi.
 
 
@@ -146,12 +147,13 @@ usando le funzioni:
 \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 ultieriori indicatori associati al processo di
+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
@@ -168,17 +170,16 @@ 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 fondamentale della gestione dei processi
-in unix; come si è detto l'unico modo di creare un nuovo processo è attraverso
-l'uso di questa funzione, che è quindi la base per il multitasking; il protipo
-della funzione è:
+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:
@@ -191,22 +192,34 @@ della funzione 
   \end{errlist}
 \end{functions}
 
-Dopo l'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.
+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
@@ -225,33 +238,34 @@ int main(int argc, char *argv[])
 /* 
  * Variables definition  
  */
-    int i;
-    int nchild;
+    int nchild, i;
     pid_t pid;
-
+    int wait_child  = 0;
+    int wait_parent = 0;
+    int wait_end    = 0;
     ...        /* handling options */
-
-    /* There must be remaing parameters */
-    if (optind == argc) {
-        usage();
-    }
     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) {
-            printf("Error on %d child creation, %s\n", i, strerror(errno));
+        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++);
-            sleep(2);
-            printf("Child %d exiting\n", i);
+            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, pid);
+            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}
@@ -259,80 +273,415 @@ int main(int argc, char *argv[])
   \label{fig:proc_fork_code}
 \end{figure}
 
-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 comunque 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}, vista in \secref{sec:proc_pid}) e si usa il valore nullo, che
-non può essere il \acr{pid} di nessun processo.
-
-In \curfig\ si è riportato il corpo del codice dell'esempio \cmd{forktest},
-che ci permette di illustrare l'uso della funzione \func{fork}, creando un
-numero di figli specificato a linea di comando; il codice completo, compresa
-la parte che gestisce le opzioni a riga di comando, è disponibile nel file
-\file{ForkTest.c}.
+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, attendere 3 secondi e scrivere un messaggio prima
-di uscire. Il processo padre invece (\texttt{\small 29--31}) stampa un
-messaggio di creazione e procede nell'esecuzione del ciclo. Se eseguiamo il
-comando otterremo come output sul terminale:
+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 5
-Test for forking 5 child
-Spawned 1 child, pid 840 
+[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
-Spawned 2 child, pid 841 
-Spawned 3 child, pid 842 
+Child 2, parent 1963, exiting
+Go to next child 
 Child 3 successfully executing
-Spawned 4 child, pid 843 
-Child 4 successfully executing
-Child 5 successfully executing
-Spawned 5 child, pid 844 
-[piccardi@selidor sources]$ Child 2 exiting
-Child 1 exiting
-Child 4 exiting
-Child 3 exiting
-Child 5 exiting
-\end{verbatim}
-
-Come si vede 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}, nel caso
-mostrato sopra ad esempio si può notare come dopo la creazione il secondo ed
-il quinto figlio sia stato stati eseguiti per primi, mantre per gli altri
-figli è stato eseguito per primo il padre. 
+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, si sono ottenute situazioni
-completamente diverse, compreso caso in cui il processo padre ha eseguito più
-di una \func{fork} prima che uno dei figli venisse messo in
-esecuzione. 
+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, 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 ricordi 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}) saranno effettive solo per essi, e non hanno alcun effetto sul valore
-che le stesse variabili hanno nel processo padre.
+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}}
@@ -359,7 +708,7 @@ funzioni per la loro manipolazione diretta.
 
 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 confonti del kernel. Questi identificatori stanno alla
+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.