+%% prochand.tex
+%%
+%% Copyright (C) 2000-2002 Simone Piccardi. Permission is granted to
+%% copy, distribute and/or modify this document under the terms of the GNU Free
+%% Documentation License, Version 1.1 or any later version published by the
+%% Free Software Foundation; with the Invariant Sections being "Prefazione",
+%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the
+%% license is included in the section entitled "GNU Free Documentation
+%% License".
+%%
\chapter{La gestione dei processi}
\label{cha:process_handling}
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}.
+numero univoco, il cosiddetto \textit{process identifier} o, più brevemente,
+\acr{pid}, assegnato in forma progressiva (vedi \secref{sec:proc_pid}) quando
+il processo viene creato.
Una seconda caratteristica di un sistema Unix è che la generazione di un
processo è un'operazione separata rispetto al lancio di un programma. In
possono classificare i 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 visualizzare questa struttura, alla cui
-base c'è \cmd{init} che è progenitore di tutti gli altri processi.
-
+\secref{sec:file_organization}); in \figref{fig:proc_tree} si è mostrato 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.
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.
+\textit{process table}; per ciascun processo viene mantenuta una voce,
+costituita da una struttura \struct{task\_struct}, nella tabella dei processi
+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 \struct{task\_struct} (che in
+seguito incontreremo a più riprese), è mostrato in
+\figref{fig:proc_task_struct}.
\begin{figure}[htb]
\centering
\end{figure}
-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
+Come accennato in \secref{sec:intro_unix_struct} è lo
+\textit{scheduler}\index{scheduler} che decide quale processo mettere in
+esecuzione; esso viene eseguito ad ogni system call ed ad ogni
+interrupt,\footnote{più in una serie di altre occasioni. NDT completare questa
+ parte.} (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
+\const{HZ}, definita in \file{asm/param.h}, ed il cui valore è espresso in
+Hertz.\footnote{Il valore usuale di questa costante è 100, per tutte le
+ architetture eccetto l'alpha, per la quale è 1000. Occorre fare attenzione a
+ non confondere questo valore con quello dei clock tick (vedi
+ \secref{sec:sys_unix_time}).}
+%Si ha cioè un interrupt dal timer ogni centesimo di secondo.
+
+Ogni volta che viene eseguito, lo \textit{scheduler}\index{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.
una system call, Linux però usa un'altra nomenclatura, e la funzione
\func{fork} è basata a sua volta sulla system call \func{\_\_clone}, che viene
usata anche per generare i \textit{thread}. Il processo figlio creato dalla
-\func{fork} è una copia identica del processo processo padre, ma ha nuovo
+\func{fork} è una copia identica del processo processo padre, ma ha un nuovo
\acr{pid} e viene eseguito in maniera indipendente (le differenze fra padre e
figlio sono affrontate in dettaglio in \secref{sec:proc_fork}).
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
+caricare un altro programma da disco sostituendo quest'ultimo all'immagine
corrente; questo fa sì che l'immagine precedente venga completamente
-cancellata. Questo significa che quando il nuovo programma esce, anche il
+cancellata. Questo significa che quando il nuovo programma termina, 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
\label{sec:proc_pid}
Come accennato nell'introduzione, ogni processo viene identificato dal sistema
-da un numero identificativo unico, il \textit{process id} o \acr{pid};
+da un numero identificativo univoco, 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}).
-
-Il \acr{pid} viene assegnato in forma progressiva ogni volta che un nuovo
-processo viene creato, fino ad un limite che, essendo il \acr{pid} un numero
-positivo memorizzato in un intero a 16 bit, arriva ad un massimo di 32767.
-Oltre questo valore l'assegnazione riparte dal numero più basso disponibile a
-partire da un minimo di 300,\footnote{questi valori sono definiti dalla macro
- \macro{PID\_MAX} in \file{threads.h} e direttamente in \file{fork.c} nei
- sorgenti del kernel.} che serve a riservare i \acr{pid} più bassi ai processi
-eseguiti dal direttamente dal kernel. Per questo motivo, come visto in
-\secref{sec:proc_hierarchy}, il processo di avvio (\cmd{init}) ha sempre il
+\ctyp{int}).
+
+Il \acr{pid} viene assegnato in forma progressiva\footnote{in genere viene
+ assegnato il numero successivo a quello usato per l'ultimo processo creato,
+ a meno che questo numero non sia già utilizzato per un altro \acr{pid},
+ \acr{pgid} o \acr{sid} (vedi \secref{sec:sess_proc_group}).} ogni volta che
+un nuovo processo viene creato, fino ad un limite che, essendo il \acr{pid} un
+numero positivo memorizzato in un intero a 16 bit, arriva ad un massimo di
+32768. Oltre questo valore l'assegnazione riparte dal numero più basso
+disponibile a partire da un minimo di 300,\footnote{questi valori, fino al
+ kernel 2.4.x, sono definiti dalla macro \const{PID\_MAX} in \file{threads.h}
+ e direttamente in \file{fork.c}, con il kernel 2.5.x e la nuova interfaccia
+ per i thread creata da Ingo Molnar anche il meccanismo di allocazione dei
+ \acr{pid} è stato modificato.} che serve a riservare i \acr{pid} più bassi
+ai processi eseguiti direttamente dal kernel. 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
-\textit{parent process id}). Questi due identificativi possono essere
-ottenuti da programma usando le funzioni:
+\textit{parent process ID}). Questi due identificativi possono essere
+ottenuti usando le due funzioni \funcd{getpid} e \funcd{getppid}, i cui
+prototipi sono:
\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
- corrente.
+ \headdecl{sys/types.h}
+ \headdecl{unistd.h}
+ \funcdecl{pid\_t getpid(void)}
+
+ Restituisce il \acr{pid} del processo corrente.
+
+ \funcdecl{pid\_t getppid(void)}
+
+ Restituisce il \acr{pid} del padre del processo corrente.
\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}.
+\figref{fig:proc_fork_code}, nel programma \file{ForkTest.c}.
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
+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
sessione.
Oltre al \acr{pid} e al \acr{ppid}, (e a quelli che vedremo in
-\secref{sec:sess_xxx}, relativi al controllo di sessione), ad ogni processo
-vengono associati degli altri identificatori che vengono usati per il
+\secref{sec:sess_proc_group}, relativi al controllo di sessione), ad ogni
+processo vengono associati degli altri identificatori che vengono usati per il
controllo di accesso. Questi servono per determinare se un processo può
eseguire o meno le operazioni richieste, a seconda dei privilegi e
dell'identità di chi lo ha posto in esecuzione; l'argomento è complesso e sarà
\subsection{La funzione \func{fork}}
\label{sec:proc_fork}
-La funzione \func{fork} è la funzione fondamentale della gestione dei
+La funzione \funcd{fork} è la funzione fondamentale della gestione dei
processi: come si è detto l'unico modo di creare un nuovo processo è
attraverso l'uso di questa funzione, essa quindi riveste un ruolo centrale
tutte le volte che si devono scrivere programmi che usano il multitasking. Il
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
+ \item[\errcode{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[\errcode{ENOMEM}] non è stato possibile allocare la memoria per le
strutture necessarie al kernel per creare il nuovo processo.
\end{errlist}}
\end{functions}
Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che
-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
+il processo figlio continuano ad essere eseguiti normalmente a partire
+dall'istruzione successiva alla \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.
-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
-figli. Per gli altri segmenti Linux utilizza la tecnica del \textit{copy on
- write}\index{copy on write}; questa tecnica comporta che una pagina di
-memoria viene effettivamente copiata per il nuovo processo solo quando ci
-viene effettuata sopra una scrittura (e si ha quindi una reale differenza fra
-padre e figlio). In questo modo si rende molto più efficiente il meccanismo
-della creazione di un nuovo processo, non essendo più necessaria la copia di
-tutto lo spazio degli indirizzi virtuali del padre, ma solo delle pagine di
-memoria che sono state modificate, e solo al momento della modifica stessa.
+Per quanto riguarda la gestione della memoria, in generale il segmento di
+testo, che è identico per i due processi, è condiviso e tenuto in read-only
+per il padre e per i figli. Per gli altri segmenti Linux utilizza la tecnica
+del \textit{copy on write}\index{copy on write}; questa tecnica comporta che
+una pagina di memoria viene effettivamente copiata per il nuovo processo solo
+quando ci viene effettuata sopra una scrittura (e si ha quindi una reale
+differenza fra padre e figlio). In questo modo si rende molto più efficiente
+il meccanismo della creazione di un nuovo processo, non essendo più necessaria
+la copia di tutto lo spazio degli indirizzi virtuali del padre, ma solo delle
+pagine di memoria che sono state modificate, e solo al momento della modifica
+stessa.
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
che non è il \acr{pid} di nessun processo.
\begin{figure}[!htb]
- \footnotesize
- \begin{lstlisting}{}
-#include <errno.h> /* error definitions and routines */
-#include <stdlib.h> /* C standard library */
-#include <unistd.h> /* unix standard library */
-#include <stdio.h> /* standard I/O library */
-#include <string.h> /* string functions */
-
-/* Help printing routine */
-void usage(void);
-
-int main(int argc, char *argv[])
-{
-/*
- * Variables definition
- */
- int nchild, i;
- pid_t pid;
- int wait_child = 0;
- int wait_parent = 0;
- int wait_end = 0;
- ... /* handling options */
- nchild = atoi(argv[optind]);
- printf("Test for forking %d child\n", nchild);
- /* loop to fork children */
- for (i=0; i<nchild; i++) {
- if ( (pid = fork()) < 0) {
- /* on error exit */
- printf("Error on %d child creation, %s\n", i+1, strerror(errno));
- exit(-1);
- }
- if (pid == 0) { /* child */
- printf("Child %d successfully executing\n", ++i);
- if (wait_child) sleep(wait_child);
- printf("Child %d, parent %d, exiting\n", i, getppid());
- exit(0);
- } else { /* parent */
- printf("Spawned %d child, pid %d \n", i+1, pid);
- if (wait_parent) sleep(wait_parent);
- printf("Go to next child \n");
- }
- }
- /* normal exit */
- if (wait_end) sleep(wait_end);
- return 0;
-}
- \end{lstlisting}
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \includecodesample{listati/ForkTest.c}
+ \end{minipage}
+ \normalsize
\caption{Esempio di codice per la creazione di nuovi processi.}
\label{fig:proc_fork_code}
\end{figure}
Normalmente la chiamata a \func{fork} può fallire solo per due ragioni, o ci
sono già troppi processi nel sistema (il che di solito è sintomo che
qualcos'altro non sta andando per il verso giusto) o si è ecceduto il limite
-sul numero totale di processi permessi all'utente (vedi \secref{sec:sys_xxx}).
+sul numero totale di processi permessi all'utente (vedi
+\secref{sec:sys_resource_limit}, ed in particolare
+\tabref{tab:sys_rlimit_values}).
L'uso di \func{fork} avviene secondo due modalità principali; la prima è
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.
+ne esegue un'altra. È il caso tipico dei programmi server (il modello
+\textit{client-server} è illustrato in \secref{sec:net_cliserv}) in cui il
+padre riceve ed accetta le richieste da parte dei programmi 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
-crea un figlio la cui unica operazione è quella fare una \func{exec} (di cui
-parleremo in \secref{sec:proc_exec}) subito dopo la \func{fork}.
+crea un figlio la cui unica operazione è quella di fare una \func{exec} (di
+cui parleremo in \secref{sec:proc_exec}) subito dopo la \func{fork}.
Alcuni sistemi operativi (il VMS ad esempio) combinano le operazioni di questa
seconda modalità (una \func{fork} seguita da una \func{exec}) in un'unica
aver bisogno di eseguire una \func{exec}. Inoltre, anche nel caso della
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ì
+dell'output, identificatori) prima della \func{exec}, rendendo così
relativamente facile intervenire sulle le modalità di esecuzione del nuovo
programma.
-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
+In \figref{fig:proc_fork_code} è riportato il corpo del codice del programma
+di esempio \cmd{forktest}, che permette di illustrare molte caratteristiche
+dell'uso della funzione \func{fork}. Il programma crea un numero di figli
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},
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}}.
+\href{http://gapil.firenze.linux.it/gapil_source.tgz}
+{\texttt{http://gapil.firenze.linux.it/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
alla conclusione del ciclo, prima di uscire, può essere specificato un altro
periodo di attesa.
-Se eseguiamo il comando senza specificare attese (come si può notare in
-\texttt{\small 17--19} i valori di default specificano di non attendere),
-otterremo come output sul terminale:
+Se eseguiamo il comando\footnote{che è preceduto dall'istruzione \code{export
+ LD\_LIBRARY\_PATH=./} per permettere l'uso delle librerie dinamiche.}
+senza specificare attese (come si può notare in (\texttt{\small 17--19}) i
+valori predefiniti specificano di non attendere), otterremo come output sul
+terminale:
\footnotesize
\begin{verbatim}
-[piccardi@selidor sources]$ ./forktest 3
+[piccardi@selidor sources]$ export LD_LIBRARY_PATH=./; ./forktest 3
Process 1963: forking 3 child
Spawned 1 child, pid 1964
Child 1 successfully executing
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
- 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.
+ scheduler\index{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
+scheduling usato dal kernel, dalla particolare situazione in cui si trova la
macchina al momento della chiamata, risultando del tutto impredicibile.
Eseguendo più volte il programma di prova e producendo un numero diverso di
figli, si sono ottenute situazioni completamente diverse, compreso il caso in
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
- condition} (vedi \secref{sec:proc_race_cond}.
+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
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,
+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
proprietà; la lista dettagliata delle proprietà che padre e figlio hanno in
comune dopo l'esecuzione di una \func{fork} è la seguente:
\begin{itemize*}
-\item i file aperti e gli eventuali flag di \textit{close-on-exec} settati
- (vedi \secref{sec:proc_exec} e \secref{sec:file_fcntl}).
-\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
+\item i file aperti e gli eventuali flag di
+ \textit{close-on-exec}\index{close-on-exec} impostati (vedi
+ \secref{sec:proc_exec} e \secref{sec:file_fcntl}).
+\item gli identificatori per il controllo di accesso: l'\textsl{user-ID
+ reale}, il \textsl{group-ID reale}, l'\textsl{user-ID effettivo}, il
+ \textsl{group-ID effettivo} ed i \textit{group-ID supplementari} (vedi
\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}).
+ group-ID} e il \textit{session id} ed il terminale di controllo (vedi
+ \secref{sec:sess_proc_group}).
\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}).
-\item la maschera dei segnali bloccati (vedi \secref{sec:sig_sigpending}) e le
+\item la maschera dei segnali bloccati (vedi \secref{sec:sig_sigmask}) e le
azioni installate (vedi \secref{sec:sig_gen_beha}).
\item i segmenti di memoria condivisa agganciati al processo (vedi
-\secref{sec:ipc_xxx}).
-\item i limiti sulle risorse (vedi \secref{sec:sys_xxx}).
+ \secref{sec:ipc_sysv_shm}).
+\item i limiti sulle risorse (vedi \secref{sec:sys_resource_limit}).
\item le variabili di ambiente (vedi \secref{sec:proc_environ}).
\end{itemize*}
le differenze fra padre e figlio dopo la \func{fork} invece sono:
\begin{itemize*}
\item il valore di ritorno di \func{fork}.
-\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 (vedi \secref{sec:sys_xxx}) che
- nel figlio sono posti a zero.
-\item i \textit{file lock} (vedi \secref{sec:file_locking}), che non
+\item il \acr{pid} (\textit{process id}).
+\item il \acr{ppid} (\textit{parent process id}), quello del figlio viene
+ impostato al \acr{pid} del padre.
+\item i valori dei tempi di esecuzione della struttura \struct{tms} (vedi
+ \secref{sec:sys_cpu_times}) che nel figlio sono posti a zero.
+\item i \textit{lock} sui file (vedi \secref{sec:file_locking}), che non
vengono ereditati dal figlio.
\item gli allarmi ed i segnali pendenti (vedi \secref{sec:sig_gen_beha}), che
per il figlio vengono cancellati.
\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 è
-assolutamente trascurabile, e l'uso di questa funzione (che resta un caso
-speciale della funzione \func{clone}), è deprecato; per questo eviteremo di
-trattarla ulteriormente.
+Dato che Linux supporta il \textit{copy on write}\index{copy on write} la
+perdita di prestazioni è assolutamente trascurabile, e l'uso di questa
+funzione (che resta un caso speciale della system call \func{\_\_clone}), è
+deprecato; per questo eviteremo di trattarla ulteriormente.
\subsection{La conclusione di un processo.}
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
seconda, dato che \func{abort} si limita a generare il segnale
-\macro{SIGABRT}.
+\const{SIGABRT}.
Qualunque sia la modalità di conclusione di un processo, il kernel esegue
comunque una serie di operazioni: chiude tutti i file aperti, rilascia la
\item viene memorizzato lo stato di terminazione del processo.
\item ad ogni processo figlio viene assegnato un nuovo padre (in genere
\cmd{init}).
-\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 (vedi \secref{sec:sess_xxx}).
+\item viene inviato il segnale \const{SIGCHLD} al processo padre (vedi
+ \secref{sec:sig_sigchld}).
+\item se il processo è un leader di sessione ed il suo terminale di controllo
+ è quello della sessione viene mandato un segnale di \const{SIGHUP} a tutti i
+ processi del gruppo di foreground e il terminale di controllo viene
+ disconnesso (vedi \secref{sec:sess_ctrl_term}).
\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}).
+ inviati in successione i segnali \const{SIGHUP} e \const{SIGCONT}
+ (vedi ancora \secref{sec:sess_ctrl_term}).
\end{itemize*}
Oltre queste operazioni è però necessario poter disporre di un meccanismo
Questo viene fatto mantenendo attiva la voce nella tabella dei processi, e
memorizzando alcuni dati essenziali, come il \acr{pid}, i tempi di CPU usati
-dal processo (vedi \secref{sec:sys_unix_time}) e lo stato di
-terminazione\footnote{NdA verificare esattamente cosa c'è!}, mentre la memoria
-in uso ed i file aperti vengono rilasciati immediatamente. I processi che sono
-terminati, ma il cui stato di terminazione non è stato ancora ricevuto dal
-padre sono chiamati \textit{zombie}, essi restano presenti nella tabella dei
-processi ed in genere possono essere identificati dall'output di \cmd{ps} per
-la presenza di una \texttt{Z} nella colonna che ne indica lo stato. Quando il
+dal processo (vedi \secref{sec:sys_unix_time}) e lo stato di terminazione,
+mentre la memoria in uso ed i file aperti vengono rilasciati immediatamente. I
+processi che sono terminati, ma il cui stato di terminazione non è stato
+ancora ricevuto dal padre sono chiamati \textit{zombie}\index{zombie}, essi
+restano presenti nella tabella dei processi ed in genere possono essere
+identificati dall'output di \cmd{ps} per la presenza di una \texttt{Z} nella
+colonna che ne indica lo stato (vedi \tabref{tab:proc_proc_states}). Quando il
padre effettuerà la lettura dello stato di uscita anche questa informazione,
non più necessaria, verrà scartata e la terminazione potrà dirsi completamente
conclusa.
571 pts/0 Z 0:00 [forktest <defunct>]
572 pts/0 R 0:00 ps T
\end{verbatim} %$
-\normalsize
-e come si vede, dato che non si è fatto nulla per riceverne lo stato di
-terminazione, i tre processi figli sono ancora presenti pur essendosi
-conclusi, con lo stato di zombie e l'indicazione che sono stati terminati.
-
-La possibilità di avere degli zombie deve essere tenuta sempre presente quando
-si scrive un programma che deve essere mantenuto in esecuzione a lungo e
-creare molti figli. In questo caso si deve sempre avere cura di far leggere
-l'eventuale stato di uscita di tutti i figli (in genere questo si fa
+\normalsize e come si vede, dato che non si è fatto nulla per riceverne lo
+stato di terminazione, i tre processi figli sono ancora presenti pur essendosi
+conclusi, con lo stato di zombie\index{zombie} e l'indicazione che sono stati
+terminati.
+
+La possibilità di avere degli zombie\index{zombie} deve essere tenuta sempre
+presente quando si scrive un programma che deve essere mantenuto in esecuzione
+a lungo e creare molti figli. In questo caso si deve sempre avere cura di far
+leggere l'eventuale stato di uscita di tutti i figli (in genere questo si fa
attraverso un apposito \textit{signal handler}, che chiama la funzione
-\func{wait}, vedi \secref{sec:sig_xxx} e \secref{sec:proc_wait}). Questa
-operazione è necessaria perché anche se gli \textit{zombie} non consumano
-risorse di memoria o processore, occupano comunque una voce nella tabella dei
-processi, che a lungo andare potrebbe esaurirsi.
+\func{wait}, vedi \secref{sec:sig_sigchld} e \secref{sec:proc_wait}). Questa
+operazione è necessaria perché anche se gli \textit{zombie}\index{zombie} non
+consumano risorse di memoria o processore, occupano comunque una voce nella
+tabella dei processi, che a lungo andare potrebbe esaurirsi.
Si noti che quando un processo adottato da \cmd{init} termina, esso non
-diviene uno \textit{zombie}; questo perché una delle funzioni di \cmd{init} è
-appunto quella di chiamare la funzione \func{wait} per i processi cui fa da
-padre, completandone la terminazione. Questo è quanto avviene anche quando,
-come nel caso del precedente esempio con \cmd{forktest}, il padre termina con
-dei figli in stato di zombie: alla sua terminazione infatti tutti i suoi figli
-(compresi gli zombie) verranno adottati da \cmd{init}, il quale provvederà a
-completarne la terminazione.
+diviene uno \textit{zombie}\index{zombie}; questo perché una delle funzioni di
+\cmd{init} è appunto quella di chiamare la funzione \func{wait} per i processi
+cui fa da padre, completandone la terminazione. Questo è quanto avviene anche
+quando, come nel caso del precedente esempio con \cmd{forktest}, il padre
+termina con dei figli in stato di zombie\index{zombie}: alla sua terminazione
+infatti tutti i suoi figli (compresi gli zombie\index{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à 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.
+Si tenga presente infine che siccome gli zombie\index{zombie} sono processi
+già usciti, non 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}}
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
+evitare di riempire di \textit{zombie}\index{zombie} la tabella dei processi;
+le funzioni deputate a questo compito sono sostanzialmente due, \funcd{wait} e
\func{waitpid}. La prima, il cui prototipo è:
\begin{functions}
\headdecl{sys/types.h}
\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[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
\end{errlist}}
\end{functions}
\noindent
è 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.
-
-Al ritorno lo stato di terminazione 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
+immediatamente, se più di un figlio è terminato occorre chiamare la funzione
+più volte se si vuole recuperare lo stato di terminazione di tutti quanti.
+
+Al ritorno della funzione lo stato di terminazione del figlio viene salvato
+nella variabile puntata da \param{status} e tutte le risorse del kernel
+relative al processo (vedi \secref{sec:proc_termination}) vengono rilasciate.
+Nel caso un processo abbia più figli il valore di ritorno (il \acr{pid} del
+figlio) permette di identificare qual'è quello che è uscito.
+
+Questa funzione ha il difetto di essere poco flessibile, in quanto ritorna
+all'uscita di un qualunque processo figlio. Nelle occasioni in cui è
+necessario attendere la conclusione di un processo specifico occorrerebbe
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ù
-ampie, legate anche al controllo di sessione. Dato che è possibile ottenere
-lo stesso comportamento di \func{wait} si consiglia di utilizzare sempre
-questa funzione, il cui prototipo è:
+provvedere 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
+\funcd{waitpid} che effettua lo stesso servizio, ma dispone di una serie di
+funzionalità più ampie, legate anche al controllo di sessione (si veda
+\secref{sec:sess_job_control}). Dato che è possibile ottenere lo stesso
+comportamento di \func{wait} si consiglia di utilizzare sempre questa
+funzione, il cui prototipo è:
\begin{functions}
\headdecl{sys/types.h}
\headdecl{sys/wait.h}
Attende la conclusione di un processo figlio.
\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
+ è stata specificata l'opzione \const{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[\errcode{EINTR}] se non è stata specificata l'opzione \const{WNOHANG} e
la funzione è stata interrotta da un segnale.
- \item[\macro{ECHILD}] il processo specificato da \param{pid} non esiste o
+ \item[\errcode{ECHILD}] il processo specificato da \param{pid} non esiste o
non è figlio del processo chiamante.
\end{errlist}}
\end{functions}
Le differenze principali fra le due funzioni sono che \func{wait} si blocca
sempre fino a che un processo figlio non termina, mentre \func{waitpid} ha la
-possibilità si specificare un'opzione \macro{WNOHANG} che ne previene il
-blocco; inoltre \func{waitpid} può specificare quale processo attendere sulla
-base del valore fornito dall'argomento \param{pid}, secondo lo
-specchietto riportato in \ntab:
+possibilità si specificare un'opzione \const{WNOHANG} che ne previene il
+blocco; inoltre \func{waitpid} può specificare in maniera flessibile quale
+processo attendere, sulla base del valore fornito dall'argomento \param{pid},
+secondo lo specchietto riportato in \tabref{tab:proc_waidpid_pid}.
+
\begin{table}[!htb]
\centering
\footnotesize
- \begin{tabular}[c]{|c|p{10cm}|}
+ \begin{tabular}[c]{|c|c|p{8cm}|}
\hline
- \textbf{Valore} & \textbf{Significato}\\
+ \textbf{Valore} & \textbf{Opzione} &\textbf{Significato}\\
\hline
\hline
- $<-1$& attende per un figlio il cui \textit{process group} è uguale al
- valore assoluto di \var{pid}. \\
- $-1$ & attende per un figlio qualsiasi, usata in questa maniera è
- equivalente a \func{wait}.\\
- $0$ & attende per un figlio il cui \textit{process group} è uguale a
- quello del processo chiamante. \\
- $>0$ & attende per un figlio il cui \acr{pid} è uguale al
- valore di \var{pid}.\\
+ $<-1$& -- & attende per un figlio il cui \textit{process group} (vedi
+ \secref{sec:sess_proc_group}) è uguale al
+ valore assoluto di \param{pid}. \\
+ $-1$ & \const{WAIT\_ANY} & attende per un figlio qualsiasi, usata in
+ questa maniera è equivalente a \func{wait}.\\
+ $0$ & \const{WAIT\_MYPGRP} & attende per un figlio il cui \textit{process
+ group} è uguale a quello del processo chiamante. \\
+ $>0$ & -- &attende per un figlio il cui \acr{pid} è uguale al
+ valore di \param{pid}.\\
\hline
\end{tabular}
- \caption{Significato dei valori del parametro \var{pid} della funzione
+ \caption{Significato dei valori dell'argomento \param{pid} della funzione
\func{waitpid}.}
\label{tab:proc_waidpid_pid}
\end{table}
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.
+sono il già citato \const{WNOHANG}, che previene il blocco della funzione
+quando il processo figlio non è terminato, e \const{WUNTRACED} che permette di
+tracciare i processi bloccati. Il valore dell'opzione deve essere specificato
+come maschera binaria ottenuta con l'OR delle suddette costanti con zero.
+
+In genere si utilizza \const{WUNTRACED} all'interno del controllo di sessione,
+(l'argomento è trattato in \secref{sec:sess_job_control}). In tal caso infatti
+la funzione ritorna, restituendone il \acr{pid}, quando c'è un processo figlio
+che è entrato in stato di sleep (vedi \tabref{tab:proc_proc_states}) e del
+quale non si è ancora letto lo stato (con questa stessa opzione). In Linux
+sono previste altre opzioni non standard relative al comportamento con i
+thread, che riprenderemo in \secref{sec:thread_xxx}.
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 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
+segnale di \const{SIGCHLD} al padre. L'azione predefinita (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}). In questo
-caso infatti, dato che il segnale è generato dalla terminazione di un figlio,
-avremo la certezza che la chiamata a \func{wait} non si bloccherà.
+per leggerne lo stato di chiusura (ed evitare la presenza di
+\textit{zombie}\index{zombie}), per questo la modalità più usata per chiamare
+queste funzioni è quella di utilizzarle all'interno di un \textit{signal
+ handler} (vedremo un esempio di come gestire \const{SIGCHLD} con i segnali
+in \secref{sec:sig_example}). In questo 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]
\centering
\macro{WEXITSTATUS(s)} & Restituisce gli otto bit meno significativi dello
stato di uscita del processo (passato attraverso \func{\_exit}, \func{exit}
o come valore di ritorno di \func{main}). Può essere valutata solo se
- \macro{WIFEXITED} ha restituito un valore non nullo.\\
+ \val{WIFEXITED} ha restituito un valore non nullo.\\
\macro{WIFSIGNALED(s)} & Vera se il processo figlio è terminato
in maniera anomala a causa di un segnale che non è stato catturato (vedi
\secref{sec:sig_notification}).\\
\macro{WTERMSIG(s)} & restituisce il numero del segnale che ha causato
la terminazione anomala del processo. Può essere valutata solo se
- \macro{WIFSIGNALED} ha restituito un valore non nullo.\\
+ \val{WIFSIGNALED} ha restituito un valore non nullo.\\
\macro{WCOREDUMP(s)} & Vera se il processo terminato ha generato un
file si \textit{core dump}. Può essere valutata solo se
- \macro{WIFSIGNALED} ha restituito un valore non nullo\footnote{questa
- macro non è definita dallo standard POSIX.1, ma è presente come estensione
- sia in Linux che in altri unix}.\\
+ \val{WIFSIGNALED} ha restituito un valore non nullo.\footnotemark \\
\macro{WIFSTOPPED(s)} & Vera se il processo che ha causato il ritorno di
\func{waitpid} è bloccato. L'uso è possibile solo avendo specificato
- l'opzione \macro{WUNTRACED}. \\
+ l'opzione \const{WUNTRACED}. \\
\macro{WSTOPSIG(s)} & restituisce il numero del segnale che ha bloccato
- il processo, Può essere valutata solo se \macro{WIFSTOPPED} ha
+ il processo, Può essere valutata solo se \val{WIFSTOPPED} ha
restituito un valore non nullo. \\
\hline
\end{tabular}
\label{tab:proc_status_macro}
\end{table}
+\footnotetext{questa macro non è definita dallo standard POSIX.1, ma è
+ presente come estensione sia in Linux che in altri Unix.}
+
Entrambe le funzioni di attesa restituiscono lo stato di terminazione del
processo tramite il puntatore \param{status} (se non interessa memorizzare lo
stato si può passare un puntatore nullo). Il valore restituito da entrambe le
analizzare lo stato di uscita. Esse sono definite sempre in
\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}).
+\ctyp{int} puntata da \param{status}).
Si tenga conto che nel caso di conclusione anomala il valore restituito da
-\macro{WTERMSIG} può essere confrontato con le costanti definite in
+\val{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}.
\label{sec:proc_wait4}
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
-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:
+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. Le due funzioni sono \funcd{wait3} e \funcd{wait4}, che
+diventano accessibili definendo la macro \macro{\_USE\_BSD}; i loro prototipi
+sono:
\begin{functions}
- \headdecl{sys/times.h}
- \headdecl{sys/types.h}
- \headdecl{sys/wait.h}
- \headdecl{sys/resource.h}
+ \headdecl{sys/times.h} \headdecl{sys/types.h} \headdecl{sys/wait.h}
+ \headdecl{sys/resource.h}
+
\funcdecl{pid\_t wait4(pid\_t pid, int * status, int options, struct rusage
- * rusage)}
- È identica a \func{waitpid} sia per comportamento che per i
- valori dei parametri, ma restituisce in \param{rusage} un sommario delle
- risorse usate dal processo (per i dettagli vedi \secref{sec:sys_xxx})
+ * rusage)}
+ È 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.
\funcdecl{pid\_t wait3(int *status, int options, struct rusage *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} (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
-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}.
+la struttura \struct{rusage} è definita in \file{sys/resource.h}, e viene
+utilizzata anche dalla funzione \func{getrusage} (vedi
+\secref{sec:sys_resource_use}) per ottenere le risorse di sistema usate da un
+processo; la sua definizione è riportata in \figref{fig:sys_rusage_struct}.
\subsection{Le funzioni \func{exec}}
Ci sono sei diverse versioni di \func{exec} (per questo la si è chiamata
famiglia di funzioni) che possono essere usate per questo compito, in realtà
(come mostrato in \figref{fig:proc_exec_relat}), sono tutte un front-end a
-\func{execve}. Il prototipo di quest'ultima è:
+\funcd{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}.
- \bodydesc{La funzione ritorna -1 solo in caso di errore, nel qual caso
- caso la \var{errno} può assumere i valori:
+ \bodydesc{La funzione ritorna solo in caso di errore, restituendo -1; nel
+ qual caso \var{errno} può assumere i valori:
\begin{errlist}
- \item[\macro{EACCES}] il file non è eseguibile, oppure il filesystem è
- montato in \cmd{noexec}, oppure non è un file normale o un interprete.
- \item[\macro{EPERM}] il file ha i bit \acr{suid} o \acr{sgid}, l'utente non
- è root, e o il processo viene tracciato, o il filesystem è montato con
+ \item[\errcode{EACCES}] il file non è eseguibile, oppure il filesystem è
+ montato in \cmd{noexec}, oppure non è un file regolare o un interprete.
+ \item[\errcode{EPERM}] il file ha i bit \acr{suid} o \acr{sgid}, l'utente
+ non è root, il processo viene tracciato, o il filesystem è montato con
l'opzione \cmd{nosuid}.
- \item[\macro{ENOEXEC}] il file è in un formato non eseguibile o non
+ \item[\errcode{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[\errcode{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[\errcode{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
+ \item[\errcode{EINVAL}] L'eseguibile ELF ha più di un segmento
+ \const{PF\_INTERP}, cioè chiede di essere eseguito da più di un
interprete.
- \item[\macro{ELIBBAD}] Un interprete ELF non è in un formato
+ \item[\errcode{ELIBBAD}] Un interprete ELF non è in un formato
riconoscibile.
+ \item[\errcode{E2BIG}] La lista degli argomenti è troppo grande.
\end{errlist}
- ed inoltre anche \macro{EFAULT}, \macro{ENOMEM}, \macro{EIO},
- \macro{ENAMETOOLONG}, \macro{E2BIG}, \macro{ELOOP}, \macro{ENOTDIR},
- \macro{ENFILE}, \macro{EMFILE}.}
+ ed inoltre anche \errval{EFAULT}, \errval{ENOMEM}, \errval{EIO},
+ \errval{ENAMETOOLONG}, \errval{ELOOP}, \errval{ENOTDIR}, \errval{ENFILE},
+ \errval{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
+\param{filename}, passandogli la lista di argomenti indicata da \param{argv}
+e come ambiente la lista di stringhe indicata da \param{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
argomento. I parametri successivi consentono di specificare gli argomenti a
linea di comando e l'ambiente ricevuti dal nuovo processo.
-\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}.}
+\bodydesc{Queste funzioni ritornano solo in caso di errore, restituendo -1;
+ nel qual caso \var{errno} assumerà i valori visti in precedenza per
+ \func{execve}.}
\end{functions}
Per capire meglio le differenze fra le funzioni della famiglia si può fare
-riferimento allo specchietto riportato in \ntab. La prima differenza riguarda
-le modalità di passaggio dei parametri che poi andranno a costituire gli
-argomenti a linea di comando (cioè i valori di \var{argv} e \var{argc} visti
-dalla funzione \func{main} del programma chiamato).
+riferimento allo specchietto riportato in \tabref{tab:proc_exec_scheme}. La
+prima differenza riguarda le modalità di passaggio dei parametri che poi
+andranno a costituire gli argomenti a linea di comando (cioè i valori di
+\param{argv} e \param{argc} visti dalla funzione \func{main} del programma
+chiamato).
Queste modalità sono due e sono riassunte dagli mnemonici \code{v} e \code{l}
che stanno rispettivamente per \textit{vector} e \textit{list}. Nel primo caso
Nel secondo caso le stringhe degli argomenti sono passate alla funzione come
lista di puntatori, nella forma:
-\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
- char *arg0, char *arg1, ..., char *argn, NULL
-\end{lstlisting}
+\includecodesnip{listati/char_list.c}
che deve essere terminata da un puntatore nullo. In entrambi i casi vale la
convenzione che il primo argomento (\var{arg0} o \var{argv[0]}) viene usato
per indicare il nome del file che contiene il programma che verrà eseguito.
\multicolumn{1}{|c|}{\textbf{Caratteristiche}} &
\multicolumn{6}{|c|}{\textbf{Funzioni}} \\
\hline
- &\func{execl\ }&\func{execlp}&\func{execle}
- &\func{execv\ }& \func{execvp}& \func{execve} \\
+ &\func{execl}\texttt{ }&\func{execlp}&\func{execle}
+ &\func{execv}\texttt{ }& \func{execvp}& \func{execve} \\
\hline
\hline
argomenti a lista &$\bullet$&$\bullet$&$\bullet$&&& \\
La seconda differenza fra le funzioni riguarda le modalità con cui 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
+specificare il comando da eseguire; quando il parametro \param{file} non
+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
relativo a permessi di accesso insufficienti (cioè l'esecuzione della
-sottostante \func{execve} ritorna un \macro{EACCESS}), la ricerca viene
+sottostante \func{execve} ritorna un \errcode{EACCES}), 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}.
+\errcode{EACCES}.
Le altre quattro funzioni si limitano invece a cercare di eseguire il file
-indicato dal parametro \var{path}, che viene interpretato come il
+indicato dall'argomento \param{path}, che viene interpretato come il
\textit{pathname} del programma.
\begin{figure}[htb]
\centering
- \includegraphics[width=13cm]{img/exec_rel}
+ \includegraphics[width=16cm]{img/exec_rel}
\caption{La interrelazione fra le sei funzioni della famiglia \func{exec}.}
\label{fig:proc_exec_relat}
\end{figure}
La terza differenza è come viene passata la lista delle variabili di ambiente.
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
+a riga di comando (terminato quindi da un \val{NULL}), le altre usano il
valore della variabile \var{environ} (vedi \secref{sec:proc_environ}) del
processo di partenza per costruire l'ambiente.
\begin{itemize*}
\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_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}).
+\item l'\textsl{user-ID reale}, il \textit{group-ID reale} ed i
+ \textsl{group-ID supplementari} (vedi \secref{sec:proc_access_id}).
+\item il \textit{session id} (\acr{sid}) ed il \textit{process group-ID}
+ (\acr{pgid}), vedi \secref{sec:sess_proc_group}.
+\item il terminale di controllo (vedi \secref{sec:sess_ctrl_term}).
+\item il tempo restante ad un allarme (vedi \secref{sec:sig_alarm_abort}).
\item la directory radice e la directory di lavoro corrente (vedi
\secref{sec:file_work_dir}).
\item la maschera di creazione dei file (\var{umask}, vedi
\secref{sec:file_umask}) ed i \textit{lock} sui file (vedi
\secref{sec:file_locking}).
\item i segnali sospesi (\textit{pending}) e la maschera dei segnali (si veda
- \secref{sec:sig_sigpending}).
-\item i limiti sulle risorse (vedi \secref{sec:sys_limits}).
+ \secref{sec:sig_sigmask}).
+\item i limiti sulle risorse (vedi \secref{sec:sys_resource_limit}).
\item i valori delle variabili \var{tms\_utime}, \var{tms\_stime},
- \var{tms\_cutime}, \var{tms\_ustime} (vedi \secref{sec:xxx_xxx}).
+ \var{tms\_cutime}, \var{tms\_ustime} (vedi \secref{sec:sys_cpu_times}).
\end{itemize*}
-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}).
+Inoltre i segnali che sono stati impostati per essere ignorati nel processo
+chiamante mantengono la stessa impostazione pure nel nuovo programma, tutti
+gli altri segnali vengono impostati alla loro azione predefinita. Un caso
+speciale è il segnale \const{SIGCHLD} che, quando impostato a
+\const{SIG\_IGN}, può anche non essere reimpostato a \const{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
-descriptor. I file per cui è settato vengono chiusi, tutti gli altri file
-restano aperti. Questo significa che il comportamento di default è che i file
-restano aperti attraverso una \func{exec}, a meno di una chiamata esplicita a
-\func{fcntl} che setti il suddetto flag.
+\textit{close-on-exec}\index{close-on-exec} (vedi anche
+\secref{sec:file_fcntl}) per ciascun file descriptor. I file per cui è
+impostato vengono chiusi, tutti gli altri file restano aperti. Questo
+significa che il comportamento predefinito è che i file restano aperti
+attraverso una \func{exec}, a meno di una chiamata esplicita a \func{fcntl}
+che imposti il suddetto flag.
Per le directory, lo standard POSIX.1 richiede che esse vengano chiuse
attraverso una \func{exec}, in genere questo è fatto dalla funzione
-\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
-maniera trasparente all'utente.
+\func{opendir} (vedi \secref{sec:file_dir_read}) che effettua da sola
+l'impostazione del flag di \textit{close-on-exec}\index{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 l'\textsl{user-ID reale} ed il \textsl{group-ID reale}
restano gli stessi all'esecuzione di \func{exec}; lo stesso vale per
-l'\textit{effective user id} ed l'\textit{effective group id}, tranne quando
-il file che si va ad eseguire abbia o il \acr{suid} bit o lo \acr{sgid} bit
-settato, in questo caso l'\textit{effective user id} e l'\textit{effective
- group id} vengono settati rispettivamente all'utente o al gruppo cui il file
-appartiene (per i dettagli vedi \secref{sec:proc_perms}).
+l'\textsl{user-ID effettivo} ed il \textsl{group-ID effettivo} (il significato
+di questi identificatori è trattato in \secref{sec:proc_access_id}), tranne
+quando il file che si va ad eseguire abbia o il \acr{suid} bit o lo \acr{sgid}
+bit impostato, in questo caso l'\textsl{user-ID effettivo} ed il
+\textsl{group-ID effettivo} vengono impostati 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
condivise, viene lanciato il \textit{linker} dinamico \cmd{ld.so} prima del
programma per caricare le librerie necessarie ed effettuare il link
dell'eseguibile. Se il programma è in formato ELF per caricare le librerie
-dinamiche viene usato l'interprete indicato nel segmento \macro{PT\_INTERP},
+dinamiche viene usato l'interprete indicato nel segmento \const{PT\_INTERP},
in genere questo è \file{/lib/ld-linux.so.1} per programmi linkati con le
-\emph{libc5}, e \file{/lib/ld-linux.so.2} per programmi linkati con le
-\emph{glibc}. Infine nel caso il file sia uno script esso deve iniziare con
+\acr{libc5}, e \file{/lib/ld-linux.so.2} per programmi linkati con le
+\acr{glibc}. Infine nel caso il file sia uno script esso deve iniziare con
una linea nella forma \cmd{\#!/path/to/interpreter} dove l'interprete indicato
-deve esse un valido programma (binario, non un altro script) che verrà
-chiamato come se si fosse eseguito il comando \cmd{interpreter [arg]
+deve esse un programma valido (binario, non un altro script) che verrà
+chiamato come se si fosse eseguito il comando \cmd{interpreter [argomenti]
filename}.
Con la famiglia delle \func{exec} si chiude il novero delle funzioni su cui è
basata la gestione dei processi in Unix: con \func{fork} si crea un nuovo
-processo, con \func{exec} si avvia un nuovo programma, con \func{exit} e
-\func{wait} si effettua e verifica la conclusione dei programmi. Tutte le
-altre funzioni sono ausiliarie e servono la lettura e il settaggio dei vari
-parametri connessi ai processi.
+processo, con \func{exec} si lancia un nuovo programma, con \func{exit} e
+\func{wait} si effettua e verifica la conclusione dei processi. Tutte le
+altre funzioni sono ausiliarie e servono per la lettura e l'impostazione dei
+vari parametri connessi ai processi.
Come accennato in \secref{sec:intro_multiuser} il modello base\footnote{in
realtà già esistono estensioni di questo modello base, che lo rendono più
flessibile e controllabile, come le \textit{capabilities}, le ACL per i file
- o il \textit{Mandatory Access Control} di SELinux} di sicurezza di un
-sistema unix-like è fondato sui concetti di utente e gruppo, e sulla
+ o il \textit{Mandatory Access Control} di SELinux; inoltre basandosi sul
+ lavoro effettuato con SELinux, a partire dal kernel 2.5.x, è iniziato lo
+ sviluppo di una infrastruttura di sicurezza, il \textit{Linux Security
+ Modules}, o LSM, in grado di fornire diversi agganci a livello del kernel
+ per modularizzare tutti i possibili controlli di accesso.} di sicurezza di
+un sistema unix-like è fondato sui concetti di utente e gruppo, e sulla
separazione fra l'amministratore (\textsl{root}, detto spesso anche
\textit{superuser}) che non è sottoposto a restrizioni, ed il resto degli
utenti, per i quali invece vengono effettuati i vari controlli di accesso.
%notevole flessibilità,
Abbiamo già accennato come il sistema associ ad ogni utente e gruppo due
-identificatori univoci, lo \acr{uid} e il \acr{gid}; questi servono al kernel
-per identificare uno specifico utente o un gruppo di utenti, per poi poter
+identificatori univoci, lo user-ID ed il group-ID; questi servono al kernel per
+identificare uno specifico utente o un gruppo di utenti, per poi poter
controllare che essi siano autorizzati a compiere le operazioni richieste. Ad
esempio in \secref{sec:file_access_control} vedremo come ad ogni file vengano
associati un utente ed un gruppo (i suoi \textsl{proprietari}, indicati
Dato che tutte le operazioni del sistema vengono compiute dai processi, è
evidente che per poter implementare un controllo sulle operazioni occorre
anche poter identificare chi è che ha lanciato un certo programma, e pertanto
-anche a ciascun processo è associato un utente e a un gruppo.
+anche a ciascun processo dovrà essere associato ad un utente e ad un gruppo.
Un semplice controllo di una corrispondenza fra identificativi non garantisce
però sufficiente flessibilità per tutti quei casi in cui è necessario poter
disporre di privilegi diversi, o dover impersonare un altro utente per un
limitato insieme di operazioni. Per questo motivo in generale tutti gli Unix
prevedono che i processi abbiano almeno due gruppi di identificatori, chiamati
-rispettivamente \textit{real} ed \textit{effective}.
+rispettivamente \textit{real} ed \textit{effective} (cioè \textsl{reali} ed
+\textsl{effettivi}). Nel caso di Linux si aggiungono poi altri due gruppi, il
+\textit{saved} (\textsl{salvati}) ed il \textit{filesystem} (\textsl{di
+ filesystem}), secondo la situazione illustrata in \tabref{tab:proc_uid_gid}.
\begin{table}[htb]
\footnotesize
\centering
- \begin{tabular}[c]{|c|l|p{6.5cm}|}
+ \begin{tabular}[c]{|c|c|l|p{7.3cm}|}
\hline
- \textbf{Suffisso} & \textbf{Significato} & \textbf{Utilizzo} \\
+ \textbf{Suffisso} & \textbf{Gruppo} & \textbf{Denominazione}
+ & \textbf{Significato} \\
\hline
\hline
- \acr{uid} & \textit{real user id} & indica l'utente che ha lanciato
- il programma\\
- \acr{gid} & \textit{real group id} & indica il gruppo dell'utente
- che ha lanciato il programma \\
+ \acr{uid} & \textit{real} & \textsl{user-ID reale}
+ & indica l'utente che ha lanciato il programma\\
+ \acr{gid} & '' &\textsl{group-ID reale}
+ & indica il gruppo principale 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 \\
+ \acr{euid} & \textit{effective} &\textsl{user-ID effettivo}
+ & indica l'utente usato nel controllo di accesso \\
+ \acr{egid} & '' & \textsl{group-ID effettivo}
+ & indica il gruppo usato nel controllo di accesso \\
+ -- & -- & \textsl{group-ID supplementari}
+ & indicano gli ulteriori gruppi cui l'utente appartiene \\
\hline
- -- & \textit{saved user id} & copia dell'\acr{euid} iniziale\\
- -- & \textit{saved group id} & copia dell'\acr{egid} iniziale \\
+ -- & \textit{saved} & \textsl{user-ID salvato}
+ & è una copia dell'\acr{euid} iniziale\\
+ -- & '' & \textsl{group-ID salvato}
+ & è una 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
- per il filesystem \\
+ \acr{fsuid} & \textit{filesystem} &\textsl{user-ID di filesystem}
+ & indica l'utente effettivo per l'accesso al filesystem \\
+ \acr{fsgid} & '' & \textsl{group-ID di filesystem}
+ & indica il gruppo effettivo per l'accesso al filesystem \\
\hline
\end{tabular}
\caption{Identificatori di utente e gruppo associati a ciascun processo con
- indicazione dei suffissi usate dalle varie funzioni di manipolazione.}
+ indicazione dei suffissi usati dalle varie funzioni di manipolazione.}
\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
-cui si accede al sistema (e relativo gruppo di default). Servono per
-l'identificazione dell'utente e normalmente non vengono mai cambiati. In
-realtà vedremo (in \secref{sec:proc_setuid}) che è possibile modificarli, ma
-solo ad un processo che abbia i privilegi di amministratore; questa
-possibilità è usata ad esempio da \cmd{login} che, una volta completata la
-procedura di autenticazione, lancia una shell per la quale setta questi
-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
-\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}).
+Al primo gruppo appartengono l'\textsl{user-ID reale} ed il \textsl{group-ID
+ reale}: questi vengono impostati al login ai valori corrispondenti
+all'utente con cui si accede al sistema (e relativo gruppo principale).
+Servono per l'identificazione dell'utente e normalmente non vengono mai
+cambiati. In realtà vedremo (in \secref{sec:proc_setuid}) che è possibile
+modificarli, ma solo ad un processo che abbia i privilegi di amministratore;
+questa possibilità è usata proprio dal programma \cmd{login} che, una volta
+completata la procedura di autenticazione, lancia una shell per la quale
+imposta questi identificatori ai valori corrispondenti all'utente che entra
+nel sistema.
+
+Al secondo gruppo appartengono lo \textsl{user-ID effettivo} ed il
+\textsl{group-ID effettivo} (a cui si aggiungono gli eventuali \textsl{group-ID
+ supplementari} 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
\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
-all'utente e al gruppo proprietari del file. Questo consente, per programmi in
-cui ci sia necessità, di dare a qualunque utente normale privilegi o permessi
-di un'altro (o dell'amministratore).
-
-Come nel caso del \acr{pid} e del \acr{ppid} tutti questi identificatori
-possono essere letti dal processo attraverso delle opportune funzioni, i cui
-prototipi sono i seguenti:
+\acr{suid} o \acr{sgid} impostati (il significato di questi bit è affrontato
+in dettaglio in \secref{sec:file_suid_sgid}). In questo caso essi saranno
+impostati all'utente e al gruppo proprietari del file. Questo consente, per
+programmi in cui ci sia necessità, di dare a qualunque utente normale
+privilegi o permessi di un altro (o dell'amministratore).
+
+Come nel caso del \acr{pid} e del \acr{ppid}, anche tutti questi
+identificatori possono essere letti attraverso le rispettive funzioni:
+\funcd{getuid}, \funcd{geteuid}, \funcd{getgid} e \funcd{getegid}, i loro
+prototipi sono:
\begin{functions}
\headdecl{unistd.h}
\headdecl{sys/types.h}
- \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 getuid(void)} Restituisce l'\textsl{user-ID reale} del
processo corrente.
- \funcdecl{gid\_t getgid(void)} Restituisce il \textit{real group id} del
+ \funcdecl{uid\_t geteuid(void)} Restituisce l'\textsl{user-ID effettivo} del
processo corrente.
- \funcdecl{gid\_t getegid(void)} Restituisce l'\textit{effective group id} del
+ \funcdecl{gid\_t getgid(void)} Restituisce il \textsl{group-ID reale} del
processo corrente.
+ \funcdecl{gid\_t getegid(void)} Restituisce il \textsl{group-ID effettivo}
+ del processo corrente.
+
\bodydesc{Queste funzioni non riportano condizioni di errore.}
\end{functions}
servano di nuovo.
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
- cuore la portabilità del programma su altri Unix è buona norma controllare
- sempre la disponibilità di queste funzioni controllando se questa costante è
+\textit{saved} ed il \textit{filesystem}. 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 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
migliorare la sicurezza con NFS.
-Il \textit{saved user id} e il \textit{saved group id} sono copie
-dell'\textit{effective user id} e dell'\textit{effective group id} del
-processo padre, e vengono settati dalla funzione \func{exec} all'avvio del
-processo, come copie dell'\textit{effective user id} e dell'\textit{effective
- group id} dopo che questo sono stati settati tenendo conto di eventuali
-\acr{suid} o \acr{sgid}. Essi quindi consentono di tenere traccia di quale
-fossero utente e gruppo effettivi all'inizio dell'esecuzione di un nuovo
-programma.
-
-Il \textit{filesystem user id} e il \textit{filesystem group id} sono una
-estensione introdotta in Linux per rendere più sicuro l'uso di NFS (torneremo
-sull'argomento in \secref{sec:proc_setfsuid}). Essi sono una replica dei
-corrispondenti \textit{effective id}, ai quali si sostituiscono per tutte le
-operazioni di verifica dei permessi relativi ai file (trattate in
-\secref{sec:file_perm_overview}). Ogni cambiamento effettuato sugli
-\textit{effective id} viene automaticamente riportato su di essi, per cui in
-condizioni normali se ne può tranquillamente ignorare l'esistenza, in quanto
-saranno del tutto equivalenti ai precedenti.
-
-Uno specchietto riassuntivo, contenente l'elenco completo degli identificatori
-di utente e gruppo associati dal kernel ad ogni processo, è riportato in
-\tabref{tab:proc_uid_gid}.
+L'\textsl{user-ID salvato} ed il \textsl{group-ID salvato} sono copie
+dell'\textsl{user-ID effettivo} e del \textsl{group-ID effettivo} del processo
+padre, e vengono impostati dalla funzione \func{exec} all'avvio del processo,
+come copie dell'\textsl{user-ID effettivo} e del \textsl{group-ID effettivo}
+dopo che questi sono stati impostati tenendo conto di eventuali \acr{suid} o
+\acr{sgid}. Essi quindi consentono di tenere traccia di quale fossero utente
+e gruppo effettivi all'inizio dell'esecuzione di un nuovo programma.
+
+L'\textsl{user-ID di filesystem} e il \textsl{group-ID di filesystem} sono
+un'estensione introdotta in Linux per rendere più sicuro l'uso di NFS
+(torneremo sull'argomento in \secref{sec:proc_setfsuid}). Essi sono una
+replica dei corrispondenti identificatori del gruppo \textit{effective}, ai
+quali si sostituiscono per tutte le operazioni di verifica dei permessi
+relativi ai file (trattate in \secref{sec:file_perm_overview}). Ogni
+cambiamento effettuato sugli identificatori effettivi viene automaticamente
+riportato su di essi, per cui in condizioni normali si può tranquillamente
+ignorarne l'esistenza, in quanto saranno del tutto equivalenti ai precedenti.
\subsection{Le funzioni \func{setuid} e \func{setgid}}
\label{sec:proc_setuid}
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_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:
+di appartenenza) ad un processo sono rispettivamente \funcd{setuid} e
+\funcd{setgid}; come accennato in \secref{sec:proc_access_id} in Linux esse
+seguono la semantica POSIX che prevede l'esistenza dell'\textit{user-ID
+ salvato} e del \textit{group-ID salvato}; i loro prototipi sono:
\begin{functions}
\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)} Imposta l'\textsl{user-ID} del processo
corrente.
-\funcdecl{int setgid(gid\_t gid)} Setta il \textit{group id} del processo
+\funcdecl{int setgid(gid\_t gid)} Imposta il \textsl{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}.}
+ di fallimento: l'unico errore possibile è \errval{EPERM}.}
\end{functions}
Il funzionamento di queste due funzioni è analogo, per cui considereremo solo
la prima; la seconda si comporta esattamente allo stesso modo facendo
-riferimento al \textit{group id} invece che all'\textit{user id}. Gli
-eventuali \textit{supplementary group id} non vengono modificati.
-
+riferimento al \textsl{group-ID} invece che all'\textsl{user-ID}. Gli
+eventuali \textsl{group-ID supplementari} non vengono modificati.
L'effetto della chiamata è diverso a seconda dei privilegi del processo; se
-l'\textit{effective user id} è zero (cioè è quello dell'amministratore di
-sistema) allora tutti gli identificatori (\textit{real}, \textit{effective}
-e \textit{saved}) vengono settati al valore specificato da \var{uid},
-altrimenti viene settato solo l'\textit{effective user id}, e soltanto se il
-valore specificato corrisponde o al \textit{real user id} o al \textit{saved
- user id}. Negli altri casi viene segnalato un errore (con \macro{EPERM}).
+l'\textsl{user-ID effettivo} è zero (cioè è quello dell'amministratore di
+sistema) allora tutti gli identificatori (\textit{real}, \textit{effective} e
+\textit{saved}) vengono impostati al valore specificato da \param{uid},
+altrimenti viene impostato solo l'\textsl{user-ID effettivo}, e soltanto se il
+valore specificato corrisponde o all'\textsl{user-ID reale} o
+all'\textsl{user-ID salvato}. Negli altri casi viene segnalato un errore (con
+\errcode{EPERM}).
Come accennato l'uso principale di queste funzioni è quello di poter
-consentire ad un programma con i bit \acr{suid} o \acr{sgid} settati di
-riportare l'\textit{effective user id} a quello dell'utente che ha lanciato il
-programma, effettuare il lavoro che non necessita di privilegi aggiuntivi, ed
-eventualmente tornare indietro.
+consentire ad un programma con i bit \acr{suid} o \acr{sgid} impostati (vedi
+\secref{sec:file_suid_sgid}) di riportare l'\textsl{user-ID effettivo} a quello
+dell'utente che ha lanciato il programma, effettuare il lavoro che non
+necessita di privilegi aggiuntivi, ed eventualmente tornare indietro.
Come esempio per chiarire l'uso di queste funzioni prendiamo quello con cui
viene gestito l'accesso al file \file{/var/log/utmp}. In questo file viene
un gruppo dedicato (\acr{utmp}) ed i programmi che devono accedervi (ad
esempio tutti i programmi di terminale in X, o il programma \cmd{screen} che
crea terminali multipli su una console) appartengono a questo gruppo ed hanno
-il bit \acr{sgid} settato.
+il bit \acr{sgid} impostato.
Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato, la
situazione degli identificatori è la seguente:
\begin{eqnarray*}
\label{eq:1}
- \textit{real group id} &=& \textrm{\acr{gid} (del chiamante)} \\
- \textit{effective group id} &=& \textrm{\acr{utmp}} \\
- \textit{saved group id} &=& \textrm{\acr{utmp}}
+ \textsl{group-ID reale} &=& \textrm{\acr{gid} (del chiamante)} \\
+ \textsl{group-ID effettivo} &=& \textrm{\acr{utmp}} \\
+ \textsl{group-ID salvato} &=& \textrm{\acr{utmp}}
\end{eqnarray*}
-in questo modo, dato che l'\textit{effective group id} è quello giusto, il
+in questo modo, dato che il \textsl{group-ID effettivo} è quello giusto, il
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à
-possibile lanciare dal terminale programmi che modificano detto file, in tal
-caso infatti la situazione degli identificatori sarebbe:
+questo punto il programma può eseguire una \code{setgid(getgid())} per
+impostare il \textsl{group-ID effettivo} a quello dell'utente (e dato che il
+\textsl{group-ID reale} corrisponde la funzione avrà successo), in questo modo
+non sarà possibile lanciare dal terminale programmi che modificano detto file,
+in tal caso infatti la situazione degli identificatori sarebbe:
\begin{eqnarray*}
\label{eq:2}
- \textit{real group id} &=& \textrm{\acr{gid} (invariato)} \\
- \textit{effective group id} &=& \textrm{\acr{gid}} \\
- \textit{saved group id} &=& \textrm{\acr{utmp} (invariato)}
+ \textsl{group-ID reale} &=& \textrm{\acr{gid} (invariato)} \\
+ \textsl{group-ID effettivo} &=& \textrm{\acr{gid}} \\
+ \textsl{group-ID salvato} &=& \textrm{\acr{utmp} (invariato)}
\end{eqnarray*}
e ogni processo lanciato dal terminale avrebbe comunque \acr{gid} come
-\textit{effective group id}. All'uscita dal terminale, per poter di nuovo
+\textsl{group-ID effettivo}. 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 precedente \func{getegid}), dato che
-in questo caso il valore richiesto corrisponde al \textit{saved group id} la
+in questo caso il valore richiesto corrisponde al \textsl{group-ID salvato} la
funzione avrà successo e riporterà la situazione a:
\begin{eqnarray*}
\label{eq:3}
- \textit{real group id} &=& \textrm{\acr{gid} (invariato)} \\
- \textit{effective group id} &=& \textrm{\acr{utmp}} \\
- \textit{saved group id} &=& \textrm{\acr{utmp} (invariato)}
+ \textsl{group-ID reale} &=& \textrm{\acr{gid} (invariato)} \\
+ \textsl{group-ID effettivo} &=& \textrm{\acr{utmp}} \\
+ \textsl{group-ID salvato} &=& \textrm{\acr{utmp} (invariato)}
\end{eqnarray*}
consentendo l'accesso a \file{/var/log/utmp}.
Occorre però tenere conto che tutto questo non è possibile con un processo con
-i privilegi di root, in tal caso infatti l'esecuzione una \func{setuid}
-comporta il cambiamento di tutti gli identificatori associati al processo,
-rendendo impossibile riguadagnare i privilegi di amministratore. Questo
-comportamento è corretto per l'uso che ne fa \cmd{login} una volta che crea
-una nuova shell per l'utente; ma quando si vuole cambiare soltanto
-l'\textit{effective user id} del processo per cedere i privilegi occorre
+i privilegi di amministratore, in tal caso infatti l'esecuzione di una
+\func{setuid} comporta il cambiamento di tutti gli identificatori associati al
+processo, rendendo impossibile riguadagnare i privilegi di amministratore.
+Questo comportamento è corretto per l'uso che ne fa \cmd{login} una volta che
+crea una nuova shell per l'utente; ma quando si vuole cambiare soltanto
+l'\textsl{user-ID effettivo} del processo per cedere i privilegi occorre
ricorrere ad altre funzioni (si veda ad esempio \secref{sec:proc_seteuid}).
-\subsection{Le funzioni \func{setreuid} e \func{setresuid}}
+\subsection{Le funzioni \func{setreuid} e \func{setregid}}
\label{sec:proc_setreuid}
-Queste due funzioni derivano da BSD che, non supportando\footnote{almeno fino
- alla versione 4.3+BSD TODO, FIXME verificare e aggiornare la nota.} i
-\textit{saved id}, le usava per poter scambiare fra di loro \textit{effective}
-e \textit{real id}. I loro prototipi sono:
+Le due funzioni \funcd{setreuid} e \funcd{setregid} derivano da BSD che, non
+supportando\footnote{almeno fino alla versione 4.3+BSD TODO, FIXME verificare
+ e aggiornare la nota.} gli identificatori del gruppo \textit{saved}, le usa
+per poter scambiare fra di loro \textit{effective} e \textit{real}. I
+rispettivi prototipi sono:
\begin{functions}
\headdecl{unistd.h}
\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
-specificati da \var{ruid} e \var{euid}.
+\funcdecl{int setreuid(uid\_t ruid, uid\_t euid)} Imposta l'\textsl{user-ID
+ reale} e l'\textsl{user-ID effettivo} del processo corrente ai valori
+specificati da \param{ruid} e \param{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
-specificati da \var{rgid} e \var{egid}.
+\funcdecl{int setregid(gid\_t rgid, gid\_t egid)} Imposta il \textsl{group-ID
+ reale} ed il \textsl{group-ID effettivo} del processo corrente ai valori
+specificati da \param{rgid} e \param{egid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
- di fallimento: l'unico errore possibile è \macro{EPERM}.}
+ di fallimento: l'unico errore possibile è \errval{EPERM}.}
\end{functions}
-I processi non privilegiati possono settare i \textit{real id} soltanto ai
-valori dei loro \textit{effective id} o \textit{real id} e gli
-\textit{effective id} ai valori dei loro \textit{real id}, \textit{effective
- id} o \textit{saved id}; valori diversi comportano il fallimento della
-chiamata; l'amministratore invece può specificare un valore qualunque.
-Specificando un valore di -1 l'identificatore corrispondente viene lasciato
-inalterato.
+La due funzioni sono analoghe ed il loro comportamento è identico; quanto
+detto per la prima prima riguardo l'user-ID, si applica immediatamente alla
+seconda per il group-ID. I processi non privilegiati possono impostare solo i
+valori del loro user-ID effettivo o reale; valori diversi comportano il
+fallimento della chiamata; l'amministratore invece può specificare un valore
+qualunque. Specificando un argomento di valore -1 l'identificatore
+corrispondente verrà lasciato inalterato.
-Con queste funzione si possono scambiare fra loro \textit{real id} e
-\textit{effective id}, e pertanto è possibile implementare un comportamento
-simile a quello visto in precedenza per \func{setgid}, cedendo i privilegi con
-un primo scambio, e recuperandoli, eseguito il lavoro non privilegiato, con un
-secondo scambio.
+Con queste funzioni si possono scambiare fra loro gli user-ID reale e
+effettivo, e pertanto è possibile implementare un comportamento simile a
+quello visto in precedenza per \func{setgid}, cedendo i privilegi con un primo
+scambio, e recuperandoli, eseguito il lavoro non privilegiato, con un secondo
+scambio.
In questo caso però occorre porre molta attenzione quando si creano nuovi
processi nella fase intermedia in cui si sono scambiati gli identificatori, in
-questo caso infatti essi avranno un \textit{real id} privilegiato, che dovrà
+questo caso infatti essi avranno un user-ID reale privilegiato, che dovrà
essere esplicitamente eliminato prima di porre in esecuzione un nuovo
-programma (occorrerà cioè eseguire un'altra chiamata dopo la \func{fork}, e
-prima della \func{exec} per uniformare i \textit{real id} agli
-\textit{effective id}) in caso contrario quest'ultimo potrebbe a sua volta
-effettuare uno scambio e riottenere privilegi non previsti.
+programma (occorrerà cioè eseguire un'altra chiamata dopo la \func{fork} e
+prima della \func{exec} per uniformare l'user-ID reale a quello effettivo) in
+caso contrario il nuovo programma potrebbe a sua volta effettuare uno scambio
+e riottenere privilegi non previsti.
Lo stesso problema di propagazione dei privilegi ad eventuali processi figli
-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 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}.
-
+si pone per l'user-ID salvato: questa funzione deriva da un'implementazione che
+non ne prevede la presenza, e quindi non è possibile usarla per correggere la
+situazione come nel caso precedente. Per questo motivo in Linux tutte le volte
+che si imposta un qualunque valore diverso da quello dall'user-ID reale
+corrente, l'user-ID salvato viene automaticamente uniformato al valore
+dell'user-ID effettivo.
\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:
+Le due funzioni \funcd{seteuid} e \funcd{setegid} sono un'estensione allo
+standard POSIX.1 (ma sono comunque supportate dalla maggior parte degli Unix)
+e vengono usate per cambiare gli identificatori del gruppo \textit{effective};
+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 seteuid(uid\_t uid)} Imposta l'user-ID effettivo del processo
+corrente a \param{uid}.
-\funcdecl{int setegid(gid\_t gid)} Setta l'\textit{effective group id} del
-processo corrente a \var{gid}.
+\funcdecl{int setegid(gid\_t gid)} Imposta il group-ID effettivo del processo
+corrente a \param{gid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
- di fallimento: l'unico errore possibile è \macro{EPERM}.}
+ di fallimento: l'unico errore è \errval{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.
+Come per le precedenti le due funzioni sono identiche, per cui tratteremo solo
+la prima. Gli utenti normali possono impostare l'user-ID effettivo solo al
+valore dell'user-ID reale o dell'user-ID salvato, l'amministratore può
+specificare qualunque valore. Queste funzioni sono usate per permettere
+all'amministratore di impostare solo l'user-ID effettivo, dato che l'uso
+normale di \func{setuid} comporta l'impostazione di tutti gli identificatori.
\subsection{Le funzioni \func{setresuid} e \func{setresgid}}
\label{sec:proc_setresuid}
-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:
+Le due funzioni \funcd{setresuid} e \funcd{setresgid} sono un'estensione
+introdotta in Linux,\footnote{a partire dal kernel 2.1.44.} e permettono un
+completo controllo su tutti e tre i gruppi di identificatori (\textit{real},
+\textit{effective} e \textit{saved}), i loro prototipi sono:
\begin{functions}
\headdecl{unistd.h}
\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
-\var{ruid}, \var{euid} e \var{suid}.
+\funcdecl{int setresuid(uid\_t ruid, uid\_t euid, uid\_t suid)} Imposta
+l'user-ID reale, l'user-ID effettivo e l'user-ID salvato del processo corrente
+ai valori specificati rispettivamente da \param{ruid}, \param{euid} e
+\param{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
-\var{rgid}, \var{egid} e \var{sgid}.
+\funcdecl{int setresgid(gid\_t rgid, gid\_t egid, gid\_t sgid)} Imposta il
+group-ID reale, il group-ID effettivo ed il group-ID salvato del processo
+corrente ai valori specificati rispettivamente da \param{rgid}, \param{egid} e
+\param{sgid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
- di fallimento: l'unico errore possibile è \macro{EPERM}.}
+ di fallimento: l'unico errore è \errval{EPERM}.}
\end{functions}
-I processi non privilegiati possono cambiare uno qualunque degli
-identificatori usando uno qualunque dei valori correnti di \textit{real id},
-\textit{effective id} o \textit{saved id}, l'amministratore può specificare i
-valori che vuole; un valore di -1 per un qualunque parametro lascia inalterato
-l'identificatore corrispondente.
+Le due funzioni sono identiche, quanto detto per la prima riguardo gli user-ID
+si applica alla seconda per i group-ID. I processi non privilegiati possono
+cambiare uno qualunque degli user-ID solo ad un valore corrispondente o
+all'user-ID reale, o a quello effettivo o a quello salvato, l'amministratore
+può specificare i valori che vuole; un valore di -1 per un qualunque parametro
+lascia inalterato l'identificatore corrispondente.
Per queste funzioni esistono anche due controparti che permettono di leggere
-in blocco i vari identificatori: \func{getresuid} e \func{getresgid}; i loro
+in blocco i vari identificatori: \funcd{getresuid} e \funcd{getresgid}; i loro
prototipi sono:
\begin{functions}
\headdecl{unistd.h}
\headdecl{sys/types.h}
-\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 getresuid(uid\_t *ruid, uid\_t *euid, uid\_t *suid)} Legge
+l'user-ID reale, l'user-ID effettivo e l'user-ID salvato 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.
+group-ID reale, il group-ID effettivo e il group-ID salvato del processo
+corrente.
\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
+ fallimento: l'unico errore possibile è \errval{EFAULT} se gli indirizzi delle
variabili di ritorno non sono validi.}
\end{functions}
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
-\textit{saved id}.
+specificati come puntatori (è un altro esempio di \textit{value result
+ argument}). Si noti che queste funzioni sono le uniche in grado di leggere
+gli identificatori del gruppo \textit{saved}.
\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_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}.
-
-C'è un solo caso in cui si ha necessità di introdurre una differenza fra
-\textit{effective id} e \textit{filesystem id}, ed è per ovviare ad un
-problema di sicurezza che si presenta quando si deve implementare un server
-NFS. Il server NFS infatti deve poter cambiare l'identificatore con cui accede
-ai file per assumere l'identità del singolo utente remoto, ma se questo viene
-fatto cambiando l'\textit{effective id} o il \textit{real id} il server si
-espone alla ricezione di eventuali segnali ostili da parte dell'utente di cui
-ha temporaneamente assunto l'identità. Cambiando solo il \textit{filesystem
- id} si ottengono i privilegi necessari per accedere ai file, mantenendo
-quelli originari per quanto riguarda tutti gli altri controlli di accesso,
-così che l'utente non possa inviare segnali al server NFS.
-
-Le due funzioni usate per cambiare questi identificatori sono \func{setfsuid}
-e \func{setfsgid}, ovviamente sono specifiche di Linux e non devono essere
+Queste funzioni servono per impostare gli identificatori del gruppo
+\textit{filesystem} che sono usati da Linux per il controllo dell'accesso ai
+file. Come già accennato in \secref{sec:proc_access_id} Linux definisce
+questo ulteriore gruppo di identificatori, che in circostanze normali sono
+assolutamente equivalenti a quelli del gruppo \textit{effective}, dato che
+ogni cambiamento di questi ultimi viene immediatamente riportato su di essi.
+
+C'è un solo caso in cui si ha necessità di introdurre una differenza fra gli
+identificatori dei gruppi \textit{effective} e \textit{filesystem}, ed è per
+ovviare ad un problema di sicurezza che si presenta quando si deve
+implementare un server NFS.
+
+Il server NFS infatti deve poter cambiare l'identificatore con cui accede ai
+file per assumere l'identità del singolo utente remoto, ma se questo viene
+fatto cambiando l'user-ID effettivo o l'user-ID reale il server si espone alla
+ricezione di eventuali segnali ostili da parte dell'utente di cui ha
+temporaneamente assunto l'identità. Cambiando solo l'user-ID di filesystem si
+ottengono i privilegi necessari per accedere ai file, mantenendo quelli
+originari per quanto riguarda tutti gli altri controlli di accesso, così che
+l'utente non possa inviare segnali al server NFS.
+
+Le due funzioni usate per cambiare questi identificatori sono \funcd{setfsuid}
+e \funcd{setfsgid}, ovviamente sono specifiche di Linux e non devono essere
usate se si intendono scrivere programmi portabili; i loro prototipi sono:
\begin{functions}
\headdecl{sys/fsuid.h}
-\funcdecl{int setfsuid(uid\_t fsuid)} Setta il \textit{filesystem user id} del
-processo corrente a \var{fsuid}.
+\funcdecl{int setfsuid(uid\_t fsuid)} Imposta l'user-ID di filesystem del
+processo corrente a \param{fsuid}.
-\funcdecl{int setfsgid(gid\_t fsgid)} Setta l'\textit{filesystem group id} del
-processo corrente a \var{fsgid}.
+\funcdecl{int setfsgid(gid\_t fsgid)} Imposta il group-ID di filesystem del
+processo corrente a \param{fsgid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
- di fallimento: l'unico errore possibile è \macro{EPERM}.}
+ di fallimento: l'unico errore possibile è \errval{EPERM}.}
\end{functions}
\noindent queste funzioni hanno successo solo se il processo chiamante ha i
privilegi di amministratore o, per gli altri utenti, se il valore specificato
-coincide con uno dei \textit{real}, \textit{effective} o \textit{saved id}.
+coincide con uno dei di quelli del gruppo \textit{real}, \textit{effective} o
+\textit{saved}.
\subsection{Le funzioni \func{setgroups} e \func{getgroups}}
\label{sec:proc_setgroups}
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 è:
+gruppi supplementari cui un utente può appartenere. Ogni processo può avere
+almeno \const{NGROUPS\_MAX} gruppi supplementari\footnote{il numero massimo di
+ gruppi secondari può essere ottenuto con \func{sysconf} (vedi
+ \secref{sec:sys_sysconf}), leggendo il parametro
+ \texttt{\_SC\_NGROUPS\_MAX}.} 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 associati ad un
+processo è \funcd{getgroups}; questa funzione è definita nello standard
+POSIX.1, 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}.
+ \funcdecl{int getgroups(int size, gid\_t list[])}
+
+ Legge gli identificatori dei gruppi supplementari.
\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:
+ successo e -1 in caso di fallimento, nel qual caso \var{errno} assumerà
+ i valori:
\begin{errlist}
- \item[\macro{EFAULT}] \param{list} non ha un indirizzo valido.
- \item[\macro{EINVAL}] il valore di \param{size} è diverso da zero ma
+ \item[\errcode{EFAULT}] \param{list} non ha un indirizzo valido.
+ \item[\errcode{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 è:
+La funzione legge gli identificatori dei gruppi supplementari del processo sul
+vettore \param{list} di dimensione \param{size}. Non è specificato se la
+funzione inserisca o meno nella lista il group-ID effettivo 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, \funcd{getgrouplist}, può invece essere usata per
+ottenere tutti i gruppi a cui appartiene un certo 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}.
+ int *ngroups)} Legge i gruppi supplementari.
\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_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, passando indietro il numero dei gruppi trovati.
-Per settare i gruppi supplementari di un processo ci sono due funzioni, che
+La funzione legge i gruppi supplementari dell'utente specificato da
+\param{user}, eseguendo una scansione del database dei gruppi (si veda
+\secref{sec:sys_user_group}). Ritorna poi 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, passando indietro il numero dei gruppi trovati.
+
+Per impostare 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 è:
+delle due è \funcd{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}.
+ \funcdecl{int setgroups(size\_t size, gid\_t *list)}
+
+ Imposta i gruppi supplementari del processo.
\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
- fallimento, nel qual caso \var{errno} viene settata a:
+ fallimento, nel qual caso \var{errno} assumerà i valori:
\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).
+ \item[\errcode{EFAULT}] \param{list} non ha un indirizzo valido.
+ \item[\errcode{EPERM}] il processo non ha i privilegi di amministratore.
+ \item[\errcode{EINVAL}] il valore di \param{size} è maggiore del valore
+ massimo consentito.
\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 è:
+La funzione imposta i gruppi supplementari del processo corrente ai valori
+specificati nel vettore passato con l'argomento \param{list}, di dimensioni
+date dall'argomento \param{size}. Il numero massimo di gruppi supplementari è
+un parametro di sistema, che può essere ricavato con le modalità spiegate in
+\secref{sec:sys_characteristics}.
+
+Se invece si vogliono impostare i gruppi supplementari del processo a quelli di
+un utente specifico, si può usare \funcd{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}.
+ \funcdecl{int initgroups(const char *user, gid\_t group)}
+
+ Inizializza la lista dei gruppi supplementari.
\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.}
+ fallimento, nel qual caso \var{errno} assumerà gli stessi valori di
+ \func{setgroups} più \errval{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} 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
-quando si definisce \macro{\_POSIX\_SOURCE} o si compila con il flag
-\cmd{-ansi}.
+\file{/etc/groups}) cercando i gruppi di cui è membro l'utente \param{user}
+con cui costruisce una lista di gruppi supplementari, a cui aggiunge anche
+\param{group}, infine imposta questa lista per il processo corrente 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}, è pertanto meglio evitarle se si vuole
+scrivere codice portabile.
\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}\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 prenderemo in esame i vari meccanismi
-con cui viene gestita l'assegnazione del tempo di CPU, ed illustreremo le
-varie funzioni di gestione.
+lo \textit{scheduler}\index{scheduler} assegna la CPU ai vari processi attivi.
+In particolare prenderemo in esame i vari meccanismi con cui viene gestita
+l'assegnazione del tempo di CPU, ed illustreremo le varie funzioni di
+gestione.
\subsection{I meccanismi di \textit{scheduling}}
La scelta di un meccanismo che sia in grado di distribuire in maniera efficace
il tempo di CPU per l'esecuzione dei processi è sempre una questione delicata,
-ed oggetto di numerose ricerche; in ogni caso essa dipende in maniera
-essenziale anche dal tipo di utilizzo che deve essere fatto del sistema.
+ed oggetto di numerose ricerche; in generale essa dipende in maniera
+essenziale anche dal tipo di utilizzo che deve essere fatto del sistema, per
+cui non esiste un meccanismo che sia valido per tutti gli usi.
+
+La caratteristica specifica di un sistema multitasking come Linux è quella del
+cosiddetto \textit{prehemptive multitasking}: questo significa che al
+contrario di altri sistemi (che usano invece il cosiddetto \textit{cooperative
+ multitasking}) non sono i singoli processi, ma il kernel stesso a decidere
+quando la CPU deve essere passata ad un altro processo. Come accennato in
+\secref{sec:proc_hierarchy} questa scelta viene eseguita da una sezione
+apposita del kernel, lo \textit{scheduler}\index{scheduler}, il cui scopo è
+quello di distribuire al meglio il tempo di CPU fra i vari processi.
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 determinare 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 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
-necessarie per l'esecuzione di un programma, e spesso non è neanche la più
-importante. Per questo non è affatto detto che dare ad un programma la massima
-priorità di esecuzione abbia risultati significativi in termini di
-prestazioni.
+multi-processore si deve anche scegliere 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 effettuare la migliore scelta fra le diverse CPU non è
+ banale.} Tutto questo comunque appartiene alle sottigliezze
+dell'implementazione del kernel; dal punto di vista dei programmi che girano
+in user space, anche quando si hanno più processori (e dei processi che sono
+eseguiti davvero in contemporanea), le politiche di scheduling riguardano
+semplicemente l'allocazione della risorsa \textsl{tempo di esecuzione}, la cui
+assegnazione sarà governata dai meccanismi di scelta delle priorità che
+restano gli stessi indipendentemente dal numero di processori.
+
+Si tenga conto poi che i processi non devono solo eseguire del codice: ad
+esempio molto spesso saranno impegnati in operazioni di I/O, o potranno
+venire bloccati da un comando dal terminale, o sospesi per un certo periodo di
+tempo. In tutti questi casi la CPU diventa disponibile ed è compito dello
+kernel provvedere a mettere in esecuzione un altro processo.
+
+Tutte queste possibilità sono caratterizzate da un diverso \textsl{stato} del
+processo, in Linux un processo può trovarsi in uno degli stati riportati in
+\tabref{tab:proc_proc_states}; ma soltanto i processi che sono nello stato
+\textit{runnable} concorrono per l'esecuzione. Questo vuol dire che, qualunque
+sia la sua priorità, un processo non potrà mai essere messo in esecuzione
+fintanto che esso si trova in uno qualunque degli altri stati.
+
+\begin{table}[htb]
+ \footnotesize
+ \centering
+ \begin{tabular}[c]{|p{2.8cm}|c|p{10cm}|}
+ \hline
+ \textbf{Stato} & \texttt{STAT} & \textbf{Descrizione} \\
+ \hline
+ \hline
+ \textbf{Runnable}& \texttt{R} & Il processo è in esecuzione o è pronto ad
+ essere eseguito (cioè è in attesa che gli
+ venga assegnata la CPU). \\
+ \textbf{Sleep} & \texttt{S} & Il processo è in attesa di un
+ risposta dal sistema, ma può essere
+ interrotto da un segnale. \\
+ \textbf{Uninterrutible Sleep}& \texttt{D} & Il processo è in
+ attesa di un risposta dal sistema (in
+ genere per I/O), e non può essere
+ interrotto in nessuna circostanza. \\
+ \textbf{Stopped} & \texttt{T} & Il processo è stato fermato con un
+ \const{SIGSTOP}, o è tracciato.\\
+ \textbf{Zombie}\index{zombie} & \texttt{Z} & Il processo è terminato ma il
+ suo stato di terminazione non è ancora
+ stato letto dal padre. \\
+ \hline
+ \end{tabular}
+ \caption{Elenco dei possibili stati di un processo in Linux, nella colonna
+ \texttt{STAT} si è riportata la corrispondente lettera usata dal comando
+ \cmd{ps} nell'omonimo campo.}
+ \label{tab:proc_proc_states}
+\end{table}
+
+Si deve quindi tenere presente che l'utilizzo della CPU è soltanto una delle
+risorse che sono necessarie per l'esecuzione di un programma, e a seconda
+dello scopo del programma non è detto neanche che sia la più importante (molti
+programmi dipendono in maniera molto più critica dall'I/O). Per questo motivo
+non è affatto detto che dare ad un programma la massima priorità di esecuzione
+abbia risultati significativi in termini di prestazioni.
Il meccanismo tradizionale di scheduling di Unix (che tratteremo in
\secref{sec:proc_sched_stand}) è sempre stato basato su delle \textsl{priorità
assoluta}, (chiamata anche \textsl{priorità statica}, in contrapposizione
alla normale priorità dinamica), per tenere conto dei sistemi
real-time,\footnote{per sistema real-time si intende un sistema in grado di
- eseguire operazioni in tempo reale; in genere si tende a distinguere fra
- l'\textit{hard real-time} in cui è necessario che i tempi di esecuzione di
- un programma siano determinabili con certezza assoluta, come nel caso di
- meccanismi di controllo di macchine, dove uno sforamento dei tempi avrebbe
- conseguenze disastrose, e \textit{soft-real-time} in cui un occasionale
- sforamento è ritenuto accettabile.} in cui è vitale che i processi che
-devono essere eseguiti in un determinato momento non debbano aspettare la
-conclusione di altri che non hanno questa necessità.
+ eseguire operazioni in un tempo ben determinato; in genere si tende a
+ distinguere fra l'\textit{hard real-time} in cui è necessario che i tempi di
+ esecuzione di un programma siano determinabili con certezza assoluta (come
+ nel caso di meccanismi di controllo di macchine, dove uno sforamento dei
+ tempi avrebbe conseguenze disastrose), e \textit{soft-real-time} in cui un
+ occasionale sforamento è ritenuto accettabile.} in cui è vitale che i
+processi che devono essere eseguiti in un determinato momento non debbano
+aspettare la conclusione di altri che non hanno questa necessità.
Il concetto di priorità assoluta dice che quando due processi si contendono
-l'esecuzione, vince sempre quello con la priorità assoluta più alta, anche
-quando l'altro è in esecuzione (grazie al \textit{prehemptive scheduling}).
+l'esecuzione, vince sempre quello con la priorità assoluta più alta.
Ovviamente questo avviene solo per i processi che sono pronti per essere
-eseguiti (cioè nello stato \textit{runnable}\footnote{lo stato di un processo
- è riportato nel campo \texttt{STAT} dell'output del comando \cmd{ps},
- abbiamo già visto che lo stato di \textit{zombie} è indicato con \texttt{Z},
- gli stati \textit{runnable}, \textit{sleep} e di I/O (\textit{uninteruttible
- sleep}) sono invece indicati con \texttt{R}, \texttt{S} e \texttt{D}.}),
-la priorità assoluta viene invece ignorata per quelli che sono bloccati su una
-richiesta di I/O o in stato di \textit{sleep}. La priorità assoluta viene in
+eseguiti (cioè nello stato \textit{runnable}). La priorità assoluta viene in
genere indicata con un numero intero, ed un valore più alto comporta una
-priorità maggiore, su questa politica di scheduling torneremo in
-\secref{sec:proc_real_time}.
+priorità maggiore. Su questa politica di scheduling torneremo in
+\secref{sec:proc_real_time}.
In generale quello che succede in tutti gli Unix moderni è che ai processi
normali viene sempre data una priorità assoluta pari a zero, e la decisione di
-assegnazione della CPU è fatta solo in base ad una priorità dinamica che è
-calcolata indipendentemente. È tuttavia possibile assegnare anche una priorità
-assoluta nel qual caso un processo avrà la precedenza su tutti gli altri di
-priorità inferiore che saranno eseguiti solo quando quest'ultimo non avrà
+assegnazione della CPU è fatta solo con il meccanismo tradizionale della
+priorità dinamica. In Linux tuttavia è possibile assegnare anche una priorità
+assoluta, nel qual caso un processo avrà la precedenza su tutti gli altri di
+priorità inferiore, che saranno eseguiti solo quando quest'ultimo non avrà
bisogno della CPU.
\subsection{Il meccanismo di \textit{scheduling} standard}
\label{sec:proc_sched_stand}
-A meno che non si abbiano specifiche esigenze, l'unico meccanismo di
-scheduling con il quale si avrà a che fare è quello tradizionale che prevede
-solo priorità dinamiche, ed è di questo che di norma ci si dovrà preoccupare
+A meno che non si abbiano esigenze specifiche, l'unico meccanismo di
+scheduling con il quale si avrà a che fare è quello tradizionale, che prevede
+solo priorità dinamiche. È di questo che, di norma, ci si dovrà preoccupare
nella programmazione.
Come accennato in Linux tutti i processi ordinari hanno la stessa priorità
assoluta. Quello che determina quale, fra tutti i processi in attesa di
esecuzione, sarà eseguito per primo, è la priorità dinamica, che è chiamata
-così proprio perché viene varia nel corso dell'esecuzione di un processo.
+così proprio perché varia nel corso dell'esecuzione di un processo. Oltre a
+questo la priorità dinamica determina quanto a lungo un processo continuerà ad
+essere eseguito, e quando un processo potrà subentrare ad un altro
+nell'esecuzione.
+
+Il meccanismo usato da Linux è piuttosto semplice, ad ogni processo è
+assegnata una \textit{time-slice}, cioè un intervallo di tempo (letteralmente
+una fetta) per il quale esso deve essere eseguito. Il valore della
+\textit{time-slice} è controllato dalla cosiddetta \textit{nice} (o
+\textit{niceness}) del processo. Essa è contenuta nel campo \var{nice} di
+\struct{task\_struct}; tutti i processi vengono creati con lo stesso valore,
+ed essa specifica il valore della durata iniziale della \textit{time-slice}
+che viene assegnato ad un altro campo della struttura (\var{counter}) quando
+il processo viene eseguito per la prima volta e diminuito progressivamente ad
+ogni interruzione del timer.
+
+Durante la sua esecuzione lo scheduler\index{scheduler} scandisce la coda dei
+processi in stato \textit{runnable} associando, in base al valore di
+\var{counter}, un peso ad ogni processo in attesa di esecuzione,\footnote{il
+ calcolo del peso in realtà è un po' più complicato, ad esempio nei sistemi
+ multiprocessore viene favorito un processo eseguito sulla stessa CPU, e a
+ parità del valore di \var{counter} viene favorito chi ha una priorità più
+ elevata.} chi ha il peso più alto verrà posto in esecuzione, ed il
+precedente processo sarà spostato in fondo alla coda. Dato che ad ogni
+interruzione del timer il valore di \var{counter} del processo corrente viene
+diminuito, questo assicura che anche i processi con priorità più bassa
+verranno messi in esecuzione.
+
+La priorità di un processo è così controllata attraverso il valore di
+\var{nice}, che stabilisce la durata della \textit{time-slice}; per il
+meccanismo appena descritto infatti un valore più lungo assicura una maggiore
+attribuzione di CPU. L'origine del nome di questo parametro sta nel fatto che
+generalmente questo viene usato per diminuire la priorità di un processo, come
+misura di cortesia nei confronti degli altri. I processi infatti vengono
+creati dal sistema con lo stesso valore di \var{nice} (nullo) e nessuno è
+privilegiato rispetto agli altri; il valore può essere modificato solo
+attraverso la funzione \funcd{nice}, il cui prototipo è:
+\begin{prototype}{unistd.h}
+{int nice(int inc)}
+ Aumenta il valore di \var{nice} per il processo corrente.
+
+ \bodydesc{La funzione ritorna zero in caso di successo e -1 in caso di
+ errore, nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{EPERM}] un processo senza i privilegi di amministratore ha
+ specificato un valore di \param{inc} negativo.
+ \end{errlist}}
+\end{prototype}
+
+L'argomento \param{inc} indica l'incremento del valore di \var{nice}:
+quest'ultimo può assumere valori compresi fra \const{PRIO\_MIN} e
+\const{PRIO\_MAX} (che nel caso di Linux sono $-19$ e $20$), ma per
+\param{inc} si può specificare un valore qualunque, positivo o negativo, ed il
+sistema provvederà a troncare il risultato nell'intervallo consentito. Valori
+positivi comportano maggiore \textit{cortesia} e cioè una diminuzione della
+priorità, ogni utente può solo innalzare il valore di un suo processo. Solo
+l'amministratore può specificare valori negativi che permettono di aumentare
+la priorità di un processo.
+
+In SUSv2 la funzione ritorna il nuovo valore di \var{nice}; Linux non segue
+questa convenzione, e per leggere il nuovo valore occorre invece usare la
+funzione \funcd{getpriority}, derivata da BSD, il cui prototipo è:
+\begin{prototype}{sys/resource.h}
+{int getpriority(int which, int who)}
+
+Restituisce il valore di \var{nice} per l'insieme dei processi specificati.
+
+ \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso di
+ errore, nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{ESRCH}] non c'è nessun processo che corrisponda ai valori di
+ \param{which} e \param{who}.
+ \item[\errcode{EINVAL}] il valore di \param{which} non è valido.
+ \end{errlist}}
+\end{prototype}
+\noindent nelle vecchie versioni può essere necessario includere anche
+\file{<sys/time.h>}, questo non è più necessario con versioni recenti delle
+librerie, ma è comunque utile per portabilità.
+
+La funzione permette, a seconda del valore di \param{which}, di leggere la
+priorità di un processo, di un gruppo di processi (vedi
+\secref{sec:sess_proc_group}) o di un utente, specificando un corrispondente
+valore per \param{who} secondo la legenda di \tabref{tab:proc_getpriority}; un
+valore nullo di quest'ultimo indica il processo, il gruppo di processi o
+l'utente correnti.
+
+\begin{table}[htb]
+ \centering
+ \footnotesize
+ \begin{tabular}[c]{|c|c|l|}
+ \hline
+ \param{which} & \param{who} & \textbf{Significato} \\
+ \hline
+ \hline
+ \const{PRIO\_PROCESS} & \type{pid\_t} & processo \\
+ \const{PRIO\_PRGR} & \type{pid\_t} & process group \\
+ \const{PRIO\_USER} & \type{uid\_t} & utente \\
+ \hline
+ \end{tabular}
+ \caption{Legenda del valore dell'argomento \param{which} e del tipo
+ dell'argomento \param{who} delle funzioni \func{getpriority} e
+ \func{setpriority} per le tre possibili scelte.}
+ \label{tab:proc_getpriority}
+\end{table}
+
+La funzione restituisce la priorità più alta (cioè il valore più basso) fra
+quelle dei processi specificati; dato che -1 è un valore possibile, per poter
+rilevare una condizione di errore è necessario cancellare sempre \var{errno}
+prima della chiamata alla funzione, per verificare che essa resti uguale a
+zero.
+
+Analoga a \func{getpriority} la funzione \funcd{setpriority} permette di
+impostare la priorità di uno o più processi; il suo prototipo è:
+\begin{prototype}{sys/resource.h}
+{int setpriority(int which, int who, int prio)}
+ Imposta la priorità per l'insieme dei processi specificati.
+
+ \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso di
+ errore, nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{ESRCH}] non c'è nessun processo che corrisponda ai valori di
+ \param{which} e \param{who}.
+ \item[\errcode{EINVAL}] il valore di \param{which} non è valido.
+ \item[\errcode{EPERM}] un processo senza i privilegi di amministratore ha
+ specificato un valore di \param{inc} negativo.
+ \item[\errcode{EACCES}] un processo senza i privilegi di amministratore ha
+ cercato di modificare la priorità di un processo di un altro utente.
+ \end{errlist}}
+\end{prototype}
+La funzione imposta la priorità al valore specificato da \param{prio} per
+tutti i processi indicati dagli argomenti \param{which} e \param{who}. La
+gestione dei permessi dipende dalle varie implementazioni; in Linux, secondo
+le specifiche dello standard SUSv3, e come avviene per tutti i sistemi che
+derivano da SysV, è richiesto che l'user-ID reale o effettivo del processo
+chiamante corrispondano al real user-ID (e solo quello) del processo di cui si
+vuole cambiare la priorità; per i sistemi derivati da BSD invece (SunOS,
+Ultrix, *BSD) la corrispondenza può essere anche con l'user-ID effettivo.
\subsection{Il meccanismo di \textit{scheduling real-time}}
\label{sec:proc_real_time}
-Per settare le
+Come spiegato in \secref{sec:proc_sched} lo standard POSIX.1b ha introdotto le
+priorità assolute per permettere la gestione di processi real-time. In realtà
+nel caso di Linux non si tratta di un vero hard real-time, in quanto in
+presenza di eventuali interrupt il kernel interrompe l'esecuzione di un
+processo qualsiasi sia la sua priorità,\footnote{questo a meno che non si
+ siano installate le patch di RTLinux, RTAI o Adeos, con i quali è possibile
+ ottenere un sistema effettivamente hard real-time. In tal caso infatti gli
+ interrupt vengono intercettati dall'interfaccia real-time (o nel caso di
+ Adeos gestiti dalle code del nano-kernel), in modo da poterli controllare
+ direttamente qualora ci sia la necessità di avere un processo con priorità
+ più elevata di un \textit{interrupt handler}.} mentre con l'incorrere in un
+page fault\index{page fault} si possono avere ritardi non previsti. Se
+l'ultimo problema può essere aggirato attraverso l'uso delle funzioni di
+controllo della memoria virtuale (vedi \secref{sec:proc_mem_lock}), il primo
+non è superabile e può comportare ritardi non prevedibili riguardo ai tempi di
+esecuzione di qualunque processo.
+
+Occorre usare le priorità assolute con molta attenzione: se si dà ad un
+processo una priorità assoluta e questo finisce in un loop infinito, nessun
+altro processo potrà essere eseguito, ed esso sarà mantenuto in esecuzione
+permanentemente assorbendo tutta la CPU e senza nessuna possibilità di
+riottenere l'accesso al sistema. Per questo motivo è sempre opportuno, quando
+si lavora con processi che usano priorità assolute, tenere attiva una shell
+cui si sia assegnata la massima priorità assoluta, in modo da poter essere
+comunque in grado di rientrare nel sistema.
+
+Quando c'è un processo con priorità assoluta lo scheduler\index{scheduler} lo
+metterà in esecuzione prima di ogni processo normale. In caso di più processi
+sarà eseguito per primo quello con priorità assoluta più alta. Quando ci sono
+più processi con la stessa priorità assoluta questi vengono tenuti in una coda
+e tocca al kernel decidere quale deve essere eseguito.
+Il meccanismo con cui vengono gestiti questi processi dipende dalla politica
+di scheduling che si è scelto; lo standard ne prevede due:
+\begin{basedescript}{\desclabelwidth{1.2cm}\desclabelstyle{\nextlinelabel}}
+\item[\textit{FIFO}] \textit{First In First Out}. Il processo viene eseguito
+ fintanto che non cede volontariamente la CPU, si blocca, finisce o viene
+ interrotto da un processo a priorità più alta.
+\item[\textit{RR}] \textit{Round Robin}. Ciascun processo viene eseguito a
+ turno per un certo periodo di tempo (una \textit{time slice}). Solo i
+ processi con la stessa priorità ed in stato \textit{runnable} entrano nel
+ circolo.
+\end{basedescript}
+
+La funzione per impostare le politiche di scheduling (sia real-time che
+ordinarie) ed i relativi parametri è \funcd{sched\_setscheduler}; il suo
+prototipo è:
+\begin{prototype}{sched.h}
+{int sched\_setscheduler(pid\_t pid, int policy, const struct sched\_param *p)}
+ Imposta priorità e politica di scheduling.
+
+ \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso
+ di errore, nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{ESRCH}] il processo \param{pid} non esiste.
+ \item[\errcode{EINVAL}] il valore di \param{policy} non esiste o il
+ relativo valore di \param{p} non è valido.
+ \item[\errcode{EPERM}] il processo non ha i privilegi per attivare la
+ politica richiesta.
+ \end{errlist}}
+\end{prototype}
+
+La funzione esegue l'impostazione per il processo specificato dall'argomento
+\param{pid}; un valore nullo esegue l'impostazione per il processo corrente.
+La politica di scheduling è specificata dall'argomento \param{policy} i cui
+possibili valori sono riportati in \tabref{tab:proc_sched_policy}; un valore
+negativo per \param{policy} mantiene la politica di scheduling corrente.
+Solo un processo con i privilegi di amministratore può impostare priorità
+assolute diverse da zero o politiche \const{SCHED\_FIFO} e \const{SCHED\_RR}.
+
+\begin{table}[htb]
+ \centering
+ \footnotesize
+ \begin{tabular}[c]{|c|l|}
+ \hline
+ \textbf{Policy} & \textbf{Significato} \\
+ \hline
+ \hline
+ \const{SCHED\_FIFO} & Scheduling real-time con politica \textit{FIFO} \\
+ \const{SCHED\_RR} & Scheduling real-time con politica \textit{Round
+ Robin} \\
+ \const{SCHED\_OTHER}& Scheduling ordinario\\
+ \hline
+ \end{tabular}
+ \caption{Valori dell'argomento \param{policy} per la funzione
+ \func{sched\_setscheduler}. }
+ \label{tab:proc_sched_policy}
+\end{table}
+
+Il valore della priorità è passato attraverso la struttura
+\struct{sched\_param} (riportata in \figref{fig:sig_sched_param}), il cui solo
+campo attualmente definito è \var{sched\_priority}, che nel caso delle
+priorità assolute deve essere specificato nell'intervallo fra un valore
+massimo ed uno minimo, che nel caso sono rispettivamente 1 e 99 (il valore
+zero è legale, ma indica i processi normali).
+
+\begin{figure}[!bht]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \includestruct{listati/sched_param.c}
+ \end{minipage}
+ \normalsize
+ \caption{La struttura \structd{sched\_param}.}
+ \label{fig:sig_sched_param}
+\end{figure}
+
+Lo standard POSIX.1b prevede comunque che i due valori della massima e minima
+priorità statica possano essere ottenuti, per ciascuna delle politiche di
+scheduling realtime, tramite le due funzioni \funcd{sched\_get\_priority\_max}
+e \funcd{sched\_get\_priority\_min}, i cui prototipi sono:
+\begin{functions}
+ \headdecl{sched.h}
+
+ \funcdecl{int sched\_get\_priority\_max(int policy)} Legge il valore
+ massimo della priorità statica per la politica di scheduling \param{policy}.
+
+
+ \funcdecl{int sched\_get\_priority\_min(int policy)} Legge il valore minimo
+ della priorità statica per la politica di scheduling \param{policy}.
+
+ \bodydesc{La funzioni ritornano il valore della priorità in caso di successo
+ e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{EINVAL}] il valore di \param{policy} non è valido.
+ \end{errlist}}
+\end{functions}
+
+
+I processi con politica di scheduling \const{SCHED\_OTHER} devono specificare
+un valore nullo (altrimenti si avrà un errore \errcode{EINVAL}), questo valore
+infatti non ha niente a che vedere con la priorità dinamica determinata dal
+valore di \var{nice}, che deve essere impostato con le funzioni viste in
+precedenza.
+
+Il kernel mantiene i processi con la stessa priorità assoluta in una lista, ed
+esegue sempre il primo della lista, mentre un nuovo processo che torna in
+stato \textit{runnable} viene sempre inserito in coda alla lista. Se la
+politica scelta è \const{SCHED\_FIFO} quando il processo viene eseguito viene
+automaticamente rimesso in coda alla lista, e la sua esecuzione continua
+fintanto che non viene bloccato da una richiesta di I/O, o non rilascia
+volontariamente la CPU (in tal caso, tornando nello stato \textit{runnable}
+sarà reinserito in coda alla lista); l'esecuzione viene ripresa subito solo
+nel caso che esso sia stato interrotto da un processo a priorità più alta.
+
+La priorità assoluta può essere riletta indietro dalla funzione
+\funcd{sched\_getscheduler}, il cui prototipo è:
+\begin{prototype}{sched.h}
+{int sched\_getscheduler(pid\_t pid)}
+ Legge la politica di scheduling per il processo \param{pid}.
+
+ \bodydesc{La funzione ritorna la politica di scheduling in caso di successo
+ e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{ESRCH}] il processo \param{pid} non esiste.
+ \item[\errcode{EINVAL}] il valore di \param{pid} è negativo.
+ \end{errlist}}
+\end{prototype}
+
+La funzione restituisce il valore (secondo quanto elencato in
+\tabref{tab:proc_sched_policy}) della politica di scheduling per il processo
+specificato; se \param{pid} è nullo viene restituito quello del processo
+chiamante.
+Se si intende operare solo sulla priorità assoluta di un processo si possono
+usare le funzioni \funcd{sched\_setparam} e \funcd{sched\_getparam}, i cui
+prototipi sono:
+\begin{functions}
+ \headdecl{sched.h}
+
+ \funcdecl{int sched\_setparam(pid\_t pid, const struct sched\_param *p)}
+ Imposta la priorità assoluta del processo \param{pid}.
+
+ \funcdecl{int sched\_getparam(pid\_t pid, struct sched\_param *p)}
+ Legge la priorità assoluta del processo \param{pid}.
+
+ \bodydesc{La funzione ritorna la priorità in caso di successo
+ e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{ESRCH}] il processo \param{pid} non esiste.
+ \item[\errcode{EINVAL}] il valore di \param{pid} è negativo.
+ \end{errlist}}
+\end{functions}
-\footnote{a meno che non si siano installate le patch di RTLinux o RTAI, con i
- quali è possibile ottenere un sistema effettivamente hard real-time.}
+L'uso di \func{sched\_setparam} che è del tutto equivalente a
+\func{sched\_setscheduler} con \param{priority} uguale a -1. Come per
+\func{sched\_setscheduler} specificando 0 come valore di \param{pid} si opera
+sul processo corrente. La disponibilità di entrambe le funzioni può essere
+verificata controllando la macro \macro{\_POSIX\_PRIORITY\_SCHEDULING} che è
+definita nell'header \file{sched.h}.
+
+L'ultima funzione che permette di leggere le informazioni relative ai processi
+real-time è \funcd{sched\_rr\_get\_interval}, che permette di ottenere la
+lunghezza della \textit{time slice} usata dalla politica \textit{round robin};
+il suo prototipo è:
+\begin{prototype}{sched.h}
+ {int sched\_rr\_get\_interval(pid\_t pid, struct timespec *tp)} Legge in
+ \param{tp} la durata della \textit{time slice} per il processo \param{pid}.
+
+ \bodydesc{La funzione ritorna 0in caso di successo e -1 in caso di errore,
+ nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{ESRCH}] il processo \param{pid} non esiste.
+ \item[\errcode{ENOSYS}] la system call non è stata implementata.
+ \end{errlist}}
+\end{prototype}
-in realtà non si tratta di un vero hard real-time, in quanto
- la presenza di eventuali interrupt o di page fault può sempre interrompere
- l'esecuzione di un processo, a meno di non installare le estensioni di
- RTLinux o RTAI, il normale kernel non è real-time.
+La funzione restituisce il valore dell'intervallo di tempo usato per la
+politica \textit{round robin} in una struttura \struct{timespec}, (la cui
+definizione si può trovare in \figref{fig:sys_timeval_struct}).
+Come accennato ogni processo che usa lo scheduling real-time può rilasciare
+volontariamente la CPU; questo viene fatto attraverso la funzione
+\funcd{sched\_yield}, il cui prototipo è:
+\begin{prototype}{sched.h}
+ {int sched\_yield(void)}
+
+ Rilascia volontariamente l'esecuzione.
+
+ \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore,
+ nel qual caso \var{errno} viene impostata opportunamente.}
+\end{prototype}
+La funzione fa sì che il processo rilasci la CPU, in modo da essere rimesso in
+coda alla lista dei processi da eseguire, e permettere l'esecuzione di un
+altro processo; se però il processo è l'unico ad essere presente sulla coda
+l'esecuzione non sarà interrotta. In genere usano questa funzione i processi
+in modalità \textit{fifo}, per permettere l'esecuzione degli altri processi
+con pari priorità quando la sezione più urgente è finita.
\section{Problematiche di programmazione multitasking}
In un ambiente multitasking il concetto è essenziale, dato che un processo può
essere interrotto in qualunque momento dal kernel che mette in esecuzione un
altro processo o dalla ricezione di un segnale; occorre pertanto essere
-accorti nei confronti delle possibili \textit{race condition} (vedi
+accorti nei confronti delle possibili
+\textit{race condition}\index{race condition} (vedi
\secref{sec:proc_race_cond}) derivanti da operazioni interrotte in una fase in
cui non erano ancora state completate.
qualunque momento, e le operazioni di un eventuale \textit{signal handler}
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}).
+operazioni atomiche (torneremo su questi aspetti in
+\secref{sec:sig_control}).
In questo caso il sistema provvede un tipo di dato, il \type{sig\_atomic\_t},
il cui accesso è assicurato essere atomico. In pratica comunque si può
assumere che, in ogni piattaforma su cui è implementato Linux, il tipo
-\type{int}, gli altri interi di dimensione inferiore ed i puntatori sono
+\ctyp{int}, gli altri interi di dimensione inferiore ed i puntatori sono
atomici. Non è affatto detto che lo stesso valga per interi di dimensioni
maggiori (in cui l'accesso può comportare più istruzioni in assembler) o per
le strutture. In tutti questi casi è anche opportuno marcare come
-\type{volatile} le variabili che possono essere interessate ad accesso
+\direct{volatile} le variabili che possono essere interessate ad accesso
condiviso, onde evitare problemi con le ottimizzazioni del codice.
-\subsection{Le \textit{race condition} e i \textit{deadlock}}
+
+\subsection{Le \textit{race condition}\index{race condition} e i
+ \textit{deadlock}\index{deadlock}}
\label{sec:proc_race_cond}
-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 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 definiscono \textit{race condition}\index{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 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.
Dato che in un sistema multitasking ogni processo può essere interrotto in
-qualunque momento per farne subentrare un'altro in esecuzione, niente può
+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
Per questo occorre essere ben consapevoli 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 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}, 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 (trovandolo 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}).
+gli adeguati provvedimenti per far sì che non si verifichino. Casi tipici di
+\textit{race condition}\index{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 sulle risorse condivise (le cosiddette
+\textsl{sezioni critiche}\index{sezioni critiche}) del programma, siano
+opportunamente protette da meccanismi di sincronizzazione (torneremo su queste
+problematiche di questo tipo in \capref{cha:IPC}).
+
+Un caso particolare di \textit{race condition}\index{race condition} sono poi
+i cosiddetti \textit{deadlock}\index{deadlock}, particolarmente gravi in
+quanto comportano spesso il blocco completo di un servizio, e non il
+fallimento di una singola operazione. Per definizione un
+\textit{deadlock}\index{deadlock} è una situazione in cui due o più processi
+non sono più in grado di proseguire perché ciascuno aspetta il risultato di
+una operazione che dovrebbe essere eseguita dall'altro.
+
+
+L'esempio tipico di una situazione che può condurre ad un
+\textit{deadlock}\index{deadlock} è quello in cui un flag di
+``\textsl{occupazione}'' viene rilasciato da un evento asincrono (come un
+segnale o un altro processo) fra il momento in cui lo si è controllato
+(trovandolo 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}\index{deadlock}).
In tutti questi casi è di fondamentale importanza il concetto di atomicità
visto in \secref{sec:proc_atom_oper}; questi problemi infatti possono essere
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.
+delle funzioni all'interno dei gestori dei segnali.
Fintanto che una funzione opera soltanto con le variabili locali è rientrante;
queste infatti vengono allocate nello stack, e un'altra invocazione non fa
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.
+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