\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}
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
\begin{functions}
\headdecl{sys/types.h}
\headdecl{unistd.h}
-\funcdecl{pid\_t getpid(void)} restituisce il pid del processo corrente.
-\funcdecl{pid\_t getppid(void)} restituisce il pid del padre del processo
+\funcdecl{pid\_t getpid(void)} Restituisce il pid del processo corrente.
+\funcdecl{pid\_t getppid(void)} Restituisce il pid del padre del processo
corrente.
-Entrambe le funzioni non riportano condizioni di errore.
+\bodydesc{Entrambe le funzioni non riportano condizioni di errore.}
\end{functions}
\noindent esempi dell'uso di queste funzioni sono riportati in
\figref{fig:proc_fork_code}, nel programma di esempio \file{ForkTest.c}.
\headdecl{sys/types.h}
\headdecl{unistd.h}
\funcdecl{pid\_t fork(void)}
- Restituisce zero al padre e il \acr{pid} al figlio in caso di successo,
- ritorna -1 al padre (senza creare il figlio) in caso di errore;
- \texttt{errno} può assumere i valori:
+ 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:
\begin{errlist}
- \item \macro{EAGAIN} non ci sono risorse sufficienti per creare un'altro
+ \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
si è esaurito il numero di processi disponibili.
- \item \macro{ENOMEM} non è stato possibile allocare la memoria per le
+ \item[\macro{ENOMEM}] non è stato possibile allocare la memoria per le
strutture necessarie al kernel per creare il nuovo processo.
- \end{errlist}
+ \end{errlist}}
\end{functions}
Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che
\item il \textit{process id}.
\item il \textit{parent process id} (quello del figlio viene settato al
\acr{pid} del padre).
-\item i valori dei tempi di esecuzione (\var{tms\_utime}, \var{tms\_stime},
- \var{tms\_cutime}, \var{tms\_uetime}) che nel figlio sono posti a zero.
-\item i \textit{file lock} (vedi \secref{sec:file_locking}, che non vengono ereditati dal figlio.
+\item i valori dei tempi di esecuzione (vedi \secref{sec:sys_xxx}) che
+ nel figlio sono posti a zero.
+\item i \textit{file lock} (vedi \secref{sec:file_locking}), che non
+ vengono ereditati dal figlio.
\item gli allarmi ed i segnali pendenti (vedi \secref{sec:sig_xxx}), che per il figlio vengono cancellati.
\end{itemize*}
\item tutti i descrittori dei file sono chiusi.
\item viene memorizzato lo stato di terminazione del processo.
\item ad ogni processo figlio viene assegnato un nuovo padre.
-\item viene inviato il segnale \macro{SIGCHLD} al processo padre.
+\item viene inviato il segnale \macro{SIGCHLD} al processo padre (vedi
+ \secref{sec:sig_xxx}) .
\item se il processo è un leader di sessione viene mandato un segnale di
- \macro{SIGHUP} a tutti i processi in background e il terminale di controllo
- viene disconnesso.
-\item se la conclusione di un processo rende orfano un \textit{process group}
- ciascun membro del gruppo viene bloccato, e poi gli vengono inviati in
- successione i segnali \macro{SIGHUP} e \macro{SIGCONT}.
+ \macro{SIGHUP} a tutti i processi in background e il terminale di
+ controllo viene disconnesso (vedi \secref{sec:sess_xxx}).
+\item se la conclusione di un processo rende orfano un \textit{process
+ group} ciascun membro del gruppo viene bloccato, e poi gli vengono
+ inviati in successione i segnali \macro{SIGHUP} e \macro{SIGCONT}
+ (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
\funcdecl{pid\_t wait(int * status)}
Sospende il processo corrente finché un figlio non è uscito, o finché un
-segnale termina il processo o chiama una funzione di gestione. Se un figlio è
-già uscito la funzione ritorna immediatamente. Al ritorno lo stato di
-termininazione del processo viene salvato nella variabile puntata da
-\var{status} e tutte le informazioni relative al processo (vedi
-\secref{sec:proc_termination}) vengono rilasciate.
+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:
\begin{errlist}
- \item \macro{EINTR} la funzione è stata interrotta da un segnale.
+ \item[\macro{EINTR}] la funzione è stata interrotta da un segnale.
\end{errlist}
+}
\end{functions}
-
+\noindent
è presente fin dalle prime versioni di unix; la funzione ritorna alla
-conclusione del primo figlio (o immediatamente se un figlio è già uscito). Nel
-caso un processo abbia più figli il valore di ritorno permette di identificare
-qual'è quello che è uscito.
-
-Questa funzione però ha il difetto di essere poco flessibile, in quanto
-ritorna all'uscita di un figlio qualunque. Nelle occasioni in cui è necessario
-attendere la conclusione di un processo specifico occorre predisporre un
-meccanismo che tenga conto dei processi già terminati, e ripeta la chiamata
-alla funzione nel caso il processo cercato sia ancora attivo.
+conclusione del primo figlio (o immediatamente se un figlio è già
+uscito). Se un figlio è già uscito la funzione ritorna immediatamente.
+
+Al ritorno lo stato di termininazione del processo viene salvato nella
+variabile puntata da \var{status} e tutte le informazioni relative al
+processo (vedi \secref{sec:proc_termination}) vengono rilasciate. Nel
+caso un processo abbia più figli il valore di ritorno permette di
+identificare qual'è quello che è uscito.
+
+Questa funzione ha il difetto di essere poco flessibile, in quanto
+ritorna all'uscita di un figlio qualunque. Nelle occasioni in cui è
+necessario attendere la conclusione di un processo specifico occorre
+predisporre un meccanismo che tenga conto dei processi già terminati, e
+provveda a ripetere la chiamata alla funzione nel caso il processo
+cercato sia ancora attivo.
Per questo motivo lo standard POSIX.1 ha introdotto la funzione \func{waitpid}
che effettua lo stesso servizio, ma dispone di una serie di funzionalità più
\headdecl{sys/types.h}
\headdecl{sys/wait.h}
\funcdecl{pid\_t waitpid(pid\_t pid, int * status, int options)}
+Attende la conclusione di un processo figlio.
-La funzione restituisce il \acr{pid} del processo che è uscito, 0 se è stata
-specificata l'opzione \macro{WNOHANG} e il processo non è uscito e -1 per un
-errore, nel qual caso \var{errno} assumerà i valori:
+\bodydesc{La funzione restituisce il \acr{pid} del processo che è uscito, 0 se
+ è stata specificata l'opzione \macro{WNOHANG} e il processo non è uscito e
+ -1 per un errore, nel qual caso \var{errno} assumerà i valori:
\begin{errlist}
- \item \macro{EINTR} se non è stata specificata l'opzione \macro{WNOHANG} e
+ \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 è
+ \item[\macro{ECHILD}] il processo specificato da \var{pid} non esiste o non è
figlio del processo chiamante.
- \end{errlist}
+ \end{errlist}}
\end{functions}
Le differenze principali fra le due funzioni sono che \func{wait} si blocca
\headdecl{sys/resource.h}
\funcdecl{pid\_t wait4(pid\_t pid, int * status, int options, struct rusage
* rusage)}
- La funzione è identica a \func{waitpid} sia per comportamento che per i
- valori dei parametri, ma restituisce in \var{rusage} un sommario delle
- risorse usate dal processo (per i dettagli vedi \secref{sec:xxx_limit_res})
+ È identica a \func{waitpid} sia per comportamento che per i
+ 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
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; in Linux è definita come:
+sistema usate dal processo; la sua definizione è riportata in \nfig.
\begin{figure}[!htb]
\footnotesize
\centering
delle risorse usate da un processo.}
\label{fig:proc_rusage_struct}
\end{figure}
-In genere includere esplicitamente \file{<sys/time.h>} non è più necessario,
-ma aumenta la portabilità, e serve in caso si debba accedere ai campi di
-\var{rusage} definiti come \type{struct timeval}. La struttura è ripresa dalla
-versione 4.3 Reno di BSD, attualmente (con il kernel 2.4.x) i soli campi che
-sono mantenuti sono: \var{ru\_utime}, \var{ru\_stime}, \var{ru\_minflt},
-\var{ru\_majflt}, e \var{ru\_nswap}.
+
+In genere includere esplicitamente \file{<sys/time.h>} non è più
+necessario, ma aumenta la portabilità, e serve in caso si debba accedere
+ai campi di \var{rusage} definiti come \type{struct timeval}. La
+struttura è ripresa da BSD 4.3, attualmente (con il kernel 2.4.x) i soli
+campi che sono mantenuti sono: \var{ru\_utime}, \var{ru\_stime},
+\var{ru\_minflt}, \var{ru\_majflt}, e \var{ru\_nswap}.
\subsection{Le funzioni \func{exec}}
front-end a \func{execve}. Il prototipo di quest'ultima è:
\begin{prototype}{unistd.h}
{int execve(const char * filename, char * const argv [], char * const envp[])}
+ Esegue il programma contenuto nel file \param{filename}.
- La funzione esegue il file o lo script indicato da \var{filename},
- passandogli la lista di argomenti indicata da \var{argv} 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[])}.
-
- La funzione ritorna -1 solo in caso di errore, nel qual caso caso la
- \var{errno} può assumere i valori:
+ \bodydesc{La funzione ritorna -1 solo in caso di errore, nel qual caso
+ caso la \var{errno} può assumere i valori:
\begin{errlist}
- \item \macro{EACCES} il file non è eseguibile, oppure il filesystem è
+ \item[\macro{EACCES}] il file non è eseguibile, oppure il filesystem è
montato in \cmd{noexec}, oppure non è un file normale o un interprete.
- \item \macro{EPERM} il file ha i bit \acr{suid} o \acr{sgid} ma l'utente non
+ \item[\macro{EPERM}] il file ha i bit \acr{suid} o \acr{sgid} ma l'utente non
è root o il filesystem è montato con \cmd{nosuid}, oppure
- \item \macro{ENOEXEC} il file è in un formato non eseguibile o non
+ \item[\macro{ENOEXEC}] il file è in un formato non eseguibile o non
riconosciuto come tale, o compilato per un'altra architettura.
- \item \macro{ENOENT} il file o una delle librerie dinamiche o l'interprete
+ \item[\macro{ENOENT}] il file o una delle librerie dinamiche o l'interprete
necessari per eseguirlo non esistono.
- \item \macro{ETXTBSY} L'eseguibile è aperto in scrittura da uno o più
+ \item[\macro{ETXTBSY}] L'eseguibile è aperto in scrittura da uno o più
processi.
- \item \macro{EINVAL} L'eseguibile ELF ha più di un segmento
- \macro{PF\_INTERP}, cioè chiede di essere eseguito da più di un interprete.
- \item \macro{ELIBBAD} Un interprete ELF non è in un formato riconoscibile.
+ \item[\macro{EINVAL}] L'eseguibile ELF ha più di un segmento
+ \macro{PF\_INTERP}, cioè chiede di essere eseguito da più di un
+ interprete.
+ \item[\macro{ELIBBAD}] Un interprete ELF non è in un formato
+ riconoscibile.
\end{errlist}
ed inoltre anche \macro{EFAULT}, \macro{ENOMEM}, \macro{EIO},
\macro{ENAMETOOLONG}, \macro{E2BIG}, \macro{ELOOP}, \macro{ENOTDIR},
- \macro{ENFILE}, \macro{EMFILE}.
+ \macro{ENFILE}, \macro{EMFILE}.}
\end{prototype}
+La funzione \func{exec} esegue il file o lo script indicato da
+\var{filename}, passandogli la lista di argomenti indicata da \var{argv}
+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
+\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
prototipi sono:
argomento. I parametri successivi consentono di specificare gli argomenti a
linea di comando e l'ambiente ricevuti dal nuovo processo.
-Queste funzioni ritornano solo in caso di errore, restituendo -1; nel qual
-caso \var{errno} andrà ad assumere i valori visti in precedenza per
-\func{execve}.
+\bodydesc{Queste funzioni ritornano solo in caso di errore, restituendo
+ -1; nel qual caso \var{errno} andrà ad assumere i valori visti in
+ precedenza per \func{execve}.}
\end{functions}
Per capire meglio le differenze fra le funzioni della famiglia si può fare
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,
\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
\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
il programma\\
\acr{gid} & \textit{real group id} & indica il gruppo dell'utente
che ha lanciato il programma \\
+ \hline
\acr{euid} & \textit{effective user id} & indica l'utente usato
dal programma nel controllo di accesso \\
\acr{egid} & \textit{effective group id} & indica il gruppo
usato dal programma nel controllo di accesso \\
-- & \textit{supplementary group id} & indica i gruppi cui
l'utente appartiene \\
+ \hline
-- & \textit{saved user id} & copia dell'\acr{euid} iniziale\\
-- & \textit{saved group id} & copia dell'\acr{egid} iniziale \\
+ \hline
\acr{fsuid} & \textit{filesystem user id} & indica l'utente effettivo per
il filesystem \\
\acr{fsgid} & \textit{filesystem group id} & indica il gruppo effettivo
\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.
- \noindent Queste funzioni non riportano condizioni di errore.
+ \bodydesc{Queste funzioni non riportano condizioni di errore.}
\end{functions}
In generale l'uso di privilegi superiori deve essere limitato il più
\headdecl{unistd.h}
\headdecl{sys/types.h}
-\funcdecl{int setuid(uid\_t uid)} setta l'\textit{user ID} del processo
+\funcdecl{int setuid(uid\_t uid)} Setta l'\textit{user ID} del processo
corrente.
-\funcdecl{int setgid(gid\_t gid)} setta il \textit{group ID} del processo
+\funcdecl{int setgid(gid\_t gid)} Setta il \textit{group ID} del processo
corrente.
-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{EPERM}.}
\end{functions}
Il funzionamento di queste due funzioni è analogo, per cui considereremo solo
\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
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:
\headdecl{unistd.h}
\headdecl{sys/types.h}
-\funcdecl{int setreuid(uid\_t ruid, uid\_t euid)} setta il \textit{real user
+\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
specificati da \var{ruid} e \var{euid}.
-\funcdecl{int setregid(gid\_t rgid, gid\_t egid)} setta il \textit{real group
+\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
specificati da \var{rgid} e \var{egid}.
-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{EPERM}.}
\end{functions}
I processi non privilegiati possono settare i \textit{real id} soltanto ai
\headdecl{unistd.h}
\headdecl{sys/types.h}
-\funcdecl{int setresuid(uid\_t ruid, uid\_t euid, uid\_t suid)} setta il
+\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
\var{ruid}, \var{euid} e \var{suid}.
-\funcdecl{int setresgid(gid\_t rgid, gid\_t egid, gid\_t sgid)} setta il
+\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
\var{rgid}, \var{egid} e \var{sgid}.
-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{EPERM}.}
\end{functions}
I processi non privilegiati possono cambiare uno qualunque degli
\headdecl{unistd.h}
\headdecl{sys/types.h}
-\funcdecl{int seteuid(uid\_t uid)} setta l'\textit{effective user ID} del
+\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
+\funcdecl{int setegid(gid\_t gid)} Setta l'\textit{effective group ID} del
processo corrente a \var{gid}.
-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{EPERM}.}
\end{functions}
Gli utenti normali possono settare l'\textit{effective id} solo al valore del
\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}.
-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{EPERM}.}
\end{functions}
Queste funzioni hanno successo solo se il processo chiamante ha i privilegi di
\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.
+
+
+