Quattro chiacchiere sullo scheduling
[gapil.git] / prochand.tex
index 70d7eb1d2e90ce7d0fd54c5b4f30b6eb6efb4483..4c16fe24584e65fc4321a5a33012e3a4adcc603d 100644 (file)
@@ -1,17 +1,17 @@
 \chapter{La gestione dei processi}
 \label{cha:process_handling}
 
-Come accennato nell'introduzione in un sistema Unix ogni attività del sistema
-viene svolta tramite i processi.  In sostanza i processi costituiscono l'unità
-base per l'allocazione e l'uso delle risorse del sistema.
+Come accennato nell'introduzione in un sistema Unix tutte le operazioni
+vengono svolte tramite opportuni processi.  In sostanza questi ultimi vengono
+a costituire l'unità base per l'allocazione e l'uso delle risorse del sistema.
 
 Nel precedente capitolo abbiamo esaminato il funzionamento di un processo come
 unità a se stante, in questo esamineremo il funzionamento dei processi
 all'interno del sistema. Saranno cioè affrontati i dettagli della creazione e
-della distruzione dei processi, della gestione dei loro attributi e privilegi,
-e di tutte le funzioni a questo connesse. Infine nella sezione finale
-affronteremo alcune problematiche generiche della programmazione in ambiente
-multitasking.
+della terminazione dei processi, della gestione dei loro attributi e
+privilegi, e di tutte le funzioni a questo connesse. Infine nella sezione
+finale introdurremo alcune problematiche generiche della programmazione in
+ambiente multitasking.
 
 
 
@@ -163,8 +163,7 @@ 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} (si veda
 \secref{sec:proc_wait}); queste funzioni restituiscono anche una informazione
-abbastanza limitata (lo stato di terminazione) sulle cause della terminazione
-del processo figlio.
+abbastanza limitata sulle cause della terminazione del processo figlio.
 
 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
@@ -216,9 +215,9 @@ intero con segno (nel caso di Linux e delle \acr{glibc} il tipo usato 
 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. 
+riparte dal numero più basso disponibile\footnote{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
@@ -236,12 +235,12 @@ ottenuti da programma usando le funzioni:
 \noindent 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. 
+Il fatto che il \acr{pid} sia un numero univoco per il sistema lo rende un
+candidato per generare ulteriori indicatori associati al processo di cui
+diventa possibile garantire l'unicità: ad esempio in alcune implementazioni 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 è una delle relazioni usate nel \textsl{controllo di
@@ -291,12 +290,12 @@ 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
+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 della creazione di
-  un nuovo processo}, pertanto padre e figlio vedono variabili diverse.
+  un nuovo processo.} 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 \func{fork} è il \acr{pid} del processo figlio, mentre
@@ -369,11 +368,11 @@ qualcos'altro non sta andando per il verso giusto) o si 
 sul numero totale di processi permessi all'utente (vedi \secref{sec:sys_xxx}).
 
 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.
+quella in cui all'interno di un programma si creano processi figli cui viene
+affidata 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
@@ -402,12 +401,12 @@ 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
+(\texttt{\small 24--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
+  25--29}); ciascun figlio (\texttt{\small 31--34}) 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
+(\texttt{\small 36--38}) 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.
@@ -458,14 +457,14 @@ 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
+essere messi in esecuzione. Se è necessaria una qualche forma di precedenza
 occorrerà provvedere ad espliciti meccanismi di sincronizzazione, pena il
 rischio di incorrere nelle cosiddette \textit{race condition} \index{race
   condition} (vedi \secref{sec:proc_race_cond}.
 
 Si noti inoltre che 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
+figli (come l'incremento di \var{i} in \texttt{\small 31}) sono visibili solo
 a loro, 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).
@@ -509,30 +508,29 @@ Il comportamento delle varie funzioni di interfaccia con i file 
 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 (di veda \secref{sec:file_buffering}) 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 carattere di a capo).
+questa bufferizzazione (trattata in dettaglio in \secref{sec:file_buffering})
+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 carattere di 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 solo alla fine del file,
-dato che in questo caso il padre esce per ultimo, troveremo 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. 
+non avviene più alla fine di ogni riga e l'output resta nel buffer. 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 il buffer viene scritto su disco all'uscita del
+figlio, 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) troveremo anche l'output completo del padre.
+
+L'esempio ci mostra un'altro aspetto fondamentale dell'interazione con i file,
+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 è 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
@@ -628,7 +626,7 @@ 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
+\func{fork} veniva fatta 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 è
@@ -641,19 +639,18 @@ trattarla ulteriormente.
 \label{sec:proc_termination}
 
 In \secref{sec:proc_conclusion} abbiamo già affrontato le modalità con cui
-chiudere 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.
+chiudere un programma, ma dall'interno del programma stesso; avendo a che fare
+con un sistema multitasking resta da affrontare l'argomento dal punto di vista
+di come il sistema gestisce la conclusione dei processi.
 
-Abbiamo già visto in \secref{sec:proc_conclusion} le tre modalità con cui un
+Abbiamo 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
+Ma abbiamo accennato che oltre alla conclusione normale 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
@@ -679,11 +676,12 @@ eseguite alla chiusura di un processo 
   inviati in successione i segnali \macro{SIGHUP} e \macro{SIGCONT}
   (vedi \secref{sec:sess_xxx}).
 \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 (il
-cosiddetto \textit{termination status}) al processo padre.
+
+Oltre queste operazioni è però necessario poter disporre di un meccanismo
+ulteriore che consenta di sapere come la 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 (il cosiddetto
+\textit{termination status}) al processo padre.
 
 Nel caso di conclusione normale, abbiamo visto in \secref{sec:proc_conclusion}
 che lo stato di uscita del processo viene caratterizzato tramite il valore del
@@ -707,15 +705,15 @@ che sia cos
 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
+Questa complicazione viene superata facendo in modo che il processo orfano
+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 possiamo eseguire il
-comando \cmd{forktest} imponendo a ciascun processo figlio due
-secondi di attesa prima di uscire, il risultato è:
+avrà sempre un padre (nel caso possiamo parlare di un padre \textsl{adottivo})
+cui riportare il suo stato di terminazione.  Come verifica di questo
+comportamento possiamo eseguire il nostro programma \cmd{forktest} imponendo a
+ciascun processo figlio due secondi di attesa prima di uscire, il risultato è:
 
 \footnotesize
 \begin{verbatim}
@@ -797,27 +795,27 @@ appunto quella di chiamare la funzione \func{wait} per i processi cui fa da
 padre, completandone la terminazione. Questo è quanto avviene anche quando,
 come nel caso del precedente esempio con \cmd{forktest}, il padre termina con
 dei figli in stato di zombie: alla sua terminazione infatti tutti i suoi figli
-vengono ereditati (compresi gli zombie) verranno adottati da \cmd{init}, il
-quale provvederà a completarne la terminazione.
+(compresi gli zombie) verranno adottati da \cmd{init}, il quale provvederà a
+completarne la terminazione.
 
 Si tenga presente infine che siccome gli zombie sono processi già usciti, non
-c'è modo di eliminarli con il comando \cmd{kill}; l'unica possibilità è quella
-di terminare il processo che li ha generati, in modo che \cmd{init} possa
-adottarli e provvedere a concludere la terminazione.
+c'è modo di eliminarli con il comando \cmd{kill}; l'unica possibilità di
+cancellarli dalla tabella dei processi è quella di terminare il processo che
+li ha generati, in modo che \cmd{init} possa adottarli e provvedere a
+concluderne la terminazione.
 
 
 \subsection{Le funzioni \func{wait} e  \func{waitpid}}
 \label{sec:proc_wait}
 
-Abbiamo già accennato come uno degli usi possibili delle capacità multitasking
-di un sistema unix-like consista nella creazione di programmi di tipo server,
-in cui un processo principale attende le richieste che vengono poi soddisfatte
-creando una serie di processi figli. Si è già sottolineato al paragrafo
-precedente come in questo caso diventi necessario gestire esplicitamente la
-conclusione dei vari processi figli onde evitare di riempire di
-\textit{zombie} la tabella dei processi; le funzioni deputate a questo compito
-sono sostanzialmente due, \func{wait} e \func{waitpid}. La prima, il cui
-prototipo è:
+Uno degli usi più comuni delle capacità multitasking di un sistema unix-like
+consiste nella creazione di programmi di tipo server, in cui un processo
+principale attende le richieste che vengono poi soddisfatte da una serie di
+processi figli. Si è già sottolineato al paragrafo precedente come in questo
+caso diventi necessario gestire esplicitamente la conclusione dei figli onde
+evitare di riempire di \textit{zombie} la tabella dei processi; le funzioni
+deputate a questo compito sono sostanzialmente due, \func{wait} e
+\func{waitpid}. La prima, il cui prototipo è:
 \begin{functions}
 \headdecl{sys/types.h}
 \headdecl{sys/wait.h}
@@ -833,7 +831,7 @@ segnale termina il processo o chiama una funzione di gestione.
   \end{errlist}}
 \end{functions}
 \noindent
-è presente fin dalle prime versioni di unix; la funzione ritorna non appena un
+è presente fin dalle prime versioni di Unix; la funzione ritorna non appena un
 processo figlio termina. Se un figlio è già terminato la funzione ritorna
 immediatamente.
 
@@ -867,8 +865,8 @@ Attende la conclusione di un processo figlio.
   \begin{errlist}
   \item[\macro{EINTR}] se non è stata specificata l'opzione \macro{WNOHANG} e
     la funzione è stata interrotta da un segnale.
-  \item[\macro{ECHILD}] il processo specificato da \var{pid} non esiste o non è
-    figlio del processo chiamante.
+  \item[\macro{ECHILD}] il processo specificato da \param{pid} non esiste o
+    non è figlio del processo chiamante.
   \end{errlist}}
 \end{functions}
 
@@ -876,7 +874,7 @@ Le differenze principali fra le due funzioni sono che \func{wait} si blocca
 sempre fino a che un processo figlio non termina, mentre \func{waitpid} ha la
 possibilità si specificare un'opzione \macro{WNOHANG} che ne previene il
 blocco; inoltre \func{waitpid} può specificare quale processo attendere sulla
-base del valore specificato tramite la variabile \var{pid}, secondo lo
+base del valore fornito dall'argomento \param{pid}, secondo lo
 specchietto riportato in \ntab:
 \begin{table}[!htb]
   \centering
@@ -901,31 +899,33 @@ specchietto riportato in \ntab:
   \label{tab:proc_waidpid_pid}
 \end{table}
 
-Il comportamento di \func{waitpid} può essere modificato passando delle
-opportune opzioni tramite la variabile \var{option}. I valori possibili sono
-il già citato \macro{WNOHANG}, che previene il blocco della funzione quando il
-processo figlio non è terminato, e \macro{WUNTRACED} (usata per il controllo
-di sessione, trattato in \capref{cha:session}) che fa ritornare la funzione
-anche per i processi figli che sono bloccati ed il cui stato non è stato
-ancora riportato al padre. Il valore dell'opzione deve essere specificato come
-maschera binaria ottenuta con l'OR delle suddette costanti con zero.
+Il comportamento di \func{waitpid} può inoltre essere modificato passando
+delle opportune opzioni tramite l'argomento \param{option}. I valori possibili
+sono il già citato \macro{WNOHANG}, che previene il blocco della funzione
+quando il processo figlio non è terminato, e \macro{WUNTRACED} (usata per il
+controllo di sessione, trattato in \capref{cha:session}) che fa ritornare la
+funzione anche per i processi figli che sono bloccati ed il cui stato non è
+stato ancora riportato al padre. Il valore dell'opzione deve essere
+specificato come maschera binaria ottenuta con l'OR delle suddette costanti
+con zero.
 
 La terminazione di un processo figlio è chiaramente un evento asincrono
 rispetto all'esecuzione di un programma e può avvenire in un qualunque
-momento, per questo motivo, come si è visto nella sezione precedente, una
-delle azioni prese dal kernel alla conclusione di un processo è quella di
-mandare un segnale di \macro{SIGCHLD} al padre. Questo segnale viene ignorato
-di default, ma costituisce il meccanismo di comunicazione asincrona con cui il
-kernel avverte un processo padre che uno dei suoi figli è terminato.
+momento. Per questo motivo, come accennato nella sezione precedente, una delle
+azioni prese dal kernel alla conclusione di un processo è quella di mandare un
+segnale di \macro{SIGCHLD} al padre. L'azione di default (si veda
+\secref{sec:sig_base}) per questo segnale è di essere ignorato, ma la sua
+generazione costituisce il meccanismo di comunicazione asincrona con cui il
+kernel avverte il processo padre che uno dei suoi figli è terminato.
 
 In genere in un programma non si vuole essere forzati ad attendere la
 conclusione di un processo per proseguire, specie se tutto questo serve solo
 per leggerne lo stato di chiusura (ed evitare la presenza di \textit{zombie}),
 per questo la modalità più usata per chiamare queste funzioni è quella di
 utilizzarle all'interno di un \textit{signal handler} (torneremo sui segnali e
-su come gestire \macro{SIGCHLD} in \secref{sec:sig_sigwait_xxx}) nel qual
-caso, dato che il segnale è generato dalla terminazione un figlio, avremo la
-certezza che la chiamata a \func{wait} non si bloccherà.
+su come gestire \macro{SIGCHLD} in \secref{sec:sig_sigwait_xxx}). In questo
+caso infatti, dato che il segnale è generato dalla terminazione un figlio,
+avremo la certezza che la chiamata a \func{wait} non si bloccherà.
 
 \begin{table}[!htb]
   \centering
@@ -966,19 +966,21 @@ certezza che la chiamata a \func{wait} non si bloccher
 \end{table}
 
 Entrambe le funzioni di attesa restituiscono lo stato di terminazione del
-processo tramite il puntatore \var{status} (se non interessa memorizzare lo
+processo tramite il puntatore \param{status} (se non interessa memorizzare lo
 stato si può passare un puntatore nullo). Il valore restituito da entrambe le
-funzioni dipende dall'implementazione, e tradizionalmente alcuni bit sono
-riservati per memorizzare lo stato di uscita (in genere 8) altri per indicare
-il segnale che ha causato la terminazione (in caso di conclusione anomala),
-uno per indicare se è stato generato un core file, ecc\footnote{le
-  definizioni esatte si possono trovare in \file{<bits/waitstatus.h} ma questo
-  file non deve mai essere usato direttamente, esso viene incluso attraverso
-  \file{<sys/wait.h>}}.  Lo standard POSIX.1 definisce una serie di macro di
-preprocessore da usare per analizzare lo stato di uscita; esse sono definite
-sempre in \file{<sys/wait.h>} ed elencate in \curtab\ (si tenga presente che
-queste macro prendono come parametro la variabile di tipo \type{int} puntata
-da \var{status}).
+funzioni dipende dall'implementazione, e tradizionalmente alcuni bit (in
+genere 8) sono riservati per memorizzare lo stato di uscita, e altri per
+indicare il segnale che ha causato la terminazione (in caso di conclusione
+anomala), uno per indicare se è stato generato un core file, ecc.\footnote{le
+  definizioni esatte si possono trovare in \file{<bits/waitstatus.h>} ma
+  questo file non deve mai essere usato direttamente, esso viene incluso
+  attraverso \file{<sys/wait.h>}.}
+
+Lo standard POSIX.1 definisce una serie di macro di preprocessore da usare per
+analizzare lo stato di uscita. Esse sono definite sempre in
+\file{<sys/wait.h>} ed elencate in \curtab\ (si tenga presente che queste
+macro prendono come parametro la variabile di tipo \type{int} puntata da
+\var{status}).
 
 Si tenga conto che nel caso di conclusione anomala il valore restituito da
 \macro{WTERMSIG} può essere controllato contro le costanti definite in
@@ -990,11 +992,12 @@ le apposite funzioni trattate in \secref{sec:sig_strsignal}.
 \label{sec:proc_wait4}
 
 Linux, seguendo una estensione di BSD, supporta altre due funzioni per la
-lettura dello stato di terminazione di un processo, analoghe a \func{wait} e
-\func{waitpid}, ma che prevedono un ulteriore parametro attraverso il quale il
-kernel può restituire al padre informazioni sulle risorse usate dal processo
-terminato e dai vari figli.  Queste funzioni, che diventano accessibili
-definendo la costante \macro{\_USE\_BSD}, sono:
+lettura dello stato di terminazione di un processo \func{wait3} e
+\func{wait4}, analoghe alle precedenti ma che prevedono un ulteriore
+parametro attraverso il quale il kernel può restituire al padre informazioni
+sulle risorse usate dal processo terminato e dai vari figli.  I prototipi di
+queste funzioni, che diventano accessibili definendo la costante
+\macro{\_USE\_BSD}, sono:
 \begin{functions}
   \headdecl{sys/times.h} 
   \headdecl{sys/types.h} 
@@ -1013,7 +1016,7 @@ definendo la costante \macro{\_USE\_BSD}, sono:
 \noindent 
 la struttura \type{rusage} è definita in \file{sys/resource.h}, e viene
 utilizzata anche dalla funzione \func{getrusage} (vedi \secref{sec:sys_xxx})
-per ottenere le risorse di sistema usate dal processo; la sua definizione è
+per ottenere le risorse di sistema usate da un processo; la sua definizione è
 riportata in \figref{fig:sys_rusage_struct}.
 
 In genere includere esplicitamente \file{<sys/time.h>} non è più
@@ -1049,8 +1052,9 @@ famiglia di funzioni) che possono essere usate per questo compito, in realt
   \begin{errlist}
   \item[\macro{EACCES}] il file non è eseguibile, oppure il filesystem è
     montato in \cmd{noexec}, oppure non è un file normale o un interprete.
-  \item[\macro{EPERM}] il file ha i bit \acr{suid} o \acr{sgid} ma l'utente non
-    è root o il filesystem è montato con \cmd{nosuid}, oppure
+  \item[\macro{EPERM}] il file ha i bit \acr{suid} o \acr{sgid}, l'utente non
+    è root, e o il processo viene tracciato, o il filesystem è montato con
+    l'opzione \cmd{nosuid}.
   \item[\macro{ENOEXEC}] il file è in un formato non eseguibile o non
     riconosciuto come tale, o compilato per un'altra architettura.
   \item[\macro{ENOENT}] il file o una delle librerie dinamiche o l'interprete
@@ -1208,12 +1212,12 @@ speciale 
 può anche non essere resettato a \macro{SIG\_DFL} (si veda
 \secref{sec:sig_xxx}).
 
-La gestione dei file aperti dipende dal valore del flag di
-\textit{close-on-exec} per ciascun file descriptor (si veda
-\secref{sec:file_fcntl}); i file per cui è settato vengono chiusi, tutti gli
-altri file restano aperti. Questo significa che il comportamento di default è
-che i file restano aperti attraverso una \func{exec}, a meno di una chiamata
-esplicita a \func{fcntl} che setti il suddetto flag.
+La gestione dei file aperti dipende dal valore che ha il flag di
+\textit{close-on-exec} (trattato in \secref{sec:file_fcntl}) per ciascun file
+descriptor. I file per cui è settato vengono chiusi, tutti gli altri file
+restano aperti. Questo significa che il comportamento di default è che i file
+restano aperti attraverso una \func{exec}, a meno di una chiamata esplicita a
+\func{fcntl} che setti il suddetto flag.
 
 Per le directory lo standard POSIX.1 richiede che esse vengano chiuse
 attraverso una \func{exec}, in genere questo è fatto dalla funzione
@@ -1223,9 +1227,9 @@ maniera trasparente all'utente.
 
 Abbiamo detto che il \textit{real user id} ed il \textit{real group id}
 restano gli stessi all'esecuzione di \func{exec}; lo stesso vale per
-l'\textit{effective user id} ed l'\textit{effective group id}, tranne il caso
-in cui il file che si va ad eseguire ha o il \acr{suid} bit o lo \acr{sgid}
-bit settato, nel qual caso \textit{effective user id} e \textit{effective
+l'\textit{effective user id} ed l'\textit{effective group id}, tranne quando
+il file che si va ad eseguire abbia o il \acr{suid} bit o lo \acr{sgid} bit
+settato, in questo caso l'\textit{effective user id} e l'\textit{effective
   group id} vengono settati rispettivamente all'utente o al gruppo cui il file
 appartiene (per i dettagli vedi \secref{sec:proc_perms}).
 
@@ -1257,8 +1261,8 @@ parametri connessi ai processi.
 In questa sezione esamineremo le problematiche relative al controllo di
 accesso dal punto di vista del processi; vedremo quali sono gli identificatori
 usati, come questi possono essere modificati nella creazione e nel lancio di
-nuovi processi, e le varie funzioni per la loro manipolazione diretta e tutte
-le problematiche connesse ad una gestione accorta dei privilegi.
+nuovi processi, le varie funzioni per la loro manipolazione diretta e tutte le
+problematiche connesse ad una gestione accorta dei privilegi.
 
 
 \subsection{Gli identificatori del controllo di accesso}
@@ -1291,12 +1295,12 @@ evidente che per poter implementare un controllo sulle operazioni occorre
 anche poter identificare chi è che ha lanciato un certo programma, e pertanto
 anche a ciascun processo è associato un utente e a un gruppo.
 
-Un semplice controllo di una corrispondenza fra identificativi però non
-garantisce però sufficiente flessibilità per tutti quei casi in cui è
-necessario poter disporre di privilegi diversi, o dover impersonare un altro
-utente per un limitato insieme di operazioni. Per questo motivo in generale
-tutti gli Unix prevedono che i processi abbiano almeno due gruppi di
-identificatori, chiamati rispettivamente \textit{real} ed \textit{effective}.
+Un semplice controllo di una corrispondenza fra identificativi non garantisce
+però sufficiente flessibilità per tutti quei casi in cui è necessario poter
+disporre di privilegi diversi, o dover impersonare un altro utente per un
+limitato insieme di operazioni. Per questo motivo in generale tutti gli Unix
+prevedono che i processi abbiano almeno due gruppi di identificatori, chiamati
+rispettivamente \textit{real} ed \textit{effective}.
 
 \begin{table}[htb]
   \footnotesize
@@ -1338,8 +1342,8 @@ cui si accede al sistema (e relativo gruppo di default). Servono per
 l'identificazione dell'utente e normalmente non vengono mai cambiati. In
 realtà vedremo (in \secref{sec:proc_setuid}) che è possibile modificarli, ma
 solo ad un processo che abbia i privilegi di amministratore; questa
-possibilità è usata ad esempio da \cmd{login} che una volta completata la
-procedura di autenticazione lancia una shell per la quale setta questi
+possibilità è usata ad esempio da \cmd{login} che, una volta completata la
+procedura di autenticazione, lancia una shell per la quale setta questi
 identificatori ai valori corrispondenti all'utente che entra nel sistema.
 
 Al secondo gruppo appartengono l'\textit{effective user id} e
@@ -1354,7 +1358,7 @@ Questi identificatori normalmente sono identici ai corrispondenti del gruppo
 \secref{sec:proc_exec}, il programma che si è posto in esecuzione abbia i bit
 \acr{suid} o \acr{sgid} settati (il significato di questi bit è affrontato in
 dettaglio in \secref{sec:file_suid_sgid}). In questo caso essi saranno settati
-all'utente e al gruppo proprietari del file; questo consente, per programmi in
+all'utente e al gruppo proprietari del file. Questo consente, per programmi in
 cui ci sia necessità, di dare a qualunque utente normale privilegi o permessi
 di un'altro (o dell'amministratore).
 
@@ -1444,8 +1448,7 @@ corrente.
 Il funzionamento di queste due funzioni è analogo, per cui considereremo solo
 la prima; la seconda si comporta esattamente allo stesso modo facendo
 riferimento al \textit{group id} invece che all'\textit{user id}.  Gli
-eventuali \textit{supplementary group id} non vengono modificati da nessuna
-delle funzioni che tratteremo in questa sezione.
+eventuali \textit{supplementary group id} non vengono modificati.
 
 
 L'effetto della chiamata è diverso a seconda dei privilegi del processo; se
@@ -1462,16 +1465,16 @@ riportare l'\textit{effective user id} a quello dell'utente che ha lanciato il
 programma, effettuare il lavoro che non necessita di privilegi aggiuntivi, ed
 eventualmente tornare indietro.
 
-Come esempio per chiarire dell'uso di queste funzioni prediamo quello con cui
+Come esempio per chiarire dell'uso di queste funzioni prendiamo quello con cui
 viene gestito l'accesso al file \file{/var/log/utmp}.  In questo file viene
 registrato chi sta usando il sistema al momento corrente; chiaramente non può
 essere lasciato aperto in scrittura a qualunque utente, che potrebbe
 falsificare la registrazione. Per questo motivo questo file (e l'analogo
 \file{/var/log/wtmp} su cui vengono registrati login e logout) appartengono ad
 un gruppo dedicato (\acr{utmp}) ed i programmi che devono accedervi (ad
-esempio tutti i programmi di terminale in X, o il programma \cmd{screen}
-che crea terminali multipli su una console) appartengono a questo gruppo ed
-hanno il bit \acr{sgid} settato.
+esempio tutti i programmi di terminale in X, o il programma \cmd{screen} che
+crea terminali multipli su una console) appartengono a questo gruppo ed hanno
+il bit \acr{sgid} settato.
 
 Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato la
 situazione degli identificatori è la seguente:
@@ -1523,7 +1526,7 @@ ricorrere ad altre funzioni (si veda ad esempio \secref{sec:proc_seteuid}).
 \label{sec:proc_setreuid}
 
 Queste due funzioni derivano da BSD che, non supportando\footnote{almeno fino
-  alla versione 4.3+BSD TODO, verificare e aggiornare la nota.} i
+  alla versione 4.3+BSD TODO, FIXME verificare e aggiornare la nota.} i
 \textit{saved id}, le usava per poter scambiare fra di loro \textit{effective}
 e \textit{real id}. I loro prototipi sono:
 \begin{functions}
@@ -1747,8 +1750,8 @@ ottenere tutti i gruppi a cui appartiene un utente; il suo prototipo 
     restituisce 0 in caso di successo e -1 in caso di fallimento.}
 \end{functions}
 \noindent la funzione esegue una scansione del database dei gruppi (si veda
-\secref{sec:sys_xxx}) e ritorna in \param{groups} la lista di quelli a cui
-l'utente appartiene. Si noti che \param{ngroups} è passato come puntatore
+\secref{sec:sys_user_group}) e ritorna in \param{groups} la lista di quelli a
+cui l'utente appartiene. Si noti che \param{ngroups} è passato come puntatore
 perché qualora il valore specificato sia troppo piccolo la funzione ritorna -1
 e passando indietro il numero dei gruppi trovati.
 
@@ -1803,10 +1806,81 @@ quando si definisce \macro{\_POSIX\_SOURCE} o si compila con il flag
 \label{sec:proc_priority}
 
 In questa sezione tratteremo più approfonditamente i meccanismi con il quale
-lo \textit{scheduler} assegna la CPU ai vari processi attivi, illustrando le
-varie funzioni che permettono di leggere e modificare le priorità di
-esecuzione dei programmi.
-
+lo \textit{scheduler}\footnote{che è la parte del kernel che si occupa di
+  stabilire quale processo dovrà essere posto in esecuzione.} assegna la CPU
+ai vari processi attivi. In particolare prendremo in esame i vari meccanismi
+con cui viene gestita l'assgnazione del tempo di CPU, ed illustreremo le varie
+funzioni di gestione.
+
+
+\subsection{I meccanismi di \textit{scheduling}}
+\label{sec:proc_sched}
+
+La scelta di un meccanismo che sia in grado di distribuire in maniera efficace
+il tempo di CPU per l'esecuzione dei processi è sempre una questione delicata,
+ed oggetto di numerose ricerche; in ogni caso essa dipende in maniera
+essenziale anche dal tipo di utilizzo che deve essere fatto del sistema.
+
+La cosa è resa ancora più complicata dal fatto che con sistemi
+multi-processore si introduce anche la complessità dovuta alla scelta di quale
+sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni la
+  presenza di ampie cache può rendere poco efficiente trasferire l'esecuzione
+  di un processo da una CPU ad un'altra, per cui occorrono meccanismi per
+  determininare quale è la migliore scelta fra le diverse CPU.} Tutto questo
+comunque appartiene alle sottigliezze dell'implementazione del kernel, e dal
+punto di vista dei programmi che girano in user space di può pensare sempre
+alla risorsa tempo di esecuzione, governata dagli stessi mecca, che nel caso
+di più processori sarà a disposizione di più di un processo alla volta.
+
+Si tenga presente inoltre che l'utilizzo della CPU è soltanto una delle
+risorse (insieme alla memoria e all'accesso alle periferiche) che sono
+necessarie per l'esecuzione di un programma, e spesso non è neanche la più
+importante. Per questo non è affatto detto che dare ad un programma la massima
+priorità di esecuzione abbia risultati significativi in termini di
+prestazioni.
+
+La politica tradizionale di scheduling di Unix (che tratteremo in
+\secref{sec:proc_sched_stand}) è sempre stata basata su delle priorità
+dinamiche, che assicurassaro che tutti i processi, anche i meno importanti,
+potessero ricevere un po' di tempo di CPU. 
+
+Lo standard POSIX però per tenere conto dei sistemi real-time,\footnote{per
+  sistema real-time si intende un sistema in grado di eseguire operazioni in
+  tempo reale; in genere si tende a distinguere fra l'\textit{hard real-time}
+  in cui è necessario che i tempi di esecuzione di un programma siano
+  determinabili con certezza assoluta, come nel caso di meccanismi di
+  controllo di macchine, dove uno sforamento dei tempi avrebbe conseguenze
+  disastrose, e \textit{soft-real-time} in cui un occasionale sforamento è
+  ritenuto accettabile.} in cui è vitale che i processi in che devono essere
+eseguiti in un determinato momento non debbano aspettare la conclusione di un
+altri processi che non hanno questa necessità, ha introdotto il concetto di
+\textsl{priorità assoluta}, chimata anche \textsl{priorità statica}, in
+contrapposizione con la normale priorità dinamica.
+
+Il concetto di prorità assoluta dice che quando due processi si contendono
+l'esecuzione, vince sempre quello con la priorità assoluta più alta, anche,
+grazie al \textit{prehemptive scheduling}, se l'altro è in esecuzione.
+Ovviamente questo avviene solo per i processi che sono pronti per essere
+eseguiti (cioè nello stato \textit{runnable}\footnote{lo stato di un processo
+  è riportato nel campo \texttt{STAT} dell'output del comando \cmd{ps},
+  abbiamo già visto che lo stato di \textit{zombie} è indicato con \texttt{Z},
+  gli stati \textit{runnable}, \textit{sleep} e di I/O (\textit{uninteruttible
+    sleep}) sono invece indicati con \texttt{R}, \texttt{S} e \texttt{D}.}),
+la priorità assoluta viene invece ignorata per quelli che sono bloccati su una
+richiesta di I/O o in stato di \textit{sleep}.
+
+Questa viene in genere indicata con un numero
+
+
+
+
+\subsection{Il meccanismo di \textit{scheduling} standard}
+\label{sec:proc_sched_stand}
+
+In Linux tutti i processi hanno sostanzialmente la stessa priorità; benché sia
+possibile specificare una priorità assoluta secondo lo standard POSIX
+(argomento che tratteremo più avanti) l'uso comune segue quello che è il
+meccanismo tradizionale con cui i sistemi
 
 
 
@@ -1820,11 +1894,10 @@ occorre tenere conto di una serie di problematiche che normalmente non
 esistono quando si ha a che fare con un sistema in cui viene eseguito un solo
 programma alla volta.
 
-Pur essendo questo argomento di carattere generale, in questa sezione
-conclusiva del capitolo in cui abbiamo affrontato la gestione dei processi ci
-è parso opportuno introdurre sinteticamente queste problematiche, che
-ritroveremo a più riprese in capitoli successivi, dando una breve descrizione
-delle loro caratteristiche principali e della terminologia relativa.
+Pur essendo questo argomento di carattere generale, ci è parso opportuno
+introdurre sinteticamente queste problematiche, che ritroveremo a più riprese
+in capitoli successivi, in questa sezione conclusiva del capitolo in cui
+abbiamo affrontato la gestione dei processi.
 
 
 \subsection{Le operazioni atomiche}
@@ -1846,7 +1919,7 @@ cui non erano ancora state completate.
 Nel caso dell'interazione fra processi la situazione è molto più semplice, ed
 occorre preoccuparsi della atomicità delle operazioni solo quando si ha a che
 fare con meccanismi di intercomunicazione (che esamineremo in dettaglio in
-\capref{cha:IPC}) o nella operazioni con i file (vedremo alcuni esempi in
+\capref{cha:IPC}) o nelle operazioni con i file (vedremo alcuni esempi in
 \secref{sec:file_atomic}). In questi casi in genere l'uso delle appropriate
 funzioni di libreria per compiere le operazioni necessarie è garanzia
 sufficiente di atomicità in quanto le system call con cui esse sono realizzate
@@ -1862,13 +1935,13 @@ operazioni atomiche (torneremo su questi aspetti in \secref{sec:sign_xxx}).
 
 In questo caso il sistema provvede un tipo di dato, il \type{sig\_atomic\_t},
 il cui accesso è assicurato essere atomico.  In pratica comunque si può
-assumere che in ogni piattaforma su cui è implementato Linux il tipo
-\type{int} (e gli altri interi di dimensione inferiore) ed i puntatori sono
+assumere che, in ogni piattaforma su cui è implementato Linux, il tipo
+\type{int}, gli altri interi di dimensione inferiore ed i puntatori sono
 atomici. Non è affatto detto che lo stesso valga per interi di dimensioni
 maggiori (in cui l'accesso può comportare più istruzioni in assembler) o per
-le strutture. In questi casi è anche opportuno marcare come \type{volatile} le
-variabili che possono essere interessate ad accesso condiviso, onde evitare
-problemi con le ottimizzazioni del codice.
+le strutture. In tutti questi casi è anche opportuno marcare come
+\type{volatile} le variabili che possono essere interessate ad accesso
+condiviso, onde evitare problemi con le ottimizzazioni del codice.
 
 
 \subsection{Le \textit{race condition} e i \textit{deadlock}}
@@ -1877,7 +1950,7 @@ problemi con le ottimizzazioni del codice.
 Si definiscono \textit{race condition} tutte quelle situazioni in cui processi
 diversi operano su una risorsa comune, ed in cui il risultato viene a
 dipendere dall'ordine in cui essi effettuano le loro operazioni. Il caso
-tipico è quella di una operazione che viene eseguita da un processo in più
+tipico è quello di una operazione che viene eseguita da un processo in più
 passi, e può essere compromessa dall'intervento di un altro processo che
 accede alla stessa risorsa quando ancora non tutti i passi sono stati
 completati.
@@ -1897,8 +1970,8 @@ gli adeguati provvedimenti per far si che non si verifichino. Casi tipici di
 file, o nell'accesso a meccanismi di intercomunicazione come la memoria
 condivisa. In questi casi, se non si dispone della possibilità di eseguire
 atomicamente le operazioni necessarie, occorre che quelle parti di codice in
-cui si compiono le operazioni critiche sulle risorse condivise, le cosiddette
-\textsl{sezioni critiche} del programma, siano opportunamente protette da
+cui si compiono le operazioni sulle risorse condivise (le cosiddette
+\textsl{sezioni critiche}) del programma, siano opportunamente protette da
 meccanismi di sincronizzazione (torneremo su queste problematiche di questo
 tipo in \secref{sec:ipc_semaph}).
 
@@ -1916,8 +1989,7 @@ quest'ultima diventer
 In tutti questi casi è di fondamentale importanza il concetto di atomicità
 visto in \secref{sec:proc_atom_oper}; questi problemi infatti possono essere
 risolti soltanto assicurandosi, quando essa sia richiesta, che sia possibile
-eseguire in maniera atomica le operazioni necessarie, proteggendo con gli
-adeguati meccanismi le \textsl{sezioni critiche} del programma.
+eseguire in maniera atomica le operazioni necessarie.
 
 
 \subsection{Le funzioni rientranti}