\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.
gestione.
-\subsection{La gerarchia dei processi}
+\subsection{L'architettura della gestione dei processi}
\label{sec:proc_hierarchy}
A differenza di quanto avviene in altri sistemi (ad esempio nel VMS la
kernel, (come \cmd{keventd}, \cmd{kswapd}, etc.)} si possono classificare i
processi con la relazione padre/figlio in una organizzazione gerarchica ad
albero, in maniera analoga a come i file sono organizzati in un albero di
-directory (si veda \secref{sec:file_file_struct}); in \curfig\ si è mostrato il
-risultato del comando \cmd{pstree} che permette di mostrare questa struttura,
-alla cui base c'è \cmd{init} che è progenitore di tutti gli altri processi.
+directory (si veda \secref{sec:file_organization}); in \curfig\ si è mostrato
+il risultato del comando \cmd{pstree} che permette di mostrare questa
+struttura, alla cui base c'è \cmd{init} che è progenitore di tutti gli altri
+processi.
+
+
+Il kernel mantiene una tabella dei processi attivi, la cosiddetta
+\textit{process table}; per ciascun processo viene mantenuta una voce nella
+tabella dei processi costituita da una struttura \type{task\_struct}, che
+contiene tutte le informazioni rilevanti per quel processo. Tutte le strutture
+usate a questo scopo sono dichiarate nell'header file \file{linux/sched.h}, ed
+uno schema semplificato che riporta la struttura delle principali informazioni
+contenute nella \type{task\_struct} (che in seguito incontreremo a più
+riprese), è mostrato in \nfig.
+
+\begin{figure}[htb]
+ \centering
+ \includegraphics[width=13cm]{img/task_struct}
+ \caption{Schema semplificato dell'architettura delle strutture usate dal
+ kernel nella gestione dei processi.}
+ \label{fig:proc_task_struct}
+\end{figure}
-\subsection{Una panoramica sulle funzioni di gestione}
+Come accennato in \secref{sec:intro_unix_struct} è lo \textit{scheduler} che
+decide quale processo mettere in esecuzione; esso viene eseguito ad ogni
+system call ed ad ogni interrupt, (ma può essere anche attivato
+esplicitamente). Il timer di sistema provvede comunque a che esso sia invocato
+periodicamente, generando un interrupt periodico secondo la frequenza
+specificata dalla costante \macro{HZ}, definita in \file{asm/param.h} Il
+valore usuale è 100 (è espresso in Hertz), si ha cioè un interrupt dal timer
+ogni centesimo di secondo.
+
+Ogni volta che viene eseguito, lo \textit{scheduler} effettua il calcolo delle
+priorità dei vari processi attivi (torneremo su questo in
+\secref{sec:proc_priority}) e stabilisce quale di essi debba essere posto in
+esecuzione fino alla successiva invocazione.
+
+
+\subsection{Una panoramica sulle funzioni fondamentali}
\label{sec:proc_handling_intro}
I processi vengono creati dalla funzione \func{fork}; in molti unix questa è
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
-\section{La gestione dei processi}
+\section{Le funzioni di base}% della gestione dei processi}
\label{sec:proc_handling}
In questa sezione tratteremo le problematiche della gestione dei processi
all'interno del sistema, illustrandone tutti i dettagli. Inizieremo con le
funzioni elementari che permettono di leggerne gli identificatori, per poi
-passare alla spiegazione delle funzioni fondamentali che si usano per la
-creazione e la terminazione dei processi, e per la messa in esecuzione degli
-altri programmi.
+passare alla spiegazione delle funzioni base che si usano per la creazione e
+la terminazione dei processi, e per la messa in esecuzione degli altri
+programmi.
\subsection{Gli identificatori dei processi}
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
\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
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
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
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.
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.
+primo\footnote{a partire dal kernel 2.5.2-pre10 è stato introdotto il nuovo
+ scheduler di Ingo Molnar che esegue sempre per primo il figlio; per
+ mantenere la portabilità è opportuno non fare comunque affidamento su questo
+ comportamento} 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
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).
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
\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} ed i \textit{supplementary group id} (vedi
- \secref{sec:proc_user_group}).
+ \secref{sec:proc_access_id}).
\item gli identificatori per il controllo di sessione: il \textit{process
group id} e il \textit{session id} ed il terminale di controllo (vedi
\secref{sec:sess_xxx} e \secref{sec:sess_xxx}).
-\item i flag di \acr{suid} e \acr{sgid} (vedi \secref{sec:file_suid_sgid}).
\item la directory di lavoro e la directory radice (vedi
\secref{sec:file_work_dir} e \secref{sec:file_chroot}).
\item la maschera dei permessi di creazione (vedi \secref{sec:file_umask}).
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 è
\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
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
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}
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}
Sospende il processo corrente finché un figlio non è uscito, o finché un
segnale termina il processo o chiama una funzione di gestione.
-\bodydesc{
-La funzione restituisce il \acr{pid} del figlio in caso di successo e -1 in
-caso di errore; \var{errno} può assumere i valori:
+\bodydesc{La funzione restituisce il \acr{pid} del figlio in caso di successo
+ e -1 in caso di errore; \var{errno} può assumere i valori:
\begin{errlist}
\item[\macro{EINTR}] la funzione è stata interrotta da un segnale.
- \end{errlist}
-}
+ \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.
\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}
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
\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
\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
\subsection{Le funzioni \func{wait3} e \func{wait4}}
\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:
+Linux, seguendo una estensione di BSD, supporta altre due funzioni,
+\func{wait} e \func{waitpd}, per la lettura dello stato di terminazione di un
+processo, 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}
\end{functions}
\noindent
la struttura \type{rusage} è definita in \file{sys/resource.h}, e viene
-utilizzata anche dalla funzione \func{getrusage} per ottenere le risorse di
-sistema usate dal processo; la sua definizione è riportata in \nfig.
-\begin{figure}[!htb]
- \footnotesize
- \centering
- \begin{minipage}[c]{15cm}
- \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
-struct rusage {
- struct timeval ru_utime; /* user time used */
- struct timeval ru_stime; /* system time used */
- long ru_maxrss; /* maximum resident set size */
- long ru_ixrss; /* integral shared memory size */
- long ru_idrss; /* integral unshared data size */
- long ru_isrss; /* integral unshared stack size */
- long ru_minflt; /* page reclaims */
- long ru_majflt; /* page faults */
- long ru_nswap; /* swaps */
- long ru_inblock; /* block input operations */
- long ru_oublock; /* block output operations */
- long ru_msgsnd; /* messages sent */
- long ru_msgrcv; /* messages received */
- long ru_nsignals; ; /* signals received */
- long ru_nvcsw; /* voluntary context switches */
- long ru_nivcsw; /* involuntary context switches */
-};
- \end{lstlisting}
- \end{minipage}
- \normalsize
- \caption{La struttura \var{rusage} per la lettura delle informazioni dei
- delle risorse usate da un processo.}
- \label{fig:proc_rusage_struct}
-\end{figure}
+utilizzata anche dalla funzione \func{getrusage} (vedi \secref{sec:sys_xxx})
+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ù
necessario, ma aumenta la portabilità, e serve in caso si debba accedere
\func{exec} assume anche una serie di altre proprietà del processo chiamante;
la lista completa è la seguente:
\begin{itemize*}
-\item il \textit{process ID} (\acr{pid}) ed il \textit{parent process ID}
+\item il \textit{process id} (\acr{pid}) ed il \textit{parent process id}
(\acr{ppid}).
-\item il \textit{real user ID} ed il \textit{real group ID} (vedi
- \secref{sec:proc_user_group}).
-\item i \textit{supplementary group ID} (vedi \secref{sec:proc_user_group}).
-\item il \textit{session ID} ed il \textit{process group ID} (vedi
+\item il \textit{real user id} ed il \textit{real group id} (vedi
+ \secref{sec:proc_access_id}).
+\item i \textit{supplementary group id} (vedi \secref{sec:proc_access_id}).
+\item il \textit{session id} ed il \textit{process group id} (vedi
\secref{sec:sess_xxx}).
\item il terminale di controllo (vedi \secref{sec:sess_xxx}).
\item il tempo restante ad un allarme (vedi \secref{sec:sig_xxx}).
settaggio del flag di \textit{close-on-exec} sulle directory che apre, in
maniera trasparente all'utente.
-Abbiamo detto che il \textit{real user ID} ed il \textit{real group ID}
+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
+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
- group ID} vengono settati rispettivamente all'utente o al gruppo cui il file
+bit settato, nel qual caso \textit{effective user id} e \textit{effective
+ group id} vengono settati rispettivamente all'utente o al gruppo cui il file
appartiene (per i dettagli vedi \secref{sec:proc_perms}).
Se il file da eseguire è in formato \emph{a.out} e necessita di librerie
le problematiche connesse ad una gestione accorta dei privilegi.
-\subsection{Utente e gruppo di un processo}
-\label{sec:proc_user_group}
+\subsection{Gli identificatori del controllo di accesso}
+\label{sec:proc_access_id}
Come accennato in \secref{sec:intro_multiuser} il modello base\footnote{in
realtà già esistono estensioni di questo modello base, che lo rendono più
\label{tab:proc_uid_gid}
\end{table}
-Al primo gruppo appartengono il \textit{real user ID} e il \textit{real group
- ID}: questi vengono settati al login ai valori corrispondenti all'utente con
+Al primo gruppo appartengono il \textit{real user id} e il \textit{real group
+ id}: questi vengono settati al login ai valori corrispondenti all'utente con
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
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
-l'\textit{effective group ID} (a cui si aggiungono gli eventuali
+Al secondo gruppo appartengono l'\textit{effective user id} e
+l'\textit{effective group id} (a cui si aggiungono gli eventuali
\textit{supplementary group id} dei gruppi dei quale l'utente fa parte).
Questi sono invece gli identificatori usati nella verifiche dei permessi del
processo e per il controllo di accesso ai file (argomento affrontato in
\begin{functions}
\headdecl{unistd.h}
\headdecl{sys/types.h}
- \funcdecl{uid\_t getuid(void)} Restituisce il \textit{real user ID} del
+ \funcdecl{uid\_t getuid(void)} Restituisce il \textit{real user id} del
processo corrente.
- \funcdecl{uid\_t geteuid(void)} Restituisce l'\textit{effective user ID} del
+ \funcdecl{uid\_t geteuid(void)} Restituisce l'\textit{effective user id} del
processo corrente.
- \funcdecl{gid\_t getgid(void)} Restituisce il \textit{real group ID} del
+ \funcdecl{gid\_t getgid(void)} Restituisce il \textit{real group id} del
processo corrente.
- \funcdecl{gid\_t getegid(void)} Restituisce l'\textit{effective group ID} del
+ \funcdecl{gid\_t getegid(void)} Restituisce l'\textit{effective group id} del
processo corrente.
\bodydesc{Queste funzioni non riportano condizioni di errore.}
Le due funzioni che vengono usate per cambiare identità (cioè utente e gruppo
di appartenenza) ad un processo sono rispettivamente \func{setuid} e
-\func{setgid}; come accennato in \secref{sec:proc_user_group} in Linux esse
+\func{setgid}; come accennato in \secref{sec:proc_access_id} in Linux esse
seguono la semantica POSIX che prevede l'esistenza del \textit{saved user id}
e del \textit{saved group id}; i loro prototipi sono:
\begin{functions}
\headdecl{sys/types.h}
\funcdecl{int setreuid(uid\_t ruid, uid\_t euid)} Setta il \textit{real user
- ID} e l'\textit{effective user ID} del processo corrente ai valori
+ id} e l'\textit{effective user id} del processo corrente ai valori
specificati da \var{ruid} e \var{euid}.
\funcdecl{int setregid(gid\_t rgid, gid\_t egid)} Setta il \textit{real group
- ID} e l'\textit{effective group ID} del processo corrente ai valori
+ id} e l'\textit{effective group id} del processo corrente ai valori
specificati da \var{rgid} e \var{egid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
\textit{saved id} viene sempre settato al valore dell'\textit{effective id}.
+
+\subsection{Le funzioni \func{seteuid} e \func{setegid}}
+\label{sec:proc_seteuid}
+
+Queste funzioni sono un'estensione allo standard POSIX.1 (ma sono comunque
+supportate dalla maggior parte degli Unix) e usate per cambiare gli
+\textit{effective id}; i loro prototipi sono:
+\begin{functions}
+\headdecl{unistd.h}
+\headdecl{sys/types.h}
+
+\funcdecl{int seteuid(uid\_t uid)} Setta l'\textit{effective user id} del
+processo corrente a \var{uid}.
+
+\funcdecl{int setegid(gid\_t gid)} Setta l'\textit{effective group id} del
+processo corrente a \var{gid}.
+
+\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
+ di fallimento: l'unico errore possibile è \macro{EPERM}.}
+\end{functions}
+
+Gli utenti normali possono settare l'\textit{effective id} solo al valore del
+\textit{real id} o del \textit{saved id}, l'amministratore può specificare
+qualunque valore. Queste funzioni sono usate per permettere a root di settare
+solo l'\textit{effective id}, dato che l'uso normale di \func{setuid} comporta
+il settaggio di tutti gli identificatori.
+
+
\subsection{Le funzioni \func{setresuid} e \func{setresgid}}
\label{sec:proc_setresuid}
\headdecl{sys/types.h}
\funcdecl{int setresuid(uid\_t ruid, uid\_t euid, uid\_t suid)} Setta il
-\textit{real user ID}, l'\textit{effective user ID} e il \textit{saved user
- ID} del processo corrente ai valori specificati rispettivamente da
+\textit{real user id}, l'\textit{effective user id} e il \textit{saved user
+ id} del processo corrente ai valori specificati rispettivamente da
\var{ruid}, \var{euid} e \var{suid}.
\funcdecl{int setresgid(gid\_t rgid, gid\_t egid, gid\_t sgid)} Setta il
-\textit{real group ID}, l'\textit{effective group ID} e il \textit{saved group
- ID} del processo corrente ai valori specificati rispettivamente da
+\textit{real group id}, l'\textit{effective group id} e il \textit{saved group
+ id} del processo corrente ai valori specificati rispettivamente da
\var{rgid}, \var{egid} e \var{sgid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
valori che vuole; un valore di -1 per un qualunque parametro lascia inalterato
l'identificatore corrispondente.
-
-
-\subsection{Le funzioni \func{seteuid} e \func{setegid}}
-\label{sec:proc_seteuid}
-
-Queste funzioni sono un'estensione allo standard POSIX.1 (ma sono comunque
-supportate dalla maggior parte degli Unix) e usate per cambiare gli
-\textit{effective id}; i loro prototipi sono:
+Per queste funzioni esistono anche due controparti che permettono di leggere
+in blocco i vari identificatori: \func{getresuid} e \func{getresgid}; i loro
+prototipi sono:
\begin{functions}
\headdecl{unistd.h}
\headdecl{sys/types.h}
-\funcdecl{int seteuid(uid\_t uid)} Setta l'\textit{effective user ID} del
-processo corrente a \var{uid}.
-
-\funcdecl{int setegid(gid\_t gid)} Setta l'\textit{effective group ID} del
-processo corrente a \var{gid}.
+\funcdecl{int getresuid(uid\_t *ruid, uid\_t *euid, uid\_t *suid)} Legge il
+\textit{real user id}, l'\textit{effective user id} e il \textit{saved user
+ id} del processo corrente.
+
+\funcdecl{int getresgid(gid\_t *rgid, gid\_t *egid, gid\_t *sgid)} Legge il
+\textit{real group id}, l'\textit{effective group id} e il \textit{saved group
+ id} del processo corrente.
-\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
- di fallimento: l'unico errore possibile è \macro{EPERM}.}
+\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso di
+ fallimento: l'unico errore possibile è \macro{EFAULT} se gli indirizzi delle
+ variabili di ritorno non sono validi.}
\end{functions}
-Gli utenti normali possono settare l'\textit{effective id} solo al valore del
-\textit{real id} o del \textit{saved id}, l'amministratore può specificare
-qualunque valore. Queste funzioni sono usate per permettere a root di settare
-solo l'\textit{effective id}, dato che l'uso normale di \func{setuid} comporta
-il settaggio di tutti gli identificatori.
-
+Anche queste funzioni sono una estensione specifica di Linux, e non richiedono
+nessun privilegio. I valori sono restituiti negli argomenti, che vanno
+specificati come puntatori (è un'altro esempio di \textit{value result
+ argument}). Si noti che queste funzioni sono le uniche in grado di leggere i
+\textit{saved id}.
+
\subsection{Le funzioni \func{setfsuid} e \func{setfsgid}}
\label{sec:proc_setfsuid}
Queste funzioni sono usate per settare gli identificatori usati da Linux per
il controllo dell'accesso ai file. Come già accennato in
-\secref{sec:proc_user_group} in Linux è definito questo ulteriore gruppo di
+\secref{sec:proc_access_id} in Linux è definito questo ulteriore gruppo di
identificatori, che di norma sono assolutamente equivalenti agli
\textit{effective id}, dato che ogni cambiamento di questi ultimi viene
immediatamente riportato sui \textit{filesystem id}.
\begin{functions}
\headdecl{sys/fsuid.h}
-\funcdecl{int setfsuid(uid\_t fsuid)} Setta il \textit{filesystem user ID} del
+\funcdecl{int setfsuid(uid\_t fsuid)} Setta il \textit{filesystem user id} del
processo corrente a \var{fsuid}.
-\funcdecl{int setfsgid(gid\_t fsgid)} Setta l'\textit{filesystem group ID} del
+\funcdecl{int setfsgid(gid\_t fsgid)} Setta l'\textit{filesystem group id} del
processo corrente a \var{fsgid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
coincide con uno dei \textit{real}, \textit{effective} o \textit{saved id}.
+\subsection{Le funzioni \func{setgroups} e \func{getgroups}}
+\label{sec:proc_setgroups}
+
+Le ultime funzioni che esamineremo sono quelle sono quelle che permettono di
+operare sui gruppi supplementari. Ogni processo può avere fino a
+\macro{NGROUPS\_MAX} gruppi supplementari in aggiunta al gruppo primario,
+questi vengono ereditati dal processo padre e possono essere cambiati con
+queste funzioni.
+
+La funzione che permette di leggere i gruppi supplementari è \func{getgroups};
+questa funzione è definita nello standard POSIX ed il suo prototipo è:
+\begin{functions}
+ \headdecl{sys/types.h}
+ \headdecl{unistd.h}
+
+ \funcdecl{int getgroups(int size, gid\_t list[])} Legge gli identificatori
+ dei gruppi supplementari del processo sul vettore \param{list} di dimensione
+ \param{size}.
+
+ \bodydesc{La funzione restituisce il numero di gruppi letti in caso di
+ successo e -1 in caso di fallimento, nel qual caso \var{errno} viene
+ settata a:
+ \begin{errlist}
+ \item[\macro{EFAULT}] \param{list} non ha un indirizzo valido.
+ \item[\macro{EINVAL}] il valore di \param{size} è diverso da zero ma
+ minore del numero di gruppi supplementari del processo.
+ \end{errlist}}
+\end{functions}
+\noindent non è specificato se la funzione inserisca o meno nella lista
+l'\textit{effective user id} del processo. Se si specifica un valore di
+\param{size} uguale a 0 \param{list} non viene modificato, ma si ottiene il
+numero di gruppi supplementari.
+
+Una seconda funzione, \func{getgrouplist}, può invece essere usata per
+ottenere tutti i gruppi a cui appartiene un utente; il suo prototipo è:
+\begin{functions}
+ \headdecl{sys/types.h}
+ \headdecl{grp.h}
+
+ \funcdecl{int getgrouplist(const char *user, gid\_t group, gid\_t *groups,
+ int *ngroups)} Legge i gruppi supplementari dell'utente \param{user}.
+
+ \bodydesc{La funzione legge fino ad un massimo di \param{ngroups} valori,
+ 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
+perché qualora il valore specificato sia troppo piccolo la funzione ritorna -1
+e passando indietro il numero dei gruppi trovati.
+
+Per settare i gruppi supplementari di un processo ci sono due funzioni, che
+possono essere usate solo se si hanno i privilegi di amministratore. La prima
+delle due è \func{setgroups}, ed il suo prototipo è:
+\begin{functions}
+ \headdecl{sys/types.h}
+ \headdecl{grp.h}
+
+ \funcdecl{int setgroups(size\_t size, gid\_t *list)} Setta i gruppi
+ supplementari del processo ai valori specificati in \param{list}.
+
+ \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
+ fallimento, nel qual caso \var{errno} viene settata a:
+ \begin{errlist}
+ \item[\macro{EFAULT}] \param{list} non ha un indirizzo valido.
+ \item[\macro{EPERM}] il processo non ha i privilegi di amministratore.
+ \item[\macro{EINVAL}] il valore di \param{size} è maggiore del valore
+ massimo (\macro{NGROUPS}, che per Linux è 32).
+ \end{errlist}}
+\end{functions}
+
+Se invece si vogliono settare i gruppi supplementari del processo a quelli di
+un utente specifico si può usare \func{initgroups} il cui prototipo è:
+\begin{functions}
+ \headdecl{sys/types.h}
+ \headdecl{grp.h}
+
+ \funcdecl{int initgroups(const char *user, gid\_t group)} Setta i gruppi
+ supplementari del processo a quelli di cui è membro l'utente \param{user},
+ aggiungendo il gruppo addizionale \param{group}.
+
+ \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
+ fallimento, nel qual caso \var{errno} viene settata agli stessi valori di
+ \func{setgroups} più \macro{ENOMEM} quando non c'è memoria sufficiente per
+ allocare lo spazio per informazioni dei gruppi.}
+\end{functions}
+
+La funzione esegue la scansione del database dei gruppi (usualmente
+\file{/etc/groups}) cercando i gruppi di cui è membro \param{user} costruendo
+una lista di gruppi supplementari a cui aggiunge \param{group}, che poi setta
+usando \func{setgroups}.
+
+Si tenga presente che sia \func{setgroups} che \func{initgroups} non sono
+definite nello standard POSIX.1 e che pertanto non è possibile utilizzarle
+quando si definisce \macro{\_POSIX\_SOURCE} o si compila con il flag
+\cmd{-ansi}.
+
+
+\section{La gestione della priorità di esecuzione}
+\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.
+
+
+
+
+
\section{Problematiche di programmazione multitasking}
\label{sec:proc_multi_prog}
Benché i processi siano strutturati in modo da apparire il più possibile come
-indipendenti l'uno dall'altro, nella programmazione in un sistema multiutente
-occorre tenere conto di tutta una serie di problematiche che normalmente non
+indipendenti l'uno dall'altro, nella programmazione in un sistema multitasking
+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.
+programma alla volta.
-Pur non essendo tutto questo direttamente legato alla modalità specifica in
-cui il multitasking è implementato in un sistema unix-like, né al solo
-concetto di multitasking (le stesse problematiche si presentano ad esempio
-nella gestione degli interrupt hardware), in questa sezione conclusiva del
-capitolo in cui abbiamo affrontato la gestione dei processi, introdurremo
-sinteticamente queste problematiche, che ritroveremo a più riprese in capitoli
-successivi, con una breve definizione della terminologia e delle loro
-caratteristiche di fondo.
+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.
\subsection{Le operazioni atomiche}
-
+%%% Local Variables:
+%%% mode: latex
+%%% TeX-master: "gapil"
+%%% End: