Inserita l'indicizzazione delle funzioni. Ricontrollate le chiamate
[gapil.git] / prochand.tex
index db517e90d28db138259c6bf95bffbedb2466a792..7a7bef4ed98671626d36f22576cda6ceb15d57c5 100644 (file)
@@ -1,26 +1,28 @@
 \chapter{La gestione dei processi}
 \label{cha:process_handling}
 
-Come accennato nell'introduzione in un sistema unix ogni attività del sistema
+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.
 
-Nel precedente capitolo abbiamo visto come funziona un singolo processo, in
-questo capitolo affronteremo 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.
+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.
 
 
 
 \section{Introduzione}
 \label{sec:proc_gen}
 
-Partiremo con una introduzione generale ai concetti che stanno alla base della
-gestione dei processi in un sistema unix-like. Introdurremo in questa sezione
-l'architettura della gestione dei processi e le sue principali
-caratteristiche, e daremo una panoramica sull'uso delle principali funzioni
-per la gestione dei processi.
+Inizieremo con una introduzione generale ai concetti che stanno alla base
+della gestione dei processi in un sistema unix-like. Introdurremo in questa
+sezione l'architettura della gestione dei processi e le sue principali
+caratteristiche, dando una panoramica sull'uso delle principali funzioni di
+gestione.
 
 
 \subsection{La gerarchia dei processi}
@@ -28,13 +30,13 @@ per 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
+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 cosiddetto \textit{process identifier} o, più brevemente, 
 \acr{pid}.
 
-Una seconda caratteristica di un sistema unix è che la generazione di un
+Una seconda caratteristica di un sistema Unix è 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
 eseguirà, in un passo successivo, il programma voluto: questo è ad esempio
@@ -963,10 +965,11 @@ accessibili definendo la costante \macro{\_USE\_BSD}, sono:
   \funcdecl{pid\_t wait4(pid\_t pid, int * status, int options, struct rusage
     * rusage)} 
   È identica a \func{waitpid} sia per comportamento che per i
-  valori dei parametri, ma restituisce in \var{rusage} un sommario delle
+  valori dei parametri, ma restituisce in \param{rusage} un sommario delle
   risorse usate dal processo (per i dettagli vedi \secref{sec:sys_xxx})
+
   \funcdecl{pid\_t wait3(int *status, int options, struct rusage *rusage)}
-  Prima versione, equivalente a \func{wait4(-1, \&status, opt, rusage)} è
+  Prima versione, equivalente a \code{wait4(-1, \&status, opt, rusage)} è
   ormai deprecata in favore di \func{wait4}.
 \end{functions}
 \noindent 
@@ -1062,7 +1065,7 @@ e come ambiente la lista di stringhe indicata da \var{envp}; entrambe le
 liste devono essere terminate da un puntatore nullo. I vettori degli
 argomenti e dell'ambiente possono essere acceduti dal nuovo programma
 quando la sua funzione \func{main} è dichiarata nella forma
-\func{main(int argc, char *argv[], char *envp[])}.
+\code{main(int argc, char *argv[], char *envp[])}.
 
 Le altre funzioni della famiglia servono per fornire all'utente una serie
 possibile di diverse interfacce per la creazione di un nuovo processo. I loro
@@ -1091,7 +1094,7 @@ le modalit
 argomenti a linea di comando (cioè i valori di \var{argv} e \var{argc} visti
 dalla funzione \func{main} del programma chiamato). 
 
-Queste modalità sono due e sono riassunte dagli mnenonici \func{v} e \func{l}
+Queste modalità sono due e sono riassunte dagli mnenonici \code{v} e \code{l}
 che stanno rispettivamente per \textit{vector} e \textit{list}. Nel primo caso
 gli argomenti sono passati tramite il vettore di puntatori \var{argv[]} a
 stringhe terminate con zero che costituiranno gli argomenti a riga di comando,
@@ -1134,7 +1137,7 @@ per indicare il nome del file che contiene il programma che verr
 \end{table}
 
 La seconda differenza fra le funzioni riguarda le modalità con cui si
-specifica il programma che si vuole eseguire. Con lo mnemonico \func{p} si
+specifica il programma che si vuole eseguire. Con lo mnemonico \code{p} si
 indicano le due funzioni che replicano il comportamento della shell nello
 specificare il comando da eseguire; quando il parametro \var{file} non
 contiene una \file{/} esso viene considerato come un nome di programma, e
@@ -1158,7 +1161,7 @@ indicato dal parametro \var{path}, che viene interpretato come il
 \end{figure}
 
 La terza differenza è come viene passata la lista delle variabili di ambiente.
-Con lo mnemonico \func{e} vengono indicate quelle funzioni che necessitano di
+Con lo mnemonico \code{e} vengono indicate quelle funzioni che necessitano di
 un vettore di parametri \var{envp[]} analogo a quello usato per gli argomenti
 a riga di comando (terminato quindi da un \macro{NULL}), le altre usano il
 valore della variabile \var{environ} (vedi \secref{sec:proc_environ}) del
@@ -1471,7 +1474,7 @@ situazione degli identificatori 
 \end{eqnarray*}
 in questo modo, dato che l'\textit{effective group id} è quello giusto, il
 programma può accedere a \file{/var/log/utmp} in scrittura ed aggiornarlo, a
-questo punto il programma può eseguire una \func{setgid(getgid())} per settare
+questo punto il programma può eseguire una \code{setgid(getgid())} per settare
 l'\textit{effective group id} a quello dell'utente (e dato che il \textit{real
   group id} corrisponde la funzione avrà successo), in questo modo non sarà
 possibile lanciare dal terminale programmi che modificano detto file, in tal
@@ -1485,7 +1488,7 @@ caso infatti la situazione degli identificatori sarebbe:
 e ogni processo lanciato dal terminale avrebbe comunque \acr{gid} come
 \textit{effective group id}. All'uscita dal terminale, per poter di nuovo
 aggiornare lo stato di \file{/var/log/utmp} il programma eseguirà una
-\func{setgid(utmp)} (dove \var{utmp} è il valore numerico associato al gruppo
+\code{setgid(utmp)} (dove \var{utmp} è il valore numerico associato al gruppo
 \acr{utmp}, ottenuto ad esempio con una \func{getegid}), dato che in questo
 caso il valore richiesto corrisponde al \textit{saved group id} la funzione
 avrà successo e riporterà la situazione a:
@@ -1729,47 +1732,85 @@ problemi con le ottimizzazioni del codice.
 \subsection{Le \textit{race condition} e i \textit{deadlock}}
 \label{sec:proc_race_cond}
 
-Si definisce una \textit{race condition} il caso in cui diversi processi
-stanno cercando di fare qualcosa con una risorsa comune ed il risultato finale
-viene a dipendere dall'ordine di esecuzione dei medesimi. Ovviamente dato che
-l'ordine di esecuzione di un processo rispetto agli altri, senza appositi
-meccanismi di sincronizzazione, non è assolutamente prevedibile, queste
-situazioni sono fonti di errori molto subdoli, che possono verificarsi solo in
-condizioni particolari e quindi difficilmente riproducibili.
-
-Casi tipici di \textit{race condition} si hanno quando diversi processi
-accedono allo stesso 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 le risorse
-condivise siano opportunamente protette da meccanismi di sincronizzazione
-(torneremo su queste problematiche di questo tipo in \secref{sec:ipc_semaph}).
+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ù
+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.
+
+Dato che in un sistema multitasking ogni processo può essere interrotto in
+qualunque momento per farne subentrare un'altro in esecuzione, niente può
+assicurare un preciso ordine di esecuzione fra processi diversi o che una
+sezione di un programma possa essere eseguita senza interruzioni da parte di
+altri. Queste situazioni comportano pertanto errori estremamente subdoli e
+difficili da tracciare, in quanto nella maggior parte dei casi tutto
+funzionerà regolarmente, e solo occasionalmente si avranno degli errori. 
+
+Per questo occorre essere ben consapovoli di queste problematiche, e del fatto
+che l'unico modo per evitarle è quello di riconoscerle come tali e prendere
+gli adeguati provvedimenti per far si che non si verifichino. Casi tipici di
+\textit{race condition} si hanno quando diversi processi accedono allo stesso
+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
+meccanismi di sincronizzazione (torneremo su queste problematiche di questo
+tipo in \secref{sec:ipc_semaph}).
 
 Un caso particolare di \textit{race condition} sono poi i cosiddetti
-\textit{deadlock}; l'esempio tipico è quello di un flag di ``occupazione'' che
-viene rilasciato da un evento asincrono fra il controllo (in cui viene trovato
-occupato) e la successiva messa in attesa, che a questo punto diventerà
-perpetua (da cui il nome di \textit{deadlock}) in quanto l'evento di sblocco
-del flag è stato perso fra il controllo e la messa in attesa.
+\textit{deadlock}, particolarmente gravi in quanto comportano spesso il blocco
+completo di un servizio, e non il fallimento di una singola operazione.
+L'esempio tipico di una situazione che può condurre ad un \textit{deadlock} è
+quello in cui un flag di ``occupazione'' viene rilasciato da un evento
+asincrono (come un segnale o un altro processo) fra il momento in cui lo si è
+controllato (trovadolo occupato) e la successiva operazione di attesa per lo
+sblocco. In questo caso, dato che l'evento di sblocco del flag è avvenuto
+senza che ce ne accorgessimo proprio fra il controllo e la messa in attesa,
+quest'ultima diventerà perpetua (da cui il nome di \textit{deadlock}).
+
+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.
 
 
 \subsection{Le funzioni rientranti}
 \label{sec:proc_reentrant}
 
-Si dice rientrante una funzione che può essere interrotta in qualunque momento
-ed essere chiamata da capo (da questo il nome) da un altro filone di
-esecuzione (thread e manipolatori di segnali sono i casi in cui occorre
-prestare attenzione a questa problematica) senza che questo comporti nessun
-problema.
-
-In genere una funzione non è rientrante se opera direttamente su memoria che
-non è nello stack. Ad esempio una funzione non è rientrante se usa una
-variabile globale o statica od un oggetto allocato dinamicamente che trova da
-sola: due chiamate alla stessa funzione interferiranno.  Una funzione può non
-essere rientrante se usa e modifica un oggetto che le viene fornito dal
-chiamante: due chiamate possono interferire se viene passato lo stesso
-oggetto. 
-
-Le glibc mettono a disposizione due macro di compilatore \macro{\_REENTRANT} e
-\macro{\_THREAD\_SAFE} per assicurare che siano usate delle versioni rientranti
-delle funzioni di libreria.
+Si dice \textsl{rientrante} una funzione che può essere interrotta in
+qualunque punto della sua esecuzione ed essere chiamata una seconda volta da
+un altro thread di esecuzione senza che questo comporti nessun problema nella
+esecuzione della stessa. La problematica è comune nella programmazione
+multi-thread, ma si hanno gli stessi problemi quando si vogliono chiamare
+delle funzioni all'interno dei manipolatori dei segnali.
+
+Fintanto che una funzione opera soltanto con le variabili locali è rientrante;
+queste infatti vengono tutte le volte allocate nello stack, e un'altra
+invocazione non fa altro che allocarne un'altra copia. Una funzione può non
+essere rientrante quando opera su memoria che non è nello stack.  Ad esempio
+una funzione non è mai rientrante se usa una variabile globale o statica.
+
+Nel caso invece la funzione operi su un oggetto allocato dinamicamente la cosa
+viene a dipendere da come avvengono le operazioni; se l'oggetto è creato ogni
+volta e ritornato indietro la funzione può essere rientrante, se invece esso
+viene individuato dalla funzione stessa due chiamate alla stessa funzione
+potranno interferire quando entrambe faranno riferimento allo stesso oggetto.
+Allo stesso modo una funzione può non essere rientrante se usa e modifica un
+oggetto che le viene fornito dal chiamante: due chiamate possono interferire
+se viene passato lo stesso oggetto; in tutti questi casi occorre molta cura da
+parte del programmatore.
+
+In genere le funzioni di libreria non sono rientranti, molte di esse ad
+esempio utilizzano variabili statiche, le \acr{glibc} però mettono a
+disposizione due macro di compilatore, \macro{\_REENTRANT} e
+\macro{\_THREAD\_SAFE}, la cui definizione attiva le versioni rientranti di
+varie funzioni di libreria, che sono identificate aggiungendo il suffisso
+\code{\_r} al nome della versione normale.
+
+
+