ambiente multitasking.
-
\section{Introduzione}
\label{sec:proc_gen}
-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
+Inizieremo con un'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.
\acr{pid}.
Una seconda caratteristica di un sistema Unix è che la generazione di un
-processo è una operazione separata rispetto al lancio di un programma. In
+processo è un'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
+eseguirà, in un passo successivo, il programma desiderato: questo è ad esempio
quello che fa la shell quando mette in esecuzione il programma che gli
indichiamo nella linea di comando.
vero, in Linux ci sono alcuni processi che pur comparendo come figli di
init, o con \acr{pid} successivi, sono in realtà generati direttamente dal
kernel, (come \cmd{keventd}, \cmd{kswapd}, etc.)} si possono classificare i
-processi con la relazione padre/figlio in una organizzazione gerarchica ad
+processi con la relazione padre/figlio in un'organizzazione gerarchica ad
albero, in maniera analoga a come i file sono organizzati in un albero di
directory (si veda \secref{sec:file_organization}); in \curfig\ si è mostrato
-il risultato del comando \cmd{pstree} che permette di mostrare questa
+il risultato del comando \cmd{pstree} che permette di visualizzare questa
struttura, alla cui base c'è \cmd{init} che è progenitore di tutti gli altri
processi.
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
+\secref{sec:proc_wait}); queste funzioni restituiscono anche un'informazione
abbastanza limitata sulle cause della terminazione del processo figlio.
Quando un processo ha concluso il suo compito o ha incontrato un errore non
Il programma che un processo sta eseguendo si chiama immagine del processo (o
\textit{process image}), le funzioni della famiglia \func{exec} permettono di
caricare un'altro programma da disco sostituendo quest'ultimo all'immagine
-corrente; questo fa si che l'immagine precedente venga completamente
-cancellata. Questo significa che quando il nuovo programma esce anche il
+corrente; questo fa sì che l'immagine precedente venga completamente
+cancellata. Questo significa che quando il nuovo programma esce, anche il
processo termina, e non si può tornare alla precedente immagine.
Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto
\subsection{Gli identificatori dei processi}
\label{sec:proc_pid}
-Come accennato nell'introduzione ogni processo viene identificato dal sistema
+Come accennato nell'introduzione, ogni processo viene identificato dal sistema
da un numero identificativo unico, il \textit{process id} o \acr{pid};
quest'ultimo è un tipo di dato standard, il \type{pid\_t} che in genere è un
-intero con segno (nel caso di Linux e delle \acr{glibc} il tipo usato è \type{int}).
+intero con segno (nel caso di Linux e delle \acr{glibc} il tipo usato è
+\type{int}).
Il \acr{pid} viene assegnato in forma progressiva ogni volta che un nuovo
processo viene creato, fino ad un limite massimo (in genere essendo detto
numero memorizzato in un intero a 16 bit si arriva a 32767) oltre il quale si
riparte dal numero più basso disponibile\footnote{FIXME: verificare, non sono
- sicuro}. Per questo motivo processo il processo di avvio (\cmd{init}) ha
-sempre il \acr{pid} uguale a uno.
+ sicuro}. Per questo motivo, come visto in \secref{sec:proc_hierarchy}, 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
\funcdecl{pid\_t fork(void)}
Crea un nuovo processo.
- \bodydesc{Restituisce zero al padre e il \acr{pid} al figlio in caso di
- successo, ritorna -1 al padre (senza creare il figlio) in caso di errore;
- \var{errno} può assumere i valori:
+ \bodydesc{In caso di successo restituisce il \acr{pid} del figlio al padre e
+ zero al figlio; ritorna -1 al padre (senza creare il figlio) in caso di
+ errore; \var{errno} può assumere i valori:
\begin{errlist}
\item[\macro{EAGAIN}] non ci sono risorse sufficienti per creare un'altro
processo (per allocare la tabella delle pagine e le strutture del task) o
\end{functions}
Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che
-il processo figlio continuano ad essere eseguiti normalmente alla istruzione
+il processo figlio continuano ad essere eseguiti normalmente all'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. Si tenga presente però che la memoria è copiata, non condivisa, pertanto
-padre e figlio vedono variabili diverse.
+padre. Si tenga presente però che la memoria è copiata, non condivisa,
+pertanto padre e figlio vedono variabili diverse.
Per quanto riguarda la gestione della memoria in generale il segmento di
testo, che è identico, è condiviso e tenuto in read-only per il padre e per i
scelto di mantenere questa separazione, dato che, come per la prima modalità
d'uso, esistono numerosi scenari in cui si può usare una \func{fork} senza
aver bisogno di eseguire una \func{exec}. Inoltre, anche nel caso della
-seconda modalità di uso, avere le due funzioni separate permette al figlio di
+seconda modalità d'uso, avere le due funzioni separate permette al figlio di
cambiare gli attributi del processo (maschera dei segnali, redirezione
dell'output, \textit{user id}) prima della \func{exec}, rendendo così
relativamente facile intervenire sulle le modalità di esecuzione del nuovo
In \curfig\ si è riportato il corpo del codice del programma di esempio
\cmd{forktest}, che ci permette di illustrare molte caratteristiche dell'uso
della funzione \func{fork}. Il programma permette di creare un numero di figli
-specificato a linea di comando, e prende anche alcune opzioni per indicare
+specificato da linea di comando, e prende anche alcune opzioni per indicare
degli eventuali tempi di attesa in secondi (eseguiti tramite la funzione
\func{sleep}) per il padre ed il figlio (con \cmd{forktest -h} si ottiene la
descrizione delle opzioni); il codice completo, compresa la parte che gestisce
-le opzioni a riga di comando, è disponibile nel file \file{ForkTest.c}.
+le opzioni a riga di comando, è disponibile nel file \file{ForkTest.c},
+distribuito insieme agli altri sorgenti degli esempi su
+\href{http://firenze.linux.it/~piccardi/gapil_source.tgz}
+{\texttt{http://firenze.linux.it/\~~\hspace{-2.0mm}piccardi/gapil\_source.tgz}}.
Decifrato il numero di figli da creare, il ciclo principale del programma
(\texttt{\small 24--40}) esegue in successione la creazione dei processi figli
\end{verbatim} %$
\normalsize
-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
+Esaminiamo questo risultato: una prima conclusione che si può trarre è che non
+si può dire quale processo fra il padre ed il figlio venga eseguito per
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
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
+istruzioni del codice fra padre e figli, né sull'ordine in cui questi potranno
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
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 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).
+a loro (ogni processo vede solo la propria copia della memoria), e non hanno
+alcun effetto sul valore che le stesse variabili hanno nel processo padre (ed
+in eventuali altri processi figli che eseguano lo stesso codice).
Un secondo aspetto molto importante nella creazione dei processi figli è
quello dell'interazione dei vari processi con i file; per illustrarlo meglio
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
+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.
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
+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 possiamo parlare di un padre \textsl{adottivo})
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}). In questo
-caso infatti, dato che il segnale è generato dalla terminazione un figlio,
+caso infatti, dato che il segnale è generato dalla terminazione di un figlio,
avremo la certezza che la chiamata a \func{wait} non si bloccherà.
\begin{table}[!htb]
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}).
+\file{<sys/wait.h>} ed elencate in \tabref{tab:proc_status_macro} (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
+\macro{WTERMSIG} può essere confrontato con le costanti definite in
\file{signal.h} ed elencate in \tabref{tab:sig_signal_list}, e stampato usando
le apposite funzioni trattate in \secref{sec:sig_strsignal}.
\subsection{Le funzioni \func{wait3} e \func{wait4}}
\label{sec:proc_wait4}
-Linux, seguendo una estensione di BSD, supporta altre due funzioni per la
+Linux, seguendo un'estensione di BSD, supporta altre due funzioni per la
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
contiene una \file{/} esso viene considerato come un nome di programma, e
viene eseguita automaticamente una ricerca fra i file presenti nella lista di
directory specificate dalla variabile di ambiente \var{PATH}. Il file che
-viene posto in esecuzione è il primo che viene trovato. Se si ha un errore di
-permessi negati (cioè l'esecuzione della sottostante \func{execve} ritorna un
-\macro{EACCESS}), la ricerca viene proseguita nelle eventuali ulteriori
-directory indicate nel \var{PATH}, solo se non viene trovato nessun altro file
-viene finalmente restituito \macro{EACCESS}.
+viene posto in esecuzione è il primo che viene trovato. Se si ha un errore
+relativo a permessi di accesso insufficienti (cioè l'esecuzione della
+sottostante \func{execve} ritorna un \macro{EACCESS}), la ricerca viene
+proseguita nelle eventuali ulteriori directory indicate in \var{PATH}; solo se
+non viene trovato nessun altro file viene finalmente restituito
+\macro{EACCESS}.
Le altre quattro funzioni si limitano invece a cercare di eseguire il file
indicato dal parametro \var{path}, che viene interpretato come il
\begin{figure}[htb]
\centering
\includegraphics[width=13cm]{img/exec_rel}
- \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}}
+ \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}.}
\label{fig:proc_exec_relat}
\end{figure}
\var{tms\_cutime}, \var{tms\_ustime} (vedi \secref{sec:xxx_xxx}).
\end{itemize*}
-Oltre a questo i segnali che sono stati settati per essere ignorati nel
-processo chiamante mantengono lo stesso settaggio pure nel nuovo programma,
-tutti gli altri segnali vengono settati alla loro azione di default. Un caso
-speciale è il segnale \macro{SIGCHLD} che, quando settato a \macro{SIG\_IGN},
-può anche non essere resettato a \macro{SIG\_DFL} (si veda
-\secref{sec:sig_gen_beha}).
+Inoltre i segnali che sono stati settati per essere ignorati nel processo
+chiamante mantengono lo stesso settaggio pure nel nuovo programma, tutti gli
+altri segnali vengono settati alla loro azione di default. Un caso speciale è
+il segnale \macro{SIGCHLD} che, quando settato a \macro{SIG\_IGN}, può anche
+non essere resettato a \macro{SIG\_DFL} (si veda \secref{sec:sig_gen_beha}).
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
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
+Per le directory, lo standard POSIX.1 richiede che esse vengano chiuse
attraverso una \func{exec}, in genere questo è fatto dalla funzione
\func{opendir} (vedi \secref{sec:file_dir_read}) che effettua da sola il
settaggio del flag di \textit{close-on-exec} sulle directory che apre, in
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).
+\textit{supplementary group id} dei gruppi dei quali 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
dettaglio in \secref{sec:file_perm_overview}).
Questi identificatori normalmente sono identici ai corrispondenti del gruppo
-\textsl{reale} tranne nel caso in cui, come accennato in
+\textit{real} tranne nel caso in cui, come accennato in
\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
Questo in Linux viene fatto usando altri due gruppi di identificatori, il
\textit{saved} ed il \textit{filesystem}, analoghi ai precedenti. Il primo
gruppo è lo stesso usato in SVr4, e previsto dallo standard POSIX quando è
-definita la costante \macro{\_POSIX\_SAVED\_IDS}\footnote{in caso si abbia a
+definita la costante \macro{\_POSIX\_SAVED\_IDS},\footnote{in caso si abbia a
cuore la portabilità del programma su altri Unix è buona norma controllare
sempre la disponibilità di queste funzioni controllando se questa costante è
- definita}, il secondo gruppo è specifico di Linux e viene usato per
+ definita.} il secondo gruppo è specifico di Linux e viene usato per
migliorare la sicurezza con NFS.
Il \textit{saved user id} e il \textit{saved group id} sono copie
programma, effettuare il lavoro che non necessita di privilegi aggiuntivi, ed
eventualmente tornare indietro.
-Come esempio per chiarire dell'uso di queste funzioni prendiamo quello con cui
+Come esempio per chiarire l'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
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
+Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato, la
situazione degli identificatori è la seguente:
\begin{eqnarray*}
\label{eq:1}
\textit{saved group id} &=& \textrm{\acr{utmp}}
\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
+programma può accedere a \file{/var/log/utmp} in scrittura ed aggiornarlo. A
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à
\textit{effective group id}. All'uscita dal terminale, per poter di nuovo
aggiornare lo stato di \file{/var/log/utmp} il programma eseguirà una
\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:
+\acr{utmp}, ottenuto ad esempio con una precedente \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:
\begin{eqnarray*}
\label{eq:3}
\textit{real group id} &=& \textrm{\acr{gid} (invariato)} \\
si porrebbe per i \textit{saved id}: queste funzioni derivano da
un'implementazione che non ne prevede la presenza, e quindi non è possibile
usarle per correggere la situazione come nel caso precedente. Per questo
-motivo in Linux tutte le volte che vengono usata per modificare uno degli
+motivo in Linux tutte le volte che vengono usate per modificare uno degli
identificatori ad un valore diverso dal \textit{real id} precedente, il
\textit{saved id} viene sempre settato al valore dell'\textit{effective id}.
\subsection{Le funzioni \func{setresuid} e \func{setresgid}}
\label{sec:proc_setresuid}
-Queste due funzioni sono una estensione introdotta in Linux dal kernel 2.1.44,
+Queste due funzioni sono un'estensione introdotta in Linux dal kernel 2.1.44,
e permettono un completo controllo su tutti gli identificatori (\textit{real},
\textit{effective} e \textit{saved}), i prototipi sono:
\begin{functions}
variabili di ritorno non sono validi.}
\end{functions}
-Anche queste funzioni sono una estensione specifica di Linux, e non richiedono
+Anche queste funzioni sono un'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
\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.
+Le ultime funzioni che esamineremo 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 è:
\noindent la funzione esegue una scansione del database dei gruppi (si veda
\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.
+perché qualora il valore specificato sia troppo piccolo la funzione ritorna
+-1, 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
\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 è:
+un utente specifico, si può usare \func{initgroups} il cui prototipo è:
\begin{functions}
\headdecl{sys/types.h}
\headdecl{grp.h}
\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}.
+\file{/etc/groups}) cercando i gruppi di cui è membro \param{user} e
+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
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.
+con cui viene gestita l'assegnazione del tempo di CPU, ed illustreremo le
+varie funzioni di gestione.
\subsection{I meccanismi di \textit{scheduling}}
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.
+La cosa è resa ancora più complicata dal fatto che con le architetture
+multi-processore si introduce anche la problematica 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 anche
+quando si hanno più processori, e quindi potenzialmente anche dei processi che
+sono eseguiti davvero in contemporanea, si può pensare alle politiche di
+scheduling come concernenti la risorsa \textsl{tempo di esecuzione}, la cui
+assegnazione sarà governata dagli stessi meccanismi di scelta di priorità,
+solo 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
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.
+dinamiche, che assicurassero 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
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
+ ritenuto accettabile.} in cui è vitale che i processi che devono essere
+eseguiti in un determinato momento non debbano aspettare la conclusione di
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.
-
\subsection{Il meccanismo di \textit{scheduling} standard}
\label{sec:proc_sched_stand}
\label{sec:proc_atom_oper}
La nozione di \textsl{operazione atomica} deriva dal significato greco della
-parola atomo, cioè indivisibile; si dice infatti che una operazione è atomica
+parola atomo, cioè indivisibile; si dice infatti che un'operazione è atomica
quando si ha la certezza che, qualora essa venga effettuata, tutti i passaggi
che devono essere compiuti per realizzarla verranno eseguiti senza possibilità
di interruzione in una fase intermedia.
Nel caso dei segnali invece la situazione è molto più delicata, in quanto lo
stesso processo, e pure alcune system call, possono essere interrotti in
qualunque momento, e le operazioni di un eventuale \textit{signal handler}
-sono compiute nello stesso spazio di indirizzi del processo. Per questo anche
+sono compiute nello stesso spazio di indirizzi del processo. Per questo, anche
il solo accesso o l'assegnazione di una variabile possono non essere più
operazioni atomiche (torneremo su questi aspetti in \secref{sec:sign_xxx}).
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 è quello di una operazione che viene eseguita da un processo in più
+tipico è quello di un'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.
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
+un altro thread di esecuzione senza che questo comporti nessun problema
+nell'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.
+queste infatti vengono 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