%% 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 "Un preambolo",
%% 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 "Un preambolo",
Come accennato nell'introduzione in un sistema Unix tutte le operazioni
vengono svolte tramite opportuni processi. In sostanza questi ultimi vengono
Come accennato nell'introduzione in un sistema Unix tutte le operazioni
vengono svolte tramite opportuni processi. In sostanza questi ultimi vengono
-unità a se stante, in questo esamineremo il funzionamento dei processi
-all'interno del sistema. Saranno cioè affrontati i dettagli della creazione e
+unità a se stante, in questo esamineremo il funzionamento dei processi
+all'interno del sistema. Saranno cioè affrontati i dettagli della creazione e
della terminazione dei processi, della gestione dei loro attributi e
privilegi, e di tutte le funzioni a questo connesse. Infine nella sezione
finale introdurremo alcune problematiche generiche della programmazione in
della terminazione dei processi, della gestione dei loro attributi e
privilegi, e di tutte le funzioni a questo connesse. Infine nella sezione
finale introdurremo alcune problematiche generiche della programmazione in
-generazione di nuovi processi è un'operazione privilegiata) una delle
-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 univoco, il cosiddetto \textit{process identifier} o, più brevemente,
+generazione di nuovi processi è un'operazione privilegiata) una delle
+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 univoco, il cosiddetto \textit{process identifier} o, più brevemente,
\acr{pid}, assegnato in forma progressiva (vedi sez.~\ref{sec:proc_pid})
quando il processo viene creato.
\acr{pid}, assegnato in forma progressiva (vedi sez.~\ref{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
-genere la sequenza è sempre quella di creare un nuovo processo, il quale
-eseguirà, in un passo successivo, il programma desiderato: questo è ad esempio
+Una seconda caratteristica di un sistema Unix è che la generazione di un
+processo è un'operazione separata rispetto al lancio di un programma. In
+genere la sequenza è sempre quella di creare un nuovo processo, il quale
+eseguirà, in un passo successivo, il programma desiderato: questo è ad esempio
quello che fa la shell quando mette in esecuzione il programma che gli
indichiamo nella linea di comando.
quello che fa la shell quando mette in esecuzione il programma che gli
indichiamo nella linea di comando.
altro, che viene chiamato processo padre (\textit{parent process}). Questo
vale per tutti i processi, con una sola eccezione: dato che ci deve essere un
altro, che viene chiamato processo padre (\textit{parent process}). Questo
vale per tutti i processi, con una sola eccezione: dato che ci deve essere un
\cmd{/sbin/init}), che viene lanciato dal kernel alla conclusione della fase
di avvio; essendo questo il primo processo lanciato dal sistema ha sempre il
\cmd{/sbin/init}), che viene lanciato dal kernel alla conclusione della fase
di avvio; essendo questo il primo processo lanciato dal sistema ha sempre il
struttura del sistema comunque consente di lanciare al posto di \cmd{init}
qualunque altro programma, e in casi di emergenza (ad esempio se il file di
struttura del sistema comunque consente di lanciare al posto di \cmd{init}
qualunque altro programma, e in casi di emergenza (ad esempio se il file di
direttamente dal kernel, (come \cmd{keventd}, \cmd{kswapd}, ecc.).} si
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
direttamente dal kernel, (come \cmd{keventd}, \cmd{kswapd}, ecc.).} si
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
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}
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}
% http://www.ibm.com/developerworks/linux/library/l-linux-process-management/
% TODO completare la parte su quando viene chiamato lo scheduler.
% http://www.ibm.com/developerworks/linux/library/l-linux-process-management/
% TODO completare la parte su quando viene chiamato lo scheduler.
-eseguito ad ogni system call ed ad ogni interrupt,\footnote{più in una serie
- di altre occasioni.} ma può essere anche attivato esplicitamente. Il timer
+eseguito ad ogni system call ed ad ogni interrupt,\footnote{più in una serie
+ di altre occasioni.} 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},\footnote{fino al kernel 2.4 il valore di \const{HZ} era 100 su
di sistema provvede comunque a che esso sia invocato periodicamente; generando
un interrupt periodico secondo la frequenza specificata dalla costante
\const{HZ},\footnote{fino al kernel 2.4 il valore di \const{HZ} era 100 su
- tutte le architetture tranne l'alpha, per cui era 1000, nel 2.6 è stato
- portato a 1000 su tutte; dal 2.6.13 lo si può impostare in fase di
+ tutte le architetture tranne l'alpha, per cui era 1000, nel 2.6 è stato
+ portato a 1000 su tutte; dal 2.6.13 lo si può impostare in fase di
refresh della televisione); occorre fare attenzione a non confondere questo
valore con quello dei \itindex{clock~tick} \textit{clock tick} (vedi
sez.~\ref{sec:sys_unix_time}).} definita in \file{asm/param.h}, ed il cui
refresh della televisione); occorre fare attenzione a non confondere questo
valore con quello dei \itindex{clock~tick} \textit{clock tick} (vedi
sez.~\ref{sec:sys_unix_time}).} definita in \file{asm/param.h}, ed il cui
frequenza prefissata, ma ad ogni chiamata del timer viene programmata
l'interruzione successiva sulla base di una stima; in questo modo si evita
di dover eseguire un migliaio di interruzioni al secondo anche su macchine
che non stanno facendo nulla, con un forte risparmio nell'uso dell'energia
frequenza prefissata, ma ad ogni chiamata del timer viene programmata
l'interruzione successiva sulla base di una stima; in questo modo si evita
di dover eseguire un migliaio di interruzioni al secondo anche su macchine
che non stanno facendo nulla, con un forte risparmio nell'uso dell'energia
per lunghi periodi di tempo.}
Ogni volta che viene eseguito, lo \itindex{scheduler} \textit{scheduler}
per lunghi periodi di tempo.}
Ogni volta che viene eseguito, lo \itindex{scheduler} \textit{scheduler}
questo in sez.~\ref{sec:proc_priority}) e stabilisce quale di essi debba
essere posto in esecuzione fino alla successiva invocazione.
questo in sez.~\ref{sec:proc_priority}) e stabilisce quale di essi debba
essere posto in esecuzione fino alla successiva invocazione.
Tradizionalmente in un sistema unix-like i processi vengono sempre creati da
altri processi tramite la funzione \func{fork}; il nuovo processo (che viene
Tradizionalmente in un sistema unix-like i processi vengono sempre creati da
altri processi tramite la funzione \func{fork}; il nuovo processo (che viene
processo processo originale (detto \textsl{padre}), ma ha un nuovo \acr{pid} e
viene eseguito in maniera indipendente (le differenze fra padre e figlio sono
affrontate in dettaglio in sez.~\ref{sec:proc_fork}).
processo processo originale (detto \textsl{padre}), ma ha un nuovo \acr{pid} e
viene eseguito in maniera indipendente (le differenze fra padre e figlio sono
affrontate in dettaglio in sez.~\ref{sec:proc_fork}).
abbastanza limitata sulle cause della terminazione del processo figlio.
Quando un processo ha concluso il suo compito o ha incontrato un errore non
abbastanza limitata sulle cause della terminazione del processo figlio.
Quando un processo ha concluso il suo compito o ha incontrato un errore non
-risolvibile esso può essere terminato con la funzione \func{exit} (si veda
-quanto discusso in sez.~\ref{sec:proc_conclusion}). La vita del processo però
+risolvibile esso può essere terminato con la funzione \func{exit} (si veda
+quanto discusso in sez.~\ref{sec:proc_conclusion}). La vita del processo però
termina completamente solo quando la notifica della sua conclusione viene
ricevuta dal processo padre, a quel punto tutte le risorse allocate nel
sistema ad esso associate vengono rilasciate.
termina completamente solo quando la notifica della sua conclusione viene
ricevuta dal processo padre, a quel punto tutte le risorse allocate nel
sistema ad esso associate vengono rilasciate.
stata stabilita), o fargli eseguire (come fa la shell) un altro programma. Per
quest'ultimo caso si usa la seconda funzione fondamentale per programmazione
stata stabilita), o fargli eseguire (come fa la shell) un altro programma. Per
quest'ultimo caso si usa la seconda funzione fondamentale per programmazione
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
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
Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto
particolari con caratteristiche uniche rispetto a tutte le altre, infatti la
Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto
particolari con caratteristiche uniche rispetto a tutte le altre, infatti la
Come accennato nell'introduzione, ogni processo viene identificato dal sistema
da un numero identificativo univoco, il \textit{process ID} o \acr{pid};
Come accennato nell'introduzione, ogni processo viene identificato dal sistema
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 è
+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 è
\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,
\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,
\acr{pgid} o \acr{sid} (vedi sez.~\ref{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
\acr{pgid} o \acr{sid} (vedi sez.~\ref{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
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 \itindex{thread} \textit{thread} creata da Ingo
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 \itindex{thread} \textit{thread} creata da Ingo
- Molnar anche il meccanismo di allocazione dei \acr{pid} è stato modificato;
- il valore massimo è impostabile attraverso il file
+ Molnar anche il meccanismo di allocazione dei \acr{pid} è stato modificato;
+ il valore massimo è impostabile attraverso il file
Per questo motivo, come visto in sez.~\ref{sec:proc_hierarchy}, il processo di
avvio (\cmd{init}) ha sempre il \acr{pid} uguale a uno.
Per questo motivo, come visto in sez.~\ref{sec:proc_hierarchy}, il processo di
avvio (\cmd{init}) ha sempre il \acr{pid} uguale a uno.
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
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
funzione \func{tempnam} (si veda sez.~\ref{sec:file_temp_file}) usa il
\acr{pid} per generare un \itindex{pathname} \textit{pathname} univoco, che
funzione \func{tempnam} (si veda sez.~\ref{sec:file_temp_file}) usa il
\acr{pid} per generare un \itindex{pathname} \textit{pathname} univoco, che
sessione}, in cui si raggruppano i processi creati su uno stesso terminale,
o relativi allo stesso login. Torneremo su questo argomento in dettaglio in
cap.~\ref{cha:session}, dove esamineremo gli altri identificativi associati ad
sessione}, in cui si raggruppano i processi creati su uno stesso terminale,
o relativi allo stesso login. Torneremo su questo argomento in dettaglio in
cap.~\ref{cha:session}, dove esamineremo gli altri identificativi associati ad
Oltre al \acr{pid} e al \acr{ppid}, (e a quelli che vedremo in
sez.~\ref{sec:sess_proc_group}, relativi al controllo di sessione), ad ogni
processo vengono associati degli altri identificatori che vengono usati per il
Oltre al \acr{pid} e al \acr{ppid}, (e a quelli che vedremo in
sez.~\ref{sec:sess_proc_group}, relativi al controllo di sessione), ad ogni
processo vengono associati degli altri identificatori che vengono usati per il
affrontato in dettaglio in sez.~\ref{sec:proc_perms}.
\subsection{La funzione \func{fork} e le funzioni di creazione dei processi}
\label{sec:proc_fork}
affrontato in dettaglio in sez.~\ref{sec:proc_perms}.
\subsection{La funzione \func{fork} e le funzioni di creazione dei processi}
\label{sec:proc_fork}
-La funzione \funcd{fork} è la funzione fondamentale della gestione dei
-processi: come si è detto tradizionalmente l'unico modo di creare un nuovo
-processo era attraverso l'uso di questa funzione,\footnote{in realtà oggi la
- system call usata più comunemente da Linux per creare nuovi processi è
- \func{clone} (vedi \ref{sec:process_clone}) , anche perché a partire dalle
- \acr{glibc} 2.3.3 non viene più usata la system call originale, ma la stessa
+La funzione \funcd{fork} è la funzione fondamentale della gestione dei
+processi: come si è detto tradizionalmente l'unico modo di creare un nuovo
+processo era attraverso l'uso di questa funzione,\footnote{in realtà oggi la
+ system call usata più comunemente da Linux per creare nuovi processi è
+ \func{clone} (vedi \ref{sec:process_clone}) , anche perché a partire dalle
+ \acr{glibc} 2.3.3 non viene più usata la system call originale, ma la stessa
\func{fork} viene implementata tramite \func{clone}, cosa che consente una
migliore interazione coi \textit{thread}.} essa quindi riveste un ruolo
centrale tutte le volte che si devono scrivere programmi che usano il
multitasking.\footnote{oggi questa rilevanza, con la diffusione dell'uso dei
\func{fork} viene implementata tramite \func{clone}, cosa che consente una
migliore interazione coi \textit{thread}.} essa quindi riveste un ruolo
centrale tutte le volte che si devono scrivere programmi che usano il
multitasking.\footnote{oggi questa rilevanza, con la diffusione dell'uso dei
\bodydesc{In caso di successo restituisce il \acr{pid} del figlio al padre e
zero al figlio; ritorna -1 al padre (senza creare il figlio) in caso di
\bodydesc{In caso di successo restituisce il \acr{pid} del figlio al padre e
zero al figlio; ritorna -1 al padre (senza creare il figlio) in caso di
\begin{errlist}
\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
\begin{errlist}
\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[\errcode{ENOMEM}] non è stato possibile allocare la memoria per le
+ si è esaurito il numero di processi disponibili.
+ \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 a partire
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 a partire
copia del padre, e riceve una copia dei \index{segmento!testo} segmenti di
testo, \itindex{stack} \textit{stack} e \index{segmento!dati} dati (vedi
sez.~\ref{sec:proc_mem_layout}), ed esegue esattamente lo stesso codice del
copia del padre, e riceve una copia dei \index{segmento!testo} segmenti di
testo, \itindex{stack} \textit{stack} e \index{segmento!dati} dati (vedi
sez.~\ref{sec:proc_mem_layout}), ed esegue esattamente lo stesso codice del
pertanto padre e figlio vedono variabili diverse.
Per quanto riguarda la gestione della memoria, in generale il
pertanto padre e figlio vedono variabili diverse.
Per quanto riguarda la gestione della memoria, in generale il
condiviso e tenuto in read-only per il padre e per i figli. Per gli altri
segmenti Linux utilizza la tecnica del \itindex{copy~on~write} \textit{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).
condiviso e tenuto in read-only per il padre e per i figli. Per gli altri
segmenti Linux utilizza la tecnica del \itindex{copy~on~write} \textit{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
+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.
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
-nel figlio è zero; in questo modo il programma può identificare se viene
+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
+nel figlio è zero; in questo modo il programma può identificare se viene
eseguito dal padre o dal figlio. Si noti come la funzione \func{fork} ritorni
\textbf{due} volte: una nel padre e una nel figlio.
eseguito dal padre o dal figlio. Si noti come la funzione \func{fork} ritorni
\textbf{due} volte: una nel padre e una nel figlio.
-La scelta di questi valori di ritorno non è casuale, un processo infatti può
-avere più figli, ed il valore di ritorno di \func{fork} è l'unico modo che gli
+La scelta di questi valori di ritorno non è casuale, un processo infatti può
+avere più figli, ed il valore di ritorno di \func{fork} è l'unico modo che gli
-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
+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
sez.~\ref{sec:sys_resource_limit}, ed in particolare
tab.~\ref{tab:sys_rlimit_values}).
sul numero totale di processi permessi all'utente (vedi
sez.~\ref{sec:sys_resource_limit}, ed in particolare
tab.~\ref{tab:sys_rlimit_values}).
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
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 programmi server (il modello
-\textit{client-server} è illustrato in sez.~\ref{sec:net_cliserv}) in cui il
+ne esegue un'altra. È il caso tipico dei programmi server (il modello
+\textit{client-server} è illustrato in sez.~\ref{sec:net_cliserv}) in cui il
-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 di fare una \func{exec} (di
+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 di fare una \func{exec} (di
cui parleremo in sez.~\ref{sec:proc_exec}) subito dopo la \func{fork}.
Alcuni sistemi operativi (il VMS ad esempio) combinano le operazioni di questa
cui parleremo in sez.~\ref{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
-operazione che viene chiamata \textit{spawn}. Nei sistemi unix-like è stato
-scelto di mantenere questa separazione, dato che, come per la prima modalità
-d'uso, esistono numerosi scenari in cui si può usare una \func{fork} senza
+seconda modalità (una \func{fork} seguita da una \func{exec}) in un'unica
+operazione che viene chiamata \textit{spawn}. Nei sistemi unix-like è stato
+scelto di mantenere questa separazione, dato che, come per la prima modalità
+d'uso, esistono numerosi scenari in cui si può usare una \func{fork} senza
-dell'output, identificatori) prima della \func{exec}, rendendo così
-relativamente facile intervenire sulle le modalità di esecuzione del nuovo
+dell'output, identificatori) prima della \func{exec}, rendendo così
+relativamente facile intervenire sulle le modalità di esecuzione del nuovo
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
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
distribuito insieme agli altri sorgenti degli esempi su
\href{http://gapil.truelite.it/gapil_source.tgz}
{\textsf{http://gapil.truelite.it/gapil\_source.tgz}}.
distribuito insieme agli altri sorgenti degli esempi su
\href{http://gapil.truelite.it/gapil_source.tgz}
{\textsf{http://gapil.truelite.it/gapil\_source.tgz}}.
specificato e scrivere un messaggio prima di uscire. Il processo padre invece
(\texttt{\small 36--38}) stampa un messaggio di creazione, eventualmente
attende il numero di secondi specificato, e procede nell'esecuzione del ciclo;
specificato e scrivere un messaggio prima di uscire. Il processo padre invece
(\texttt{\small 36--38}) stampa un messaggio di creazione, eventualmente
attende il numero di secondi specificato, e procede nell'esecuzione del ciclo;
valori predefiniti specificano di non attendere), otterremo come output sul
terminale:
\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm]
valori predefiniti specificano di non attendere), otterremo come output sul
terminale:
\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm]
-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
-dopo la chiamata a \func{fork}; dall'esempio si può notare infatti come nei
+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
+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),
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),
\itindex{scheduler} scheduling usato dal kernel, dalla particolare situazione
in cui si trova la macchina al momento della chiamata, risultando del tutto
\itindex{scheduler} scheduling usato dal kernel, dalla particolare situazione
in cui si trova la macchina al momento della chiamata, risultando del tutto
-Pertanto non si può fare nessuna assunzione sulla sequenza di esecuzione delle
-istruzioni del codice fra padre e figli, né sull'ordine in cui questi potranno
-essere messi in esecuzione. Se è necessaria una qualche forma di precedenza
-occorrerà provvedere ad espliciti meccanismi di sincronizzazione, pena il
+Pertanto non si può fare nessuna assunzione sulla sequenza di esecuzione delle
+istruzioni del codice fra padre e figli, né sull'ordine in cui questi potranno
+essere messi in esecuzione. Se è necessaria una qualche forma di precedenza
+occorrerà provvedere ad espliciti meccanismi di sincronizzazione, pena il
rischio di incorrere nelle cosiddette \itindex{race~condition} \textit{race
condition} (vedi sez.~\ref{sec:proc_race_cond}).
rischio di incorrere nelle cosiddette \itindex{race~condition} \textit{race
condition} (vedi sez.~\ref{sec:proc_race_cond}).
\textit{scheduler} di Ingo Molnar esegue sempre per primo il
figlio;\footnote{i risultati precedenti sono stati ottenuti usando un kernel
\textit{scheduler} di Ingo Molnar esegue sempre per primo il
figlio;\footnote{i risultati precedenti sono stati ottenuti usando un kernel
padre, effettuando per primo una operazione di scrittura in memoria, attivi il
meccanismo del \itindex{copy~on~write} \textit{copy on write}. Questa
operazione infatti potrebbe risultare del tutto inutile qualora il figlio
padre, effettuando per primo una operazione di scrittura in memoria, attivi il
meccanismo del \itindex{copy~on~write} \textit{copy on write}. Questa
operazione infatti potrebbe risultare del tutto inutile qualora il figlio
invocherebbe un altro programma scartando completamente lo spazio degli
indirizzi, rendendo superflua la copia della memoria modificata dal padre.
invocherebbe un altro programma scartando completamente lo spazio degli
indirizzi, rendendo superflua la copia della memoria modificata dal padre.
opportuno non fare affidamento su questo comportamento, che non si riscontra
in altri Unix e nelle versioni del kernel precedenti a quella indicata.
opportuno non fare affidamento su questo comportamento, che non si riscontra
in altri Unix e nelle versioni del kernel precedenti a quella indicata.
alcun effetto sul valore che le stesse variabili hanno nel processo padre (ed
in eventuali altri processi figli che eseguano lo stesso codice).
alcun effetto sul valore che le stesse variabili hanno nel processo padre (ed
in eventuali altri processi figli che eseguano lo stesso codice).
quello dell'interazione dei vari processi con i file; per illustrarlo meglio
proviamo a redirigere su un file l'output del nostro programma di test, quello
quello dell'interazione dei vari processi con i file; per illustrarlo meglio
proviamo a redirigere su un file l'output del nostro programma di test, quello
\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm]
[piccardi@selidor sources]$ ./forktest 3 > output
[piccardi@selidor sources]$ cat output
\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm]
[piccardi@selidor sources]$ ./forktest 3 > output
[piccardi@selidor sources]$ cat output
in gran dettaglio in cap.~\ref{cha:file_unix_interface} e in
cap.~\ref{cha:files_std_interface}. Qui basta accennare che si sono usate le
funzioni standard della libreria del C che prevedono l'output bufferizzato; e
in gran dettaglio in cap.~\ref{cha:file_unix_interface} e in
cap.~\ref{cha:files_std_interface}. Qui basta accennare che si sono usate le
funzioni standard della libreria del C che prevedono l'output bufferizzato; e
Nel primo esempio allora avevamo che ad ogni chiamata a \func{printf} il
buffer veniva scaricato, e le singole righe erano stampate a video subito dopo
l'esecuzione della \func{printf}. Ma con la redirezione su file la scrittura
Nel primo esempio allora avevamo che ad ogni chiamata a \func{printf} il
buffer veniva scaricato, e le singole righe erano stampate a video subito dopo
l'esecuzione della \func{printf}. Ma con la redirezione su file la scrittura
-non avviene più alla fine di ogni riga e l'output resta nel buffer. Dato che
-ogni figlio riceve una copia della memoria del padre, esso riceverà anche
-quanto c'è nel buffer delle funzioni di I/O, comprese le linee scritte dal
-padre fino allora. Così quando il buffer viene scritto su disco all'uscita del
+non avviene più alla fine di ogni riga e l'output resta nel buffer. Dato che
+ogni figlio riceve una copia della memoria del padre, esso riceverà anche
+quanto c'è nel buffer delle funzioni di I/O, comprese le linee scritte dal
+padre fino allora. Così quando il buffer viene scritto su disco all'uscita del
figlio, troveremo nel file anche tutto quello che il processo padre aveva
scritto prima della sua creazione. E alla fine del file (dato che in questo
caso il padre esce per ultimo) troveremo anche l'output completo del padre.
L'esempio ci mostra un altro aspetto fondamentale dell'interazione con i file,
figlio, troveremo nel file anche tutto quello che il processo padre aveva
scritto prima della sua creazione. E alla fine del file (dato che in questo
caso il padre esce per ultimo) troveremo anche l'output completo del padre.
L'esempio ci mostra un altro aspetto fondamentale dell'interazione con i file,
-Quello che succede è che quando lo standard output del padre viene rediretto
-come si è fatto nell'esempio, lo stesso avviene anche per tutti i figli; la
+Quello che succede è che quando lo standard output del padre viene rediretto
+come si è fatto nell'esempio, lo stesso avviene anche per tutti i figli; la
funzione \func{fork} infatti ha la caratteristica di duplicare nei processi
figli tutti i file descriptor aperti nel processo padre (allo stesso modo in
cui lo fa la funzione \func{dup}, trattata in sez.~\ref{sec:file_dup}), il che
comporta che padre e figli condividono le stesse voci della
\itindex{file~table} \textit{file table} (per la spiegazione di questi termini
funzione \func{fork} infatti ha la caratteristica di duplicare nei processi
figli tutti i file descriptor aperti nel processo padre (allo stesso modo in
cui lo fa la funzione \func{dup}, trattata in sez.~\ref{sec:file_dup}), il che
comporta che padre e figli condividono le stesse voci della
\itindex{file~table} \textit{file table} (per la spiegazione di questi termini
sulla \itindex{file~table} \textit{file table}, e tutti gli altri processi,
che vedono la stessa \itindex{file~table} \textit{file table}, vedranno il
nuovo valore. In questo modo si evita, in casi come quello appena mostrato in
cui diversi processi scrivono sullo stesso file, che l'output successivo di un
sulla \itindex{file~table} \textit{file table}, e tutti gli altri processi,
che vedono la stessa \itindex{file~table} \textit{file table}, vedranno il
nuovo valore. In questo modo si evita, in casi come quello appena mostrato in
cui diversi processi scrivono sullo stesso file, che l'output successivo di un
-l'output viene rediretto, il padre potrà sempre continuare a scrivere in coda
-a quanto scritto dal figlio in maniera automatica; se così non fosse ottenere
+l'output viene rediretto, il padre potrà sempre continuare a scrivere in coda
+a quanto scritto dal figlio in maniera automatica; se così non fosse ottenere
questo comportamento sarebbe estremamente complesso necessitando di una
qualche forma di comunicazione fra i due processi per far riprendere al padre
la scrittura al punto giusto.
questo comportamento sarebbe estremamente complesso necessitando di una
qualche forma di comunicazione fra i due processi per far riprendere al padre
la scrittura al punto giusto.
file senza una qualche forma di sincronizzazione in quanto, come visto anche
con il nostro esempio, le varie scritture risulteranno mescolate fra loro in
file senza una qualche forma di sincronizzazione in quanto, come visto anche
con il nostro esempio, le varie scritture risulteranno mescolate fra loro in
i file dopo una \func{fork} sono sostanzialmente due:
\begin{enumerate*}
\item Il processo padre aspetta la conclusione del figlio. In questo caso non
i file dopo una \func{fork} sono sostanzialmente due:
\begin{enumerate*}
\item Il processo padre aspetta la conclusione del figlio. In questo caso non
\item L'esecuzione di padre e figlio procede indipendentemente. In questo caso
ciascuno dei due processi deve chiudere i file che non gli servono una volta
\item L'esecuzione di padre e figlio procede indipendentemente. In questo caso
ciascuno dei due processi deve chiudere i file che non gli servono una volta
-proprietà; la lista dettagliata delle proprietà che padre e figlio hanno in
-comune dopo l'esecuzione di una \func{fork} è la seguente:
+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 \itindex{close-on-exec}
\textit{close-on-exec} impostati (vedi sez.~\ref{sec:proc_exec} e
\begin{itemize*}
\item i file aperti e gli eventuali flag di \itindex{close-on-exec}
\textit{close-on-exec} impostati (vedi sez.~\ref{sec:proc_exec} e
\item i segmenti di memoria condivisa agganciati al processo (vedi
sez.~\ref{sec:ipc_sysv_shm});
\item i limiti sulle risorse (vedi sez.~\ref{sec:sys_resource_limit});
\item i segmenti di memoria condivisa agganciati al processo (vedi
sez.~\ref{sec:ipc_sysv_shm});
\item i limiti sulle risorse (vedi sez.~\ref{sec:sys_resource_limit});
processore (vedi sez.~\ref{sec:proc_sched_stand},
sez.~\ref{sec:proc_real_time} e sez.~\ref{sec:proc_sched_multiprocess});
\item le variabili di ambiente (vedi sez.~\ref{sec:proc_environ}).
\end{itemize*}
Le differenze fra padre e figlio dopo la \func{fork} invece sono:\footnote{a
processore (vedi sez.~\ref{sec:proc_sched_stand},
sez.~\ref{sec:proc_real_time} e sez.~\ref{sec:proc_sched_multiprocess});
\item le variabili di ambiente (vedi sez.~\ref{sec:proc_environ}).
\end{itemize*}
Le differenze fra padre e figlio dopo la \func{fork} invece sono:\footnote{a
altre sono esplicitamente menzionate dallo standard POSIX.1-2001.}
\begin{itemize*}
\item il valore di ritorno di \func{fork};
altre sono esplicitamente menzionate dallo standard POSIX.1-2001.}
\begin{itemize*}
\item il valore di ritorno di \func{fork};
\item i \textit{lock} sui file (vedi sez.~\ref{sec:file_locking}) e sulla
memoria (vedi sez.~\ref{sec:proc_mem_lock}), che non vengono ereditati dal
figlio;
\item i \textit{lock} sui file (vedi sez.~\ref{sec:file_locking}) e sulla
memoria (vedi sez.~\ref{sec:proc_mem_lock}), che non vengono ereditati dal
figlio;
-\item gli allarmi (vedi sez.~\ref{sec:sig_alarm_abort}) ed i segnali pendenti
- (vedi sez.~\ref{sec:sig_gen_beha}), che per il figlio vengono cancellati.
+\item gli allarmi, i timer (vedi sez.~\ref{sec:sig_alarm_abort}) ed i segnali
+ pendenti (vedi sez.~\ref{sec:sig_gen_beha}), che per il figlio vengono
+ cancellati.
\item le operazioni di I/O asincrono in corso (vedi
sez.~\ref{sec:file_asyncronous_io}) che non vengono ereditate dal figlio;
\item gli aggiustamenti fatti dal padre ai semafori con \func{semop} (vedi
\item le operazioni di I/O asincrono in corso (vedi
sez.~\ref{sec:file_asyncronous_io}) che non vengono ereditate dal figlio;
\item gli aggiustamenti fatti dal padre ai semafori con \func{semop} (vedi
sez.~\ref{sec:file_memory_map}) che non vengono ereditate dal figlio;
\item l'impostazione con \func{prctl} (vedi sez.~\ref{sec:prctl_xxx}) che
notifica al figlio la terminazione del padre viene cancellata;
sez.~\ref{sec:file_memory_map}) che non vengono ereditate dal figlio;
\item l'impostazione con \func{prctl} (vedi sez.~\ref{sec:prctl_xxx}) che
notifica al figlio la terminazione del padre viene cancellata;
-Una seconda funzione storica usata per la creazione di un nuovo processo è
-\func{vfork}, che è esattamente identica a \func{fork} ed ha la stessa
-semantica e gli stessi errori; la sola differenza è che non viene creata la
-tabella delle pagine né la struttura dei task per il nuovo processo. Il
-processo padre è posto in attesa fintanto che il figlio non ha eseguito una
-\func{execve} o non è uscito con una \func{\_exit}. Il figlio condivide la
+Una seconda funzione storica usata per la creazione di un nuovo processo è
+\func{vfork}, che è esattamente identica a \func{fork} ed ha la stessa
+semantica e gli stessi errori; la sola differenza è che non viene creata la
+tabella delle pagine né la struttura dei task per il nuovo processo. Il
+processo padre è posto in attesa fintanto che il figlio non ha eseguito una
+\func{execve} o non è uscito con una \func{\_exit}. Il figlio condivide la
memoria del padre (e modifiche possono avere effetti imprevedibili) e non deve
ritornare o uscire con \func{exit} ma usare esplicitamente \func{\_exit}.
memoria del padre (e modifiche possono avere effetti imprevedibili) e non deve
ritornare o uscire con \func{exit} ma usare esplicitamente \func{\_exit}.
\func{fork} comportava anche la copia completa del segmento dati del processo
padre, che costituiva un inutile appesantimento in tutti quei casi in cui la
\func{fork} veniva fatta solo per poi eseguire una \func{exec}. La funzione
venne introdotta in BSD per migliorare le prestazioni.
Dato che Linux supporta il \itindex{copy~on~write} \textit{copy on write} la
\func{fork} comportava anche la copia completa del segmento dati del processo
padre, che costituiva un inutile appesantimento in tutti quei casi in cui la
\func{fork} veniva fatta solo per poi eseguire una \func{exec}. La funzione
venne introdotta in BSD per migliorare le prestazioni.
Dato che Linux supporta il \itindex{copy~on~write} \textit{copy on write} la
questo eviteremo di trattarla ulteriormente.
\subsection{La conclusione di un processo}
\label{sec:proc_termination}
questo eviteremo di trattarla ulteriormente.
\subsection{La conclusione di un processo}
\label{sec:proc_termination}
chiudere un programma, ma dall'interno del programma stesso; avendo a che fare
con un sistema multitasking resta da affrontare l'argomento dal punto di vista
di come il sistema gestisce la conclusione dei processi.
chiudere un programma, ma dall'interno del programma stesso; avendo a che fare
con un sistema multitasking resta da affrontare l'argomento dal punto di vista
di come il sistema gestisce la conclusione dei processi.
programma viene terminato in maniera normale: la chiamata di \func{exit} (che
esegue le funzioni registrate per l'uscita e chiude gli stream), il ritorno
dalla funzione \func{main} (equivalente alla chiamata di \func{exit}), e la
programma viene terminato in maniera normale: la chiamata di \func{exit} (che
esegue le funzioni registrate per l'uscita e chiude gli stream), il ritorno
dalla funzione \func{main} (equivalente alla chiamata di \func{exit}), e la
terminazione del processo da parte del kernel).
Ma abbiamo accennato che oltre alla conclusione normale esistono anche delle
terminazione del processo da parte del kernel).
Ma abbiamo accennato che oltre alla conclusione normale esistono anche delle
chiamare la funzione \func{abort} per invocare una chiusura anomala, o essere
terminato da un segnale (torneremo sui segnali in cap.~\ref{cha:signals}). In
chiamare la funzione \func{abort} per invocare una chiusura anomala, o essere
terminato da un segnale (torneremo sui segnali in cap.~\ref{cha:signals}). In
-memoria che stava usando, e così via; l'elenco completo delle operazioni
-eseguite alla chiusura di un processo è il seguente:
+memoria che stava usando, e così via; l'elenco completo delle operazioni
+eseguite alla chiusura di un processo è il seguente:
\begin{itemize*}
\item tutti i file descriptor sono chiusi;
\item viene memorizzato lo stato di terminazione del processo;
\begin{itemize*}
\item tutti i file descriptor sono chiusi;
\item viene memorizzato lo stato di terminazione del processo;
-\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
+\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 \textit{foreground} e il terminale di controllo viene
disconnesso (vedi sez.~\ref{sec:sess_ctrl_term});
\item se la conclusione di un processo rende orfano un \textit{process
processi del gruppo di \textit{foreground} e il terminale di controllo viene
disconnesso (vedi sez.~\ref{sec:sess_ctrl_term});
\item se la conclusione di un processo rende orfano un \textit{process
-Oltre queste operazioni è però necessario poter disporre di un meccanismo
-ulteriore che consenta di sapere come la terminazione è avvenuta: dato che in
+Oltre queste operazioni è però necessario poter disporre di un meccanismo
+ulteriore che consenta di sapere come la terminazione è avvenuta: dato che in
un sistema unix-like tutto viene gestito attraverso i processi, il meccanismo
scelto consiste nel riportare lo stato di terminazione (il cosiddetto
\textit{termination status}) al processo padre.
Nel caso di conclusione normale, abbiamo visto in
sez.~\ref{sec:proc_conclusion} che lo stato di uscita del processo viene
un sistema unix-like tutto viene gestito attraverso i processi, il meccanismo
scelto consiste nel riportare lo stato di terminazione (il cosiddetto
\textit{termination status}) al processo padre.
Nel caso di conclusione normale, abbiamo visto in
sez.~\ref{sec:proc_conclusion} che lo stato di uscita del processo viene
valore passato alle funzioni \func{exit} o \func{\_exit} (o dal valore di
ritorno per \func{main}). Ma se il processo viene concluso in maniera anomala
valore passato alle funzioni \func{exit} o \func{\_exit} (o dal valore di
ritorno per \func{main}). Ma se il processo viene concluso in maniera anomala
che deve generare autonomamente il \textit{termination status} per indicare le
ragioni della conclusione anomala.
Si noti la distinzione fra \textit{exit status} e \textit{termination status}:
quello che contraddistingue lo stato di chiusura del processo e viene
riportato attraverso le funzioni \func{wait} o \func{waitpid} (vedi
che deve generare autonomamente il \textit{termination status} per indicare le
ragioni della conclusione anomala.
Si noti la distinzione fra \textit{exit status} e \textit{termination status}:
quello che contraddistingue lo stato di chiusura del processo e viene
riportato attraverso le funzioni \func{wait} o \func{waitpid} (vedi
il kernel usa il primo (nel codice eseguito da \func{\_exit}) per produrre il
secondo.
La scelta di riportare al padre lo stato di terminazione dei figli, pur
essendo l'unica possibile, comporta comunque alcune complicazioni: infatti se
il kernel usa il primo (nel codice eseguito da \func{\_exit}) per produrre il
secondo.
La scelta di riportare al padre lo stato di terminazione dei figli, pur
essendo l'unica possibile, comporta comunque alcune complicazioni: infatti se
-alla sua creazione è scontato che ogni nuovo processo ha un padre, non è detto
-che sia così alla sua conclusione, dato che il padre potrebbe essere già
-terminato; si potrebbe avere cioè quello che si chiama un processo
+alla sua creazione è scontato che ogni nuovo processo ha un padre, non è detto
+che sia così alla sua conclusione, dato che il padre potrebbe essere già
+terminato; si potrebbe avere cioè quello che si chiama un processo
-venga \textsl{adottato} da \cmd{init}. Come già accennato quando un processo
-termina, il kernel controlla se è il padre di altri processi in esecuzione: in
+venga \textsl{adottato} da \cmd{init}. Come già accennato quando un processo
+termina, il kernel controlla se è il padre di altri processi in esecuzione: in
-con il \acr{pid} di \cmd{init} (e cioè con 1); in questo modo ogni processo
-avrà sempre un padre (nel caso possiamo parlare di un padre \textsl{adottivo})
+con il \acr{pid} di \cmd{init} (e cioè con 1); in questo modo ogni processo
+avrà sempre un padre (nel caso possiamo parlare di un padre \textsl{adottivo})
cui riportare il suo stato di terminazione. Come verifica di questo
comportamento possiamo eseguire il nostro programma \cmd{forktest} imponendo a
cui riportare il suo stato di terminazione. Come verifica di questo
comportamento possiamo eseguire il nostro programma \cmd{forktest} imponendo a
\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm]
[piccardi@selidor sources]$ ./forktest -c2 3
Process 1972: forking 3 child
\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm]
[piccardi@selidor sources]$ ./forktest -c2 3
Process 1972: forking 3 child
Child 2, parent 1, exiting
Child 1, parent 1, exiting
\end{Verbatim}
Child 2, parent 1, exiting
Child 1, parent 1, exiting
\end{Verbatim}
figli, tornando alla shell, che stampa il prompt sul terminale: circa due
secondi dopo viene stampato a video anche l'output dei tre figli che
figli, tornando alla shell, che stampa il prompt sul terminale: circa due
secondi dopo viene stampato a video anche l'output dei tre figli che
-Altrettanto rilevante è il caso in cui il figlio termina prima del padre,
-perché non è detto che il padre possa ricevere immediatamente lo stato di
-terminazione, quindi il kernel deve comunque conservare una certa quantità di
+Altrettanto rilevante è il caso in cui il figlio termina prima del padre,
+perché non è detto che il padre possa ricevere immediatamente lo stato di
+terminazione, quindi il kernel deve comunque conservare una certa quantità di
informazioni riguardo ai processi che sta terminando.
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 sez.~\ref{sec:sys_unix_time}) e lo stato di terminazione,
mentre la memoria in uso ed i file aperti vengono rilasciati immediatamente. I
informazioni riguardo ai processi che sta terminando.
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 sez.~\ref{sec:sys_unix_time}) e lo stato di terminazione,
mentre la memoria in uso ed i file aperti vengono rilasciati immediatamente. I
ancora ricevuto dal padre sono chiamati \index{zombie} \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 (vedi tab.~\ref{tab:proc_proc_states}). Quando
ancora ricevuto dal padre sono chiamati \index{zombie} \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 (vedi tab.~\ref{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
+il padre effettuerà la lettura dello stato di uscita anche questa
+informazione, non più necessaria, verrà scartata e la terminazione potrà dirsi
stato di terminazione, i tre processi figli sono ancora presenti pur essendosi
conclusi, con lo stato di \index{zombie} \textit{zombie} e l'indicazione che
sono stati terminati.
stato di terminazione, i tre processi figli sono ancora presenti pur essendosi
conclusi, con lo stato di \index{zombie} \textit{zombie} e l'indicazione che
sono stati terminati.
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 sez.~\ref{sec:sig_sigchld} e
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 sez.~\ref{sec:sig_sigchld} e
\index{zombie} \textit{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
\index{zombie} \textit{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 \index{zombie} \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
+diviene uno \index{zombie} \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 \index{zombie} \textit{zombie}:
alla sua terminazione infatti tutti i suoi figli (compresi gli \index{zombie}
avviene anche quando, come nel caso del precedente esempio con \cmd{forktest},
il padre termina con dei figli in stato di \index{zombie} \textit{zombie}:
alla sua terminazione infatti tutti i suoi figli (compresi gli \index{zombie}
completarne la terminazione.
Si tenga presente infine che siccome gli \index{zombie} \textit{zombie} sono
completarne la terminazione.
Si tenga presente infine che siccome gli \index{zombie} \textit{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
+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.
terminare il processo che li ha generati, in modo che \cmd{init} possa
adottarli e provvedere a concluderne la terminazione.
consiste nella creazione di programmi di tipo server, in cui un processo
principale attende le richieste che vengono poi soddisfatte da una serie di
consiste nella creazione di programmi di tipo server, in cui un processo
principale attende le richieste che vengono poi soddisfatte da una serie di
caso diventi necessario gestire esplicitamente la conclusione dei figli onde
evitare di riempire di \index{zombie} \textit{zombie} la tabella dei processi;
le funzioni deputate a questo compito sono principalmente due, \funcd{wait} e
caso diventi necessario gestire esplicitamente la conclusione dei figli onde
evitare di riempire di \index{zombie} \textit{zombie} la tabella dei processi;
le funzioni deputate a questo compito sono principalmente due, \funcd{wait} e
segnale termina il processo o chiama una funzione di gestione.
\bodydesc{La funzione restituisce il \acr{pid} del figlio in caso di successo
segnale termina il processo o chiama una funzione di gestione.
\bodydesc{La funzione restituisce il \acr{pid} del figlio in caso di successo
-è 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, se più di un figlio è terminato occorre chiamare la funzione
-più volte se si vuole recuperare lo stato di terminazione di tutti quanti.
+è 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, 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 sez.~\ref{sec:proc_termination}) vengono rilasciate.
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 sez.~\ref{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.
+Nel caso un processo abbia più figli il valore di ritorno (il \acr{pid} del
+figlio) permette di identificare qual è quello che è uscito.
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
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
-sez.~\ref{sec:sess_job_control}). Dato che è possibile ottenere lo stesso
+funzionalità più ampie, legate anche al controllo di sessione (si veda
+sez.~\ref{sec:sess_job_control}). Dato che è possibile ottenere lo stesso
\begin{functions}
\headdecl{sys/types.h}
\headdecl{sys/wait.h}
\funcdecl{pid\_t waitpid(pid\_t pid, int *status, int options)}
Attende la conclusione di un processo figlio.
\begin{functions}
\headdecl{sys/types.h}
\headdecl{sys/wait.h}
\funcdecl{pid\_t waitpid(pid\_t pid, int *status, int options)}
Attende la conclusione di un processo figlio.
-\bodydesc{La funzione restituisce il \acr{pid} del processo che è uscito, 0 se
- è stata specificata l'opzione \const{WNOHANG} e il processo non è uscito e
- -1 per un errore, nel qual caso \var{errno} assumerà i valori:
+\bodydesc{La funzione restituisce il \acr{pid} del processo che è uscito, 0 se
+ è stata specificata l'opzione \const{WNOHANG} e il processo non è uscito e
+ -1 per un errore, nel qual caso \var{errno} assumerà i valori:
- \item[\errcode{EINTR}] non è stata specificata l'opzione \const{WNOHANG} e
- la funzione è stata interrotta da un segnale.
+ \item[\errcode{EINTR}] non è stata specificata l'opzione \const{WNOHANG} e
+ la funzione è stata interrotta da un segnale.
- non è figlio del processo chiamante.
- \item[\errcode{EINVAL}] si è specificato un valore non valido per
+ non è figlio del processo chiamante.
+ \item[\errcode{EINVAL}] si è specificato un valore non valido per
secondo lo specchietto riportato in tab.~\ref{tab:proc_waidpid_pid}, dove si
sono riportate anche le costanti definite per indicare alcuni di essi.
secondo lo specchietto riportato in tab.~\ref{tab:proc_waidpid_pid}, dove si
sono riportate anche le costanti definite per indicare alcuni di essi.
al valore assoluto di \param{pid}. \\
$-1$&\const{WAIT\_ANY} & Attende per un figlio qualsiasi, usata in
questa maniera senza specificare nessuna opzione
al valore assoluto di \param{pid}. \\
$-1$&\const{WAIT\_ANY} & Attende per un figlio qualsiasi, usata in
questa maniera senza specificare nessuna opzione
$ 0$&\const{WAIT\_MYPGRP}&Attende per un figlio il cui
\itindex{process~group} \textit{process group}
$ 0$&\const{WAIT\_MYPGRP}&Attende per un figlio il cui
\itindex{process~group} \textit{process group}
funzione delle opportune opzioni tramite l'argomento \param{options}; questo
deve essere specificato come maschera binaria dei flag riportati nella prima
parte in tab.~\ref{tab:proc_waitpid_options} che possono essere combinati fra
loro con un OR aritmetico. Nella seconda parte della stessa tabella si sono
riportati anche alcuni valori non standard specifici di Linux, che consentono
funzione delle opportune opzioni tramite l'argomento \param{options}; questo
deve essere specificato come maschera binaria dei flag riportati nella prima
parte in tab.~\ref{tab:proc_waitpid_options} che possono essere combinati fra
loro con un OR aritmetico. Nella seconda parte della stessa tabella si sono
riportati anche alcuni valori non standard specifici di Linux, che consentono
\func{clone} (vedi sez.~\ref{sec:process_clone}) usati principalmente per la
gestione della terminazione dei \itindex{thread} \textit{thread} (vedi
sez.~\ref{sec:thread_xxx}).
\func{clone} (vedi sez.~\ref{sec:process_clone}) usati principalmente per la
gestione della terminazione dei \itindex{thread} \textit{thread} (vedi
sez.~\ref{sec:thread_xxx}).
\const{WCONTINUED}& Ritorna anche quando un processo figlio che era stato
fermato ha ripreso l'esecuzione.\footnotemark \\
\hline
\const{WCONTINUED}& Ritorna anche quando un processo figlio che era stato
fermato ha ripreso l'esecuzione.\footnotemark \\
\hline
L'uso dell'opzione \const{WNOHANG} consente di prevenire il blocco della
funzione qualora nessun figlio sia uscito (o non si siano verificate le altre
L'uso dell'opzione \const{WNOHANG} consente di prevenire il blocco della
funzione qualora nessun figlio sia uscito (o non si siano verificate le altre
-condizioni per l'uscita della funzione); in tal caso la funzione ritornerà un
-valore nullo anziché positivo.\footnote{anche in questo caso un valore
- positivo indicherà il \acr{pid} del processo di cui si è ricevuto lo stato
+condizioni per l'uscita della funzione); in tal caso la funzione ritornerà un
+valore nullo anziché positivo.\footnote{anche in questo caso un valore
+ positivo indicherà il \acr{pid} del processo di cui si è ricevuto lo stato
ed un valore negativo un errore.}
Le altre due opzioni \const{WUNTRACED} e \const{WCONTINUED} consentono
ed un valore negativo un errore.}
Le altre due opzioni \const{WUNTRACED} e \const{WCONTINUED} consentono
Nel caso di \const{WUNTRACED} la funzione ritorna, restituendone il \acr{pid},
quando un processo figlio entra nello stato \textit{stopped}\footnote{in
Nel caso di \const{WUNTRACED} la funzione ritorna, restituendone il \acr{pid},
quando un processo figlio entra nello stato \textit{stopped}\footnote{in
sez.~\ref{sec:xxx_ptrace}).} (vedi tab.~\ref{tab:proc_proc_states}), mentre
con \const{WCONTINUED} la funzione ritorna quando un processo in stato
\textit{stopped} riprende l'esecuzione per la ricezione del segnale
sez.~\ref{sec:xxx_ptrace}).} (vedi tab.~\ref{tab:proc_proc_states}), mentre
con \const{WCONTINUED} la funzione ritorna quando un processo in stato
\textit{stopped} riprende l'esecuzione per la ricezione del segnale
-La terminazione di un processo figlio (così come gli altri eventi osservabili
-con \func{waitpid}) è chiaramente un evento asincrono rispetto all'esecuzione
-di un programma e può avvenire in un qualunque momento. Per questo motivo,
+La terminazione di un processo figlio (così come gli altri eventi osservabili
+con \func{waitpid}) è chiaramente un evento asincrono rispetto all'esecuzione
+di un programma e può avvenire in un qualunque momento. Per questo motivo,
-Il comportamento delle funzioni è però cambiato nel passaggio dal kernel 2.4
-al kernel 2.6, quest'ultimo infatti si è adeguato alle prescrizioni dello
+Il comportamento delle funzioni è però cambiato nel passaggio dal kernel 2.4
+al kernel 2.6, quest'ultimo infatti si è adeguato alle prescrizioni dello
standard POSIX.1-2001,\footnote{una revisione del 2001 dello standard POSIX.1
che ha aggiunto dei requisiti e delle nuove funzioni, come \func{waitid}.}
e come da esso richiesto se \const{SIGCHLD} viene ignorato, o se si imposta il
standard POSIX.1-2001,\footnote{una revisione del 2001 dello standard POSIX.1
che ha aggiunto dei requisiti e delle nuove funzioni, come \func{waitid}.}
e come da esso richiesto se \const{SIGCHLD} viene ignorato, o se si imposta il
sez.~\ref{sec:sig_sigaction}) i processi figli che terminano non diventano
\textit{zombie} e sia \func{wait} che \func{waitpid} si bloccano fintanto che
tutti i processi figli non sono terminati, dopo di che falliscono con un
sez.~\ref{sec:sig_sigaction}) i processi figli che terminano non diventano
\textit{zombie} e sia \func{wait} che \func{waitpid} si bloccano fintanto che
tutti i processi figli non sono terminati, dopo di che falliscono con un
opzioni \const{WUNTRACED} e \const{WCONTINUED} sono utilizzabili soltanto
qualora non si sia impostato il flag di \const{SA\_NOCLDSTOP} per il segnale
\const{SIGCHLD}.}
opzioni \const{WUNTRACED} e \const{WCONTINUED} sono utilizzabili soltanto
qualora non si sia impostato il flag di \const{SA\_NOCLDSTOP} per il segnale
\const{SIGCHLD}.}
\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
\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
sez.~\ref{sec:sig_notification}).\\
\macro{WTERMSIG(s)} & Restituisce il numero del segnale che ha causato
sez.~\ref{sec:sig_notification}).\\
\macro{WTERMSIG(s)} & Restituisce il numero del segnale che ha causato
valutata solo se \val{WIFSIGNALED} ha restituito
un valore non nullo.\\
\macro{WCOREDUMP(s)} & Vera se il processo terminato ha generato un
file di \itindex{core~dump} \textit{core
valutata solo se \val{WIFSIGNALED} ha restituito
un valore non nullo.\\
\macro{WCOREDUMP(s)} & Vera se il processo terminato ha generato un
file di \itindex{core~dump} \textit{core
\val{WIFSIGNALED} ha restituito un valore non
nullo.\footnotemark \\
\macro{WIFSTOPPED(s)} & Vera se il processo che ha causato il ritorno di
\val{WIFSIGNALED} ha restituito un valore non
nullo.\footnotemark \\
\macro{WIFSTOPPED(s)} & Vera se il processo che ha causato il ritorno di
con \func{waitpid} avendo specificato l'opzione
\const{WUNTRACED}.\\
\macro{WSTOPSIG(s)} & Restituisce il numero del segnale che ha bloccato
con \func{waitpid} avendo specificato l'opzione
\const{WUNTRACED}.\\
\macro{WSTOPSIG(s)} & Restituisce il numero del segnale che ha bloccato
In generale in un programma non si vuole essere forzati ad attendere la
conclusione di un processo figlio per proseguire l'esecuzione, specie se tutto
questo serve solo per leggerne lo stato di chiusura (ed evitare eventualmente
la presenza di \index{zombie} \textit{zombie}).
In generale in un programma non si vuole essere forzati ad attendere la
conclusione di un processo figlio per proseguire l'esecuzione, specie se tutto
questo serve solo per leggerne lo stato di chiusura (ed evitare eventualmente
la presenza di \index{zombie} \textit{zombie}).
utilizzarle all'interno di un \textit{signal handler} (vedremo un esempio di
come gestire \const{SIGCHLD} con i segnali in sez.~\ref{sec:sig_example}). In
utilizzarle all'interno di un \textit{signal handler} (vedremo un esempio di
come gestire \const{SIGCHLD} con i segnali in sez.~\ref{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{waitpid} non si bloccherà.
+questo caso infatti, dato che il segnale è generato dalla terminazione di un
+figlio, avremo la certezza che la chiamata a \func{waitpid} non si bloccherà.
Come accennato sia \func{wait} che \func{waitpid} restituiscono lo stato di
terminazione del processo tramite il puntatore \param{status} (se non
Come accennato sia \func{wait} che \func{waitpid} restituiscono lo stato di
terminazione del processo tramite il puntatore \param{status} (se non
restituito da entrambe le funzioni dipende dall'implementazione, ma
tradizionalmente alcuni bit (in genere 8) sono riservati per memorizzare lo
stato di uscita, e altri per indicare il segnale che ha causato la
restituito da entrambe le funzioni dipende dall'implementazione, ma
tradizionalmente alcuni bit (in genere 8) sono riservati per memorizzare lo
stato di uscita, e altri per indicare il segnale che ha causato la
generato un \itindex{core~dump} \textit{core dump}, ecc.\footnote{le
definizioni esatte si possono trovare in \file{<bits/waitstatus.h>} ma
questo file non deve mai essere usato direttamente, esso viene incluso
generato un \itindex{core~dump} \textit{core dump}, ecc.\footnote{le
definizioni esatte si possono trovare in \file{<bits/waitstatus.h>} ma
questo file non deve mai essere usato direttamente, esso viene incluso
da \func{wait} o \func{waitpid}.
Si tenga conto che nel caso di conclusione anomala il valore restituito da
da \func{wait} o \func{waitpid}.
Si tenga conto che nel caso di conclusione anomala il valore restituito da
segnali definite in \file{signal.h} ed elencate in
tab.~\ref{tab:sig_signal_list}, e stampato usando le apposite funzioni
trattate in sez.~\ref{sec:sig_strsignal}.
segnali definite in \file{signal.h} ed elencate in
tab.~\ref{tab:sig_signal_list}, e stampato usando le apposite funzioni
trattate in sez.~\ref{sec:sig_strsignal}.
-controllo molto più preciso sui possibili cambiamenti di stato dei processi
-figli e più dettagli sullo stato di uscita; la funzione è \funcd{waitid} ed il
-suo prototipo è:
+controllo molto più preciso sui possibili cambiamenti di stato dei processi
+figli e più dettagli sullo stato di uscita; la funzione è \funcd{waitid} ed il
+suo prototipo è:
Attende la conclusione di un processo figlio.
\bodydesc{La funzione restituisce 0 in caso di successo e -1 per un errore,
Attende la conclusione di un processo figlio.
\bodydesc{La funzione restituisce 0 in caso di successo e -1 per un errore,
- \item[\errcode{EINTR}] se non è stata specificata l'opzione \const{WNOHANG} e
- la funzione è stata interrotta da un segnale.
+ \item[\errcode{EINTR}] se non è stata specificata l'opzione \const{WNOHANG} e
+ la funzione è stata interrotta da un segnale.
- non è figlio del processo chiamante.
- \item[\errcode{EINVAL}] si è specificato un valore non valido per
+ non è figlio del processo chiamante.
+ \item[\errcode{EINVAL}] si è specificato un valore non valido per
Come per \func{waitpid} anche il comportamento di \func{waitid} viene
controllato dall'argomento \param{options}, da specificare come maschera
Come per \func{waitpid} anche il comportamento di \func{waitid} viene
controllato dall'argomento \param{options}, da specificare come maschera
alcuni di questi siano identici come significato ed effetto ai precedenti di
tab.~\ref{tab:proc_waitpid_options}, ci sono delle differenze significative:
alcuni di questi siano identici come significato ed effetto ai precedenti di
tab.~\ref{tab:proc_waitpid_options}, ci sono delle differenze significative:
- \const{WEXITED} & Ritorna quando un processo figlio è terminato.\\
- \const{WNOHANG} & Ritorna immediatamente anche se non c'è niente da
+ \const{WEXITED} & Ritorna quando un processo figlio è terminato.\\
+ \const{WNOHANG} & Ritorna immediatamente anche se non c'è niente da
\const{WCONTINUED}& Ritorna quando un processo figlio che era stato
fermato ha ripreso l'esecuzione.\\
\const{WCONTINUED}& Ritorna quando un processo figlio che era stato
fermato ha ripreso l'esecuzione.\\
-$-1$ in caso di errore; viene restituito un valore nullo anche se è stata
-specificata l'opzione \const{WNOHANG} e la funzione è ritornata immediatamente
+$-1$ in caso di errore; viene restituito un valore nullo anche se è stata
+specificata l'opzione \const{WNOHANG} e la funzione è ritornata immediatamente
senza che nessun figlio sia terminato. Pertanto per verificare il motivo del
ritorno della funzione occorre analizzare le informazioni che essa
restituisce; queste, al contrario delle precedenti \func{wait} e
senza che nessun figlio sia terminato. Pertanto per verificare il motivo del
ritorno della funzione occorre analizzare le informazioni che essa
restituisce; queste, al contrario delle precedenti \func{wait} e
\item[\var{si\_code}] con uno fra \const{CLD\_EXITED}, \const{CLD\_KILLED},
\const{CLD\_STOPPED}, \const{CLD\_CONTINUED}, \const{CLD\_TRAPPED} e
\const{CLD\_DUMPED} a indicare la ragione del ritorno della funzione, il cui
\item[\var{si\_code}] con uno fra \const{CLD\_EXITED}, \const{CLD\_KILLED},
\const{CLD\_STOPPED}, \const{CLD\_CONTINUED}, \const{CLD\_TRAPPED} e
\const{CLD\_DUMPED} a indicare la ragione del ritorno della funzione, il cui
- significato è, nell'ordine: uscita normale, terminazione da segnale,
+ significato è, nell'ordine: uscita normale, terminazione da segnale,
Infine Linux, seguendo un'estensione di BSD, supporta altre due funzioni per
la lettura dello stato di terminazione di un processo, analoghe alle
precedenti ma che prevedono un ulteriore argomento attraverso il quale il
Infine Linux, seguendo un'estensione di BSD, supporta altre due funzioni per
la lettura dello stato di terminazione di un processo, analoghe alle
precedenti ma che prevedono un ulteriore argomento attraverso il quale il
sez.~\ref{sec:sys_res_limits}) 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:
sez.~\ref{sec:sys_res_limits}) 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:
\funcdecl{pid\_t wait4(pid\_t pid, int *status, int options, struct rusage
*rusage)}
\funcdecl{pid\_t wait4(pid\_t pid, int *status, int options, struct rusage
*rusage)}
argomenti, ma restituisce in \param{rusage} un sommario delle risorse usate
dal processo.
\funcdecl{pid\_t wait3(int *status, int options, struct rusage *rusage)}
argomenti, 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)} è
+ Prima versione, equivalente a \code{wait4(-1, \&status, opt, rusage)} è
utilizzata anche dalla funzione \func{getrusage} (vedi
sez.~\ref{sec:sys_resource_use}) per ottenere le risorse di sistema usate da un
utilizzata anche dalla funzione \func{getrusage} (vedi
sez.~\ref{sec:sys_resource_use}) per ottenere le risorse di sistema usate da un
-Abbiamo già detto che una delle modalità principali con cui si utilizzano i
-processi in Unix è quella di usarli per lanciare nuovi programmi: questo viene
+Abbiamo già detto che una delle modalità principali con cui si utilizzano i
+processi in Unix è quella di usarli per lanciare nuovi programmi: questo viene
fatto attraverso una delle funzioni della famiglia \func{exec}. Quando un
processo chiama una di queste funzioni esso viene completamente sostituito dal
nuovo programma; il \acr{pid} del processo non cambia, dato che non viene
fatto attraverso una delle funzioni della famiglia \func{exec}. Quando un
processo chiama una di queste funzioni esso viene completamente sostituito dal
nuovo programma; il \acr{pid} del processo non cambia, dato che non viene
\index{segmento!dati} dati ed il \index{segmento!testo} testo del processo
corrente con un nuovo programma letto da disco.
\index{segmento!dati} dati ed il \index{segmento!testo} testo del processo
corrente con un nuovo programma letto da disco.
-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à
+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à
\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 solo in caso di errore, restituendo -1; nel
\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 solo in caso di errore, restituendo -1; nel
- \item[\errcode{EACCES}] il file non è eseguibile, oppure il filesystem è
- montato in \cmd{noexec}, oppure non è un file regolare o un interprete.
+ \item[\errcode{EACCES}] il file non è eseguibile, oppure il filesystem è
+ montato in \cmd{noexec}, oppure non è un file regolare o un interprete.
- \itindex{sgid~bit} \acr{sgid}, l'utente non è root, il processo viene
- tracciato, o il filesystem è montato con l'opzione \cmd{nosuid}.
- \item[\errcode{ENOEXEC}] il file è in un formato non eseguibile o non
+ \itindex{sgid~bit} \acr{sgid}, l'utente non è root, il processo viene
+ tracciato, o il filesystem è montato con l'opzione \cmd{nosuid}.
+ \item[\errcode{ENOEXEC}] il file è in un formato non eseguibile o non
riconosciuto come tale, o compilato per un'altra architettura.
\item[\errcode{ENOENT}] il file o una delle librerie dinamiche o l'interprete
necessari per eseguirlo non esistono.
riconosciuto come tale, o compilato per un'altra architettura.
\item[\errcode{ENOENT}] il file o una delle librerie dinamiche o l'interprete
necessari per eseguirlo non esistono.
- \item[\errcode{EINVAL}] l'eseguibile ELF ha più di un segmento
- \const{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
\end{errlist}
ed inoltre anche \errval{EFAULT}, \errval{ENOMEM}, \errval{EIO},
\errval{ENAMETOOLONG}, \errval{ELOOP}, \errval{ENOTDIR}, \errval{ENFILE},
\end{errlist}
ed inoltre anche \errval{EFAULT}, \errval{ENOMEM}, \errval{EIO},
\errval{ENAMETOOLONG}, \errval{ELOOP}, \errval{ENOTDIR}, \errval{ENFILE},
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
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
\code{main(int argc, char *argv[], char *envp[])}.
Le altre funzioni della famiglia servono per fornire all'utente una serie di
\code{main(int argc, char *argv[], char *envp[])}.
Le altre funzioni della famiglia servono per fornire all'utente una serie di
linea di comando e l'ambiente ricevuti dal nuovo processo.
\bodydesc{Queste funzioni ritornano solo in caso di errore, restituendo -1;
linea di comando e l'ambiente ricevuti dal nuovo processo.
\bodydesc{Queste funzioni ritornano solo in caso di errore, restituendo -1;
-prima differenza riguarda le modalità di passaggio dei valori che poi andranno
-a costituire gli argomenti a linea di comando (cioè i valori di
+prima differenza riguarda le modalità di passaggio dei valori che poi andranno
+a costituire gli argomenti a linea di comando (cioè i valori di
che stanno rispettivamente per \textit{vector} e \textit{list}. Nel primo caso
gli argomenti sono passati tramite il vettore di puntatori \var{argv[]} a
stringhe terminate con zero che costituiranno gli argomenti a riga di comando,
che stanno rispettivamente per \textit{vector} e \textit{list}. Nel primo caso
gli argomenti sono passati tramite il vettore di puntatori \var{argv[]} a
stringhe terminate con zero che costituiranno gli argomenti a riga di comando,
\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
\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
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 l'argomento \param{file} non
contiene una ``\texttt{/}'' 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
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 l'argomento \param{file} non
contiene una ``\texttt{/}'' 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
+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 \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
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
Con lo mnemonico \texttt{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 \val{NULL}), le altre
Con lo mnemonico \texttt{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 \val{NULL}), le altre
-\func{exec} mantiene la gran parte delle proprietà del processo chiamante; una
-lista delle più significative è la seguente:
+\func{exec} mantiene la gran parte delle proprietà del processo chiamante; una
+lista delle più significative è la seguente:
\item i valori delle variabili \var{tms\_utime}, \var{tms\_stime};
\var{tms\_cutime}, \var{tms\_ustime} (vedi sez.~\ref{sec:sys_cpu_times});
% TODO ===========Importante=============
\item i valori delle variabili \var{tms\_utime}, \var{tms\_stime};
\var{tms\_cutime}, \var{tms\_ustime} (vedi sez.~\ref{sec:sys_cpu_times});
% TODO ===========Importante=============
% TODO ===========Importante=============
\item la maschera dei segnali (si veda sez.~\ref{sec:sig_sigmask}).
\end{itemize*}
% TODO ===========Importante=============
\item la maschera dei segnali (si veda sez.~\ref{sec:sig_sigmask}).
\end{itemize*}
in un programma che esegue un codice completamente diverso in uno spazio di
indirizzi totalmente indipendente e ricreato da zero, vengono perse con
l'esecuzione di \func{exec}; lo standard POSIX.1-2001 prevede che le seguenti
in un programma che esegue un codice completamente diverso in uno spazio di
indirizzi totalmente indipendente e ricreato da zero, vengono perse con
l'esecuzione di \func{exec}; lo standard POSIX.1-2001 prevede che le seguenti
I segnali che sono stati impostati per essere ignorati nel processo chiamante
mantengono la stessa impostazione pure nel nuovo programma, ma tutti gli altri
I segnali che sono stati impostati per essere ignorati nel processo chiamante
mantengono la stessa impostazione pure nel nuovo programma, ma tutti gli altri
che, quando impostato a \const{SIG\_IGN}, potrebbe anche essere reimpostato a
\const{SIG\_DFL}, anche se questo con Linux non avviene.\footnote{lo standard
POSIX.1-2001 prevede che questo comportamento sia deciso dalla singola
che, quando impostato a \const{SIG\_IGN}, potrebbe anche essere reimpostato a
\const{SIG\_DFL}, anche se questo con Linux non avviene.\footnote{lo standard
POSIX.1-2001 prevede che questo comportamento sia deciso dalla singola
caratteristiche specifiche di Linux, che non vengono preservate
nell'esecuzione della funzione \func{exec}, queste sono:
\begin{itemize*}
caratteristiche specifiche di Linux, che non vengono preservate
nell'esecuzione della funzione \func{exec}, queste sono:
\begin{itemize*}
La gestione dei file aperti nel passaggio al nuovo programma lanciato con
\func{exec} dipende dal valore che ha il flag di \itindex{close-on-exec}
\textit{close-on-exec} (vedi anche sez.~\ref{sec:file_fcntl}) per ciascun file
La gestione dei file aperti nel passaggio al nuovo programma lanciato con
\func{exec} dipende dal valore che ha il flag di \itindex{close-on-exec}
\textit{close-on-exec} (vedi anche sez.~\ref{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
+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
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
sez.~\ref{sec:file_dir_read}) che effettua da sola l'impostazione del flag di
\itindex{close-on-exec} \textit{close-on-exec} sulle directory che apre, in
maniera trasparente all'utente.
Il comportamento della funzione in relazione agli identificatori relativi al
sez.~\ref{sec:file_dir_read}) che effettua da sola l'impostazione del flag di
\itindex{close-on-exec} \textit{close-on-exec} sulle directory che apre, in
maniera trasparente all'utente.
Il comportamento della funzione in relazione agli identificatori relativi al
-controllo di accesso verrà trattato in dettaglio in sez.~\ref{sec:proc_perms},
-qui è sufficiente anticipare (si faccia riferimento a
+controllo di accesso verrà trattato in dettaglio in sez.~\ref{sec:proc_perms},
+qui è sufficiente anticipare (si faccia riferimento a
sez.~\ref{sec:proc_access_id} per la definizione di questi identificatori)
come l'\textsl{user-ID reale} ed il \textsl{group-ID reale} restano sempre gli
stessi, mentre l'\textsl{user-ID salvato} ed il \textsl{group-ID salvato}
sez.~\ref{sec:proc_access_id} per la definizione di questi identificatori)
come l'\textsl{user-ID reale} ed il \textsl{group-ID reale} restano sempre gli
stessi, mentre l'\textsl{user-ID salvato} ed il \textsl{group-ID salvato}
condivise, viene lanciato il \textit{linker} dinamico \cmd{/lib/ld.so} prima
del programma per caricare le librerie necessarie ed effettuare il link
condivise, viene lanciato il \textit{linker} dinamico \cmd{/lib/ld.so} prima
del programma per caricare le librerie necessarie ed effettuare il link
collegati con le \acr{libc5}, e \sysfile{/lib/ld-linux.so.2} per programmi
collegati con le \acr{glibc}.
Infine nel caso il file sia uno script esso deve iniziare con una linea nella
forma \cmd{\#!/path/to/interpreter [argomenti]} dove l'interprete indicato
collegati con le \acr{libc5}, e \sysfile{/lib/ld-linux.so.2} per programmi
collegati con le \acr{glibc}.
Infine nel caso il file sia uno script esso deve iniziare con una linea nella
forma \cmd{\#!/path/to/interpreter [argomenti]} dove l'interprete indicato
chiamato come se si fosse eseguito il comando \cmd{interpreter [argomenti]
filename}.\footnote{si tenga presente che con Linux quanto viene scritto
come \texttt{argomenti} viene passato all'interprete come un unico argomento
con una unica stringa di lunghezza massima di 127 caratteri e se questa
dimensione viene ecceduta la stringa viene troncata; altri Unix hanno
dimensioni massime diverse, e diversi comportamenti, ad esempio FreeBSD
chiamato come se si fosse eseguito il comando \cmd{interpreter [argomenti]
filename}.\footnote{si tenga presente che con Linux quanto viene scritto
come \texttt{argomenti} viene passato all'interprete come un unico argomento
con una unica stringa di lunghezza massima di 127 caratteri e se questa
dimensione viene ecceduta la stringa viene troncata; altri Unix hanno
dimensioni massime diverse, e diversi comportamenti, ad esempio FreeBSD
lunga restituisce un errore di \const{ENAMETOOLONG}, una comparazione dei
vari comportamenti si trova su
\href{http://www.in-ulm.de/~mascheck/various/shebang/}
{\textsf{http://www.in-ulm.de/\tild mascheck/various/shebang/}}.}
lunga restituisce un errore di \const{ENAMETOOLONG}, una comparazione dei
vari comportamenti si trova su
\href{http://www.in-ulm.de/~mascheck/various/shebang/}
{\textsf{http://www.in-ulm.de/\tild mascheck/various/shebang/}}.}
basata la gestione dei processi in Unix: con \func{fork} si crea un nuovo
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
basata la gestione dei processi in Unix: con \func{fork} si crea un nuovo
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
\label{sec:proc_access_id}
Come accennato in sez.~\ref{sec:intro_multiuser} il modello base\footnote{in
\label{sec:proc_access_id}
Come accennato in sez.~\ref{sec:intro_multiuser} il modello base\footnote{in
flessibile e controllabile, come le \itindex{capabilities}
\textit{capabilities} illustrate in sez.~\ref{sec:proc_capabilities}, le ACL
per i file (vedi sez.~\ref{sec:file_ACL}) o il
\itindex{Mandatory~Access~Control~(MAC)} \textit{Mandatory Access Control}
di \index{SELinux} SELinux; inoltre basandosi sul lavoro effettuato con
flessibile e controllabile, come le \itindex{capabilities}
\textit{capabilities} illustrate in sez.~\ref{sec:proc_capabilities}, le ACL
per i file (vedi sez.~\ref{sec:file_ACL}) o il
\itindex{Mandatory~Access~Control~(MAC)} \textit{Mandatory Access Control}
di \index{SELinux} SELinux; inoltre basandosi sul lavoro effettuato con
infrastruttura di sicurezza, i \itindex{Linux~Security~Modules}
\textit{Linux Security Modules}, o LSM, in grado di fornire diversi agganci
a livello del kernel per modularizzare tutti i possibili controlli di
infrastruttura di sicurezza, i \itindex{Linux~Security~Modules}
\textit{Linux Security Modules}, o LSM, in grado di fornire diversi agganci
a livello del kernel per modularizzare tutti i possibili controlli di
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
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
appunto tramite un \acr{uid} ed un \acr{gid}) che vengono controllati dal
kernel nella gestione dei permessi di accesso.
appunto tramite un \acr{uid} ed un \acr{gid}) che vengono controllati dal
kernel nella gestione dei permessi di accesso.
-anche poter identificare chi è che ha lanciato un certo programma, e pertanto
-anche a ciascun processo dovrà essere associato un utente e un gruppo.
+anche poter identificare chi è che ha lanciato un certo programma, e pertanto
+anche a ciascun processo dovrà essere associato un utente e un gruppo.
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
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
\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
\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
& Indicano gli ulteriori gruppi cui l'utente appartiene.\\
\hline
-- & \textit{saved} & \textsl{user-ID salvato}
& Indicano gli ulteriori gruppi cui l'utente appartiene.\\
\hline
-- & \textit{saved} & \textsl{user-ID salvato}
\hline
\acr{fsuid} & \textit{filesystem} &\textsl{user-ID di filesystem}
& Indica l'utente effettivo per l'accesso al filesystem. \\
\hline
\acr{fsuid} & \textit{filesystem} &\textsl{user-ID di filesystem}
& Indica l'utente effettivo per l'accesso al filesystem. \\
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
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
completata la procedura di autenticazione, lancia una shell per la quale
imposta questi identificatori ai valori corrispondenti all'utente che entra
nel sistema.
completata la procedura di autenticazione, lancia una shell per la quale
imposta questi identificatori ai valori corrispondenti all'utente che entra
nel sistema.
Questi identificatori normalmente sono identici ai corrispondenti del gruppo
\textit{real} tranne nel caso in cui, come accennato in
Questi identificatori normalmente sono identici ai corrispondenti del gruppo
\textit{real} tranne nel caso in cui, come accennato in
sez.~\ref{sec:file_special_perm}). In questo caso essi saranno impostati
all'utente e al gruppo proprietari del file. Questo consente, per programmi in
sez.~\ref{sec:file_special_perm}). In questo caso essi saranno impostati
all'utente e al gruppo proprietari del file. Questo consente, per programmi in
di un altro (o dell'amministratore).
Come nel caso del \acr{pid} e del \acr{ppid}, anche tutti questi
di un altro (o dell'amministratore).
Come nel caso del \acr{pid} e del \acr{ppid}, anche tutti questi
possibile, per evitare abusi e problemi di sicurezza, per questo occorre anche
un meccanismo che consenta ad un programma di rilasciare gli eventuali
maggiori privilegi necessari, una volta che si siano effettuate le operazioni
possibile, per evitare abusi e problemi di sicurezza, per questo occorre anche
un meccanismo che consenta ad un programma di rilasciare gli eventuali
maggiori privilegi necessari, una volta che si siano effettuate le operazioni
-\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
+\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.
L'\textsl{user-ID salvato} ed il \textsl{group-ID salvato} sono copie
migliorare la sicurezza con NFS.
L'\textsl{user-ID salvato} ed il \textsl{group-ID salvato} sono copie
all'inizio dell'esecuzione di un nuovo programma.
L'\textsl{user-ID di filesystem} e il \textsl{group-ID di filesystem} sono
all'inizio dell'esecuzione di un nuovo programma.
L'\textsl{user-ID di filesystem} e il \textsl{group-ID di filesystem} sono
(torneremo sull'argomento in sez.~\ref{sec:proc_setuid}). 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 sez.~\ref{sec:file_perm_overview}). Ogni
cambiamento effettuato sugli identificatori effettivi viene automaticamente
(torneremo sull'argomento in sez.~\ref{sec:proc_setuid}). 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 sez.~\ref{sec:file_perm_overview}). Ogni
cambiamento effettuato sugli identificatori effettivi viene automaticamente
ignorarne l'esistenza, in quanto saranno del tutto equivalenti ai precedenti.
\subsection{Le funzioni di gestione degli identificatori dei processi}
\label{sec:proc_setuid}
ignorarne l'esistenza, in quanto saranno del tutto equivalenti ai precedenti.
\subsection{Le funzioni di gestione degli identificatori dei processi}
\label{sec:proc_setuid}
utente e gruppo di appartenenza) ad un processo sono rispettivamente
\funcd{setuid} e \funcd{setgid}; come accennato in
sez.~\ref{sec:proc_access_id} in Linux esse seguono la semantica POSIX che
utente e gruppo di appartenenza) ad un processo sono rispettivamente
\funcd{setuid} e \funcd{setgid}; come accennato in
sez.~\ref{sec:proc_access_id} in Linux esse seguono la semantica POSIX che
la prima; la seconda si comporta esattamente allo stesso modo facendo
riferimento al \textsl{group-ID} invece che all'\textsl{user-ID}. Gli
eventuali \textsl{group-ID supplementari} non vengono modificati.
la prima; la seconda si comporta esattamente allo stesso modo facendo
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'\textsl{user-ID effettivo} è zero (cioè è quello dell'amministratore di
+L'effetto della chiamata è diverso a seconda dei privilegi del processo; se
+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
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
consentire ad un programma con i bit \itindex{suid~bit} \acr{suid} o
\itindex{sgid~bit} \acr{sgid} impostati (vedi sez.~\ref{sec:file_special_perm})
di riportare l'\textsl{user-ID effettivo} a quello dell'utente che ha lanciato
consentire ad un programma con i bit \itindex{suid~bit} \acr{suid} o
\itindex{sgid~bit} \acr{sgid} impostati (vedi sez.~\ref{sec:file_special_perm})
di riportare l'\textsl{user-ID effettivo} a quello dell'utente che ha lanciato
Come esempio per chiarire l'uso di queste funzioni prendiamo quello con cui
viene gestito l'accesso al file \sysfile{/var/log/utmp}. In questo file viene
Come esempio per chiarire l'uso di queste funzioni prendiamo quello con cui
viene gestito l'accesso al file \sysfile{/var/log/utmp}. In questo file viene
essere lasciato aperto in scrittura a qualunque utente, che potrebbe
falsificare la registrazione. Per questo motivo questo file (e l'analogo
\sysfile{/var/log/wtmp} su cui vengono registrati login e logout) appartengono
essere lasciato aperto in scrittura a qualunque utente, che potrebbe
falsificare la registrazione. Per questo motivo questo file (e l'analogo
\sysfile{/var/log/wtmp} su cui vengono registrati login e logout) appartengono
il bit \acr{sgid} impostato.
Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato, la
il bit \acr{sgid} impostato.
Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato, la
\begin{eqnarray*}
\label{eq:1}
\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*}
\begin{eqnarray*}
\label{eq:1}
\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 il \textsl{group-ID effettivo} è quello giusto, il
-programma può accedere a \sysfile{/var/log/utmp} in scrittura ed aggiornarlo.
-A questo punto il programma può eseguire una \code{setgid(getgid())} per
+in questo modo, dato che il \textsl{group-ID effettivo} è quello giusto, il
+programma può accedere a \sysfile{/var/log/utmp} in scrittura ed aggiornarlo.
+A questo punto il programma può eseguire una \code{setgid(getgid())} per
-\textsl{group-ID reale} corrisponde la funzione avrà successo), in questo modo
-non sarà possibile lanciare dal terminale programmi che modificano detto file,
+\textsl{group-ID reale} corrisponde la funzione avrà successo), in questo modo
+non sarà possibile lanciare dal terminale programmi che modificano detto file,
\end{eqnarray*}
e ogni processo lanciato dal terminale avrebbe comunque \acr{gid} come
\textsl{group-ID effettivo}. All'uscita dal terminale, per poter di nuovo
\end{eqnarray*}
e ogni processo lanciato dal terminale avrebbe comunque \acr{gid} come
\textsl{group-ID effettivo}. All'uscita dal terminale, per poter di nuovo
-aggiornare lo stato di \sysfile{/var/log/utmp} il programma eseguirà una
-\code{setgid(utmp)} (dove \var{utmp} è il valore numerico associato al gruppo
+aggiornare lo stato di \sysfile{/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 \textsl{group-ID salvato} la
\acr{utmp}, ottenuto ad esempio con una precedente \func{getegid}), dato che
in questo caso il valore richiesto corrisponde al \textsl{group-ID salvato} la
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.
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.
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.
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.
specificati da \param{rgid} e \param{egid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
specificati da \param{rgid} e \param{egid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
detto per la 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
detto per la 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à
+della chiamata; l'amministratore invece può specificare un valore qualunque.
+Specificando un argomento di valore -1 l'identificatore corrispondente verrà
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.
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.
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 pone per l'user-ID salvato: questa funzione deriva da un'implementazione che
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 pone per l'user-ID salvato: questa funzione deriva da un'implementazione che
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
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
\end{functions}
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
\end{functions}
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
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.
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.
\end{functions}
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
\end{functions}
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
lascia inalterato l'identificatore corrispondente.
Per queste funzioni esistono anche due controparti che permettono di leggere
lascia inalterato l'identificatore corrispondente.
Per queste funzioni esistono anche due controparti che permettono di leggere
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
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
\itindex{value~result~argument} \textit{value result argument}). Si noti che
queste funzioni sono le uniche in grado di leggere gli identificatori del
gruppo \textit{saved}.
\itindex{value~result~argument} \textit{value result argument}). Si noti che
queste funzioni sono le uniche in grado di leggere gli identificatori del
gruppo \textit{saved}.
Infine le funzioni \func{setfsuid} e \func{setfsgid} servono per impostare gli
identificatori del gruppo \textit{filesystem} che sono usati da Linux per il
Infine le funzioni \func{setfsuid} e \func{setfsgid} servono per impostare gli
identificatori del gruppo \textit{filesystem} che sono usati da Linux per il
sez.~\ref{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.
sez.~\ref{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
+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
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
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
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
l'utente non possa inviare segnali al server NFS.
Le due funzioni usate per cambiare questi identificatori sono \funcd{setfsuid}
l'utente non possa inviare segnali al server NFS.
Le due funzioni usate per cambiare questi identificatori sono \funcd{setfsuid}
processo corrente a \param{fsgid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
processo corrente a \param{fsgid}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso
\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
\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
\label{sec:proc_setgroups}
Le ultime funzioni che esamineremo sono quelle che permettono di operare sui
\label{sec:proc_setgroups}
Le ultime funzioni che esamineremo sono quelle che permettono di operare sui
sez.~\ref{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
sez.~\ref{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 è:
+processo è \funcd{getgroups}; questa funzione è definita nello standard
+POSIX.1, ed il suo prototipo è:
Legge gli identificatori dei gruppi supplementari.
\bodydesc{La funzione restituisce il numero di gruppi letti in caso di
Legge gli identificatori dei gruppi supplementari.
\bodydesc{La funzione restituisce il numero di gruppi letti in caso di
minore del numero di gruppi supplementari del processo.
\end{errlist}}
\end{functions}
La funzione legge gli identificatori dei gruppi supplementari del processo sul
minore del numero di gruppi supplementari del processo.
\end{errlist}}
\end{functions}
La funzione legge gli identificatori dei gruppi supplementari del processo sul
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.
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 è:
+Una seconda funzione, \funcd{getgrouplist}, può invece essere usata per
+ottenere tutti i gruppi a cui appartiene un certo utente; il suo prototipo è:
La funzione legge i gruppi supplementari dell'utente specificato da
\param{user}, eseguendo una scansione del database dei gruppi (si veda
sez.~\ref{sec:sys_user_group}). Ritorna poi in \param{groups} la lista di
La funzione legge i gruppi supplementari dell'utente specificato da
\param{user}, eseguendo una scansione del database dei gruppi (si veda
sez.~\ref{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
+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
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
Imposta i gruppi supplementari del processo.
\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
Imposta i gruppi supplementari del processo.
\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
\begin{errlist}
\item[\errcode{EFAULT}] \param{list} non ha un indirizzo valido.
\item[\errcode{EPERM}] il processo non ha i privilegi di amministratore.
\begin{errlist}
\item[\errcode{EFAULT}] \param{list} non ha un indirizzo valido.
\item[\errcode{EPERM}] il processo non ha i privilegi di amministratore.
massimo consentito.
\end{errlist}}
\end{functions}
La funzione imposta i gruppi supplementari del processo corrente ai valori
specificati nel vettore passato con l'argomento \param{list}, di dimensioni
massimo consentito.
\end{errlist}}
\end{functions}
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
+date dall'argomento \param{size}. Il numero massimo di gruppi supplementari è
+un parametro di sistema, che può essere ricavato con le modalità spiegate in
sez.~\ref{sec:sys_characteristics}.
Se invece si vogliono impostare i gruppi supplementari del processo a quelli di
sez.~\ref{sec:sys_characteristics}.
Se invece si vogliono impostare i gruppi supplementari del processo a quelli di
Inizializza la lista dei gruppi supplementari.
\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
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} assumerà gli stessi valori di
- \func{setgroups} più \errval{ENOMEM} quando non c'è memoria sufficiente
+ 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
per allocare lo spazio per informazioni dei gruppi.}
\end{functions}
La funzione esegue la scansione del database dei gruppi (usualmente
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
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
+è possibile utilizzarle quando si definisce \macro{\_POSIX\_SOURCE} o si
+compila con il flag \cmd{-ansi}, è pertanto meglio evitarle se si vuole
lo \itindex{scheduler} \textit{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
lo \itindex{scheduler} \textit{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. Tratteremo infine anche le altre priorità dei processi (come quelle
-per l'accesso a disco) divenute disponibili con i kernel più recenti.
+gestione. Tratteremo infine anche le altre priorità dei processi (come quelle
+per l'accesso a disco) divenute disponibili con i kernel più recenti.
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.
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.
cosiddetto \itindex{preemptive~multitasking} \textit{preemptive
multitasking}: questo significa che al contrario di altri sistemi (che usano
invece il cosiddetto \itindex{cooperative~multitasking} \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
sez.~\ref{sec:proc_hierarchy} questa scelta viene eseguita da una sezione
cosiddetto \itindex{preemptive~multitasking} \textit{preemptive
multitasking}: questo significa che al contrario di altri sistemi (che usano
invece il cosiddetto \itindex{cooperative~multitasking} \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
sez.~\ref{sec:proc_hierarchy} questa scelta viene eseguita da una sezione
-La cosa è resa ancora più complicata dal fatto che con le architetture
-multi-processore si deve anche scegliere quale sia la CPU più opportuna da
-utilizzare.\footnote{nei processori moderni la presenza di ampie cache può
+La cosa è resa ancora più complicata dal fatto che con le architetture
+multi-processore si deve anche scegliere quale sia la CPU più opportuna da
+utilizzare.\footnote{nei processori moderni la presenza di ampie cache può
banale.} Tutto questo comunque appartiene alle sottigliezze
dell'implementazione del kernel; dal punto di vista dei programmi che girano
banale.} Tutto questo comunque appartiene alle sottigliezze
dell'implementazione del kernel; dal punto di vista dei programmi che girano
eseguiti davvero in contemporanea), le politiche di scheduling riguardano
semplicemente l'allocazione della risorsa \textsl{tempo di esecuzione}, la cui
eseguiti davvero in contemporanea), le politiche di scheduling riguardano
semplicemente l'allocazione della risorsa \textsl{tempo di esecuzione}, la cui
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
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
-Tutte queste possibilità sono caratterizzate da un diverso \textsl{stato} del
-processo, in Linux un processo può trovarsi in uno degli stati riportati in
+Tutte queste possibilità sono caratterizzate da un diverso \textsl{stato} del
+processo, in Linux un processo può trovarsi in uno degli stati riportati in
tab.~\ref{tab:proc_proc_states}; ma soltanto i processi che sono nello stato
\textbf{Runnable} concorrono per l'esecuzione. Questo vuol dire che, qualunque
tab.~\ref{tab:proc_proc_states}; ma soltanto i processi che sono nello stato
\textbf{Runnable} concorrono per l'esecuzione. Questo vuol dire che, qualunque
- \textbf{Runnable}& \texttt{R} & Il processo è in esecuzione o è pronto ad
- essere eseguito (cioè è in attesa che gli
+ \textbf{Runnable}& \texttt{R} & Il processo è in esecuzione o è pronto ad
+ essere eseguito (cioè è in attesa che gli
- \textbf{Sleep} & \texttt{S} & Il processo è in attesa di un
- risposta dal sistema, ma può essere
+ \textbf{Sleep} & \texttt{S} & Il processo è in attesa di un
+ risposta dal sistema, ma può essere
- \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
+ \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.\\
\textbf{Killable}& \texttt{D} & Un nuovo stato introdotto con il kernel
2.6.25, sostanzialmente identico
all'\textbf{Uninterrutible Sleep} con la
stato letto dal padre.\\
\textbf{Killable}& \texttt{D} & Un nuovo stato introdotto con il kernel
2.6.25, sostanzialmente identico
all'\textbf{Uninterrutible Sleep} con la
-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
+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
abbia risultati significativi in termini di prestazioni.
Il meccanismo tradizionale di scheduling di Unix (che tratteremo in
-sez.~\ref{sec:proc_sched_stand}) è sempre stato basato su delle
-\textsl{priorità dinamiche}, in modo da assicurare che tutti i processi, anche
+sez.~\ref{sec:proc_sched_stand}) è sempre stato basato su delle
+\textsl{priorità dinamiche}, in modo da assicurare che tutti i processi, anche
-un processo ottiene la CPU la sua priorità viene diminuita. In questo modo
-alla fine, anche un processo con priorità iniziale molto bassa, finisce per
-avere una priorità sufficiente per essere eseguito.
+un processo ottiene la CPU la sua priorità viene diminuita. In questo modo
+alla fine, anche un processo con priorità iniziale molto bassa, finisce per
+avere una priorità sufficiente per essere eseguito.
-Lo standard POSIX.1b però ha introdotto il concetto di \textsl{priorità
- assoluta}, (chiamata anche \textsl{priorità statica}, in contrapposizione
-alla normale priorità dinamica), per tenere conto dei sistemi
+Lo standard POSIX.1b però ha introdotto il concetto di \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 un tempo ben determinato; in genere si tende a
real-time,\footnote{per sistema real-time si intende un sistema in grado di
eseguire operazioni in un tempo ben determinato; in genere si tende a
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
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
-Il concetto di priorità assoluta dice che quando due processi si contendono
-l'esecuzione, vince sempre quello con la priorità assoluta più alta.
+Il concetto di priorità assoluta dice che quando due processi si contendono
+l'esecuzione, vince sempre quello con la priorità assoluta più alta.
-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
+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
-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 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à
+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 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à
A meno che non si abbiano esigenze specifiche,\footnote{per alcune delle quali
sono state introdotte delle varianti specifiche.} l'unico meccanismo di
A meno che non si abbiano esigenze specifiche,\footnote{per alcune delle quali
sono state introdotte delle varianti 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
+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
-una priorità assoluta nulla; quello che determina quale, fra tutti i processi
-in attesa di esecuzione, sarà eseguito per primo, è la cosiddetta
-\textsl{priorità dinamica},\footnote{quella che viene mostrata nella colonna
- \texttt{PR} del comando \texttt{top}.} che è chiamata così proprio perché
+una priorità assoluta nulla; quello che determina quale, fra tutti i processi
+in attesa di esecuzione, sarà eseguito per primo, è la cosiddetta
+\textsl{priorità dinamica},\footnote{quella che viene mostrata nella colonna
+ \texttt{PR} del comando \texttt{top}.} che è chiamata così proprio perché
- ed è stata anche introdotta la possibilità di usare diversi algoritmi,
- selezionabili sia in fase di compilazione, che, nelle versioni più recenti,
- all'avvio (addirittura è stato ideato un sistema modulare che permette di
- cambiare lo scheduler a sistema attivo).} ma a grandi linee si può dire che
-ad ogni processo è assegnata una \textit{time-slice}, cioè un intervallo di
+ ed è stata anche introdotta la possibilità di usare diversi algoritmi,
+ selezionabili sia in fase di compilazione, che, nelle versioni più recenti,
+ all'avvio (addirittura è stato ideato un sistema modulare che permette di
+ cambiare lo scheduler a sistema attivo).} ma a grandi linee si può dire che
+ad ogni processo è assegnata una \textit{time-slice}, cioè un intervallo di
-\textsl{diminuito} tutte le volte che un processo è in stato \textbf{Runnable}
-ma non viene posto in esecuzione.\footnote{in realtà il calcolo della priorità
+\textsl{diminuito} tutte le volte che un processo è in stato \textbf{Runnable}
+ma non viene posto in esecuzione.\footnote{in realtà il calcolo della priorità
- avviene con un algoritmo molto più complicato, che tiene conto anche della
- \textsl{interattività} del processo, utilizzando diversi fattori, questa è
+ avviene con un algoritmo molto più complicato, che tiene conto anche della
+ \textsl{interattività} del processo, utilizzando diversi fattori, questa è
funzionamento dello scheduler si legga il quarto capitolo di
\cite{LinKernDev}.} Lo scheduler infatti mette sempre in esecuzione, fra
tutti i processi in stato \textbf{Runnable}, quello che ha il valore di
funzionamento dello scheduler si legga il quarto capitolo di
\cite{LinKernDev}.} Lo scheduler infatti mette sempre in esecuzione, fra
tutti i processi in stato \textbf{Runnable}, quello che ha il valore di
-priorità dinamica più basso.\footnote{con le priorità dinamiche il significato
- del valore numerico ad esse associato è infatti invertito, un valore più
- basso significa una priorità maggiore.} Il fatto che questo valore venga
+priorità dinamica più basso.\footnote{con le priorità dinamiche il significato
+ del valore numerico ad esse associato è infatti invertito, un valore più
+ basso significa una priorità maggiore.} Il fatto che questo valore venga
-significa che la priorità dei processi che non ottengono l'uso del processore
-viene progressivamente incrementata, così che anche questi alla fine hanno la
-possibilità di essere eseguiti.
+significa che la priorità dei processi che non ottengono l'uso del processore
+viene progressivamente incrementata, così che anche questi alla fine hanno la
+possibilità di essere eseguiti.
-priorità dinamica sono determinate dalla cosiddetta \textit{nice} (o
-\textit{niceness}) del processo.\footnote{questa è una delle tante proprietà
+priorità dinamica sono determinate dalla cosiddetta \textit{nice} (o
+\textit{niceness}) del processo.\footnote{questa è una delle tante proprietà
che ciascun processo si porta dietro, essa viene ereditata dai processi
figli e mantenuta attraverso una \func{exec}; fino alla serie 2.4 essa era
mantenuta nell'omonimo campo \texttt{nice} della \texttt{task\_struct}, con
la riscrittura dello scheduler eseguita nel 2.6 viene mantenuta nel campo
che ciascun processo si porta dietro, essa viene ereditata dai processi
figli e mantenuta attraverso una \func{exec}; fino alla serie 2.4 essa era
mantenuta nell'omonimo campo \texttt{nice} della \texttt{task\_struct}, con
la riscrittura dello scheduler eseguita nel 2.6 viene mantenuta nel campo
-valore di \var{nice} nullo e nessuno è privilegiato rispetto agli altri;
-specificando un valore positivo si avrà una \textit{time-slice} più breve ed
-un valore di priorità dinamica iniziale più alto, mentre un valore negativo
-darà una \textit{time-slice} più lunga ed un valore di priorità dinamica
-iniziale più basso.
+valore di \var{nice} nullo e nessuno è privilegiato rispetto agli altri;
+specificando un valore positivo si avrà una \textit{time-slice} più breve ed
+un valore di priorità dinamica iniziale più alto, mentre un valore negativo
+darà una \textit{time-slice} più lunga ed un valore di priorità dinamica
+iniziale più basso.
-un processo; la più semplice è funzione \funcd{nice}, che opera sul processo
-corrente, il suo prototipo è:
+un processo; la più semplice è funzione \funcd{nice}, che opera sul processo
+corrente, il suo prototipo è:
\begin{prototype}{unistd.h}
{int nice(int inc)}
Aumenta il valore di \var{nice} per il processo corrente.
\bodydesc{La funzione ritorna zero o il nuovo valore di \var{nice} in caso
\begin{prototype}{unistd.h}
{int nice(int inc)}
Aumenta il valore di \var{nice} per il processo corrente.
\bodydesc{La funzione ritorna zero o il nuovo valore di \var{nice} in caso
-$19$,\footnote{in realtà l'intervallo varia a seconda delle versioni di
- kernel, ed è questo a partire dal kernel 1.3.43, anche se oggi si può avere
- anche l'intervallo fra $-20$ e $20$.} ma per \param{inc} si può specificare
-un valore qualunque, positivo o negativo, ed il sistema provvederà a troncare
+$19$,\footnote{in realtà l'intervallo varia a seconda delle versioni di
+ kernel, ed è questo a partire dal kernel 1.3.43, anche se oggi si può avere
+ anche l'intervallo fra $-20$ e $20$.} ma per \param{inc} si può specificare
+un valore qualunque, positivo o negativo, ed il sistema provvederà a troncare
-\textit{cortesia} e cioè una diminuzione della priorità, valori negativi
-comportano invece un aumento della priorità. Con i kernel precedenti il
+\textit{cortesia} e cioè una diminuzione della priorità, valori negativi
+comportano invece un aumento della priorità. Con i kernel precedenti il
2.6.12 solo l'amministratore\footnote{o un processo con la
\itindex{capabilities} \textit{capability} \const{CAP\_SYS\_NICE}, vedi
2.6.12 solo l'amministratore\footnote{o un processo con la
\itindex{capabilities} \textit{capability} \const{CAP\_SYS\_NICE}, vedi
- sez.~\ref{sec:proc_capabilities}.} può specificare valori negativi
-di \param{inc} che permettono di aumentare la priorità di un processo, a
-partire da questa versione è consentito anche agli utenti normali alzare
-(entro certi limiti, che vedremo più avanti) la priorità dei propri processi.
+ sez.~\ref{sec:proc_capabilities}.} può specificare valori negativi
+di \param{inc} che permettono di aumentare la priorità di un processo, a
+partire da questa versione è consentito anche agli utenti normali alzare
+(entro certi limiti, che vedremo più avanti) la priorità dei propri processi.
Gli standard SUSv2 e POSIX.1 prevedono che la funzione ritorni il nuovo valore
di \var{nice} del processo; tuttavia la system call di Linux non segue questa
convenzione e restituisce sempre 0 in caso di successo e $-1$ in caso di
Gli standard SUSv2 e POSIX.1 prevedono che la funzione ritorni il nuovo valore
di \var{nice} del processo; tuttavia la system call di Linux non segue questa
convenzione e restituisce sempre 0 in caso di successo e $-1$ in caso di
comporta una confusione con una eventuale condizione di errore. La system call
originaria inoltre non consente, se non dotati di adeguati privilegi, di
diminuire un valore di \var{nice} precedentemente innalzato.
comporta una confusione con una eventuale condizione di errore. La system call
originaria inoltre non consente, se non dotati di adeguati privilegi, di
diminuire un valore di \var{nice} precedentemente innalzato.
Fino alle \acr{glibc} 2.2.4 la funzione di libreria riportava direttamente il
risultato dalla system call, violando lo standard, per cui per ottenere il
nuovo valore occorreva una successiva chiamata alla funzione
Fino alle \acr{glibc} 2.2.4 la funzione di libreria riportava direttamente il
risultato dalla system call, violando lo standard, per cui per ottenere il
nuovo valore occorreva una successiva chiamata alla funzione
-\func{getpriority}. A partire dalla \acr{glibc} 2.2.4 \func{nice} è stata
-reimplementata e non viene più chiamata la omonima system call, con questa
+\func{getpriority}. A partire dalla \acr{glibc} 2.2.4 \func{nice} è stata
+reimplementata e non viene più chiamata la omonima system call, con questa
versione viene restituito come valore di ritorno il valore di \var{nice}, come
richiesto dallo standard.\footnote{questo viene fatto chiamando al suo interno
\func{setpriority}, che tratteremo a breve.} In questo caso l'unico modo
versione viene restituito come valore di ritorno il valore di \var{nice}, come
richiesto dallo standard.\footnote{questo viene fatto chiamando al suo interno
\func{setpriority}, che tratteremo a breve.} In questo caso l'unico modo
azzerare \var{errno} prima della chiamata della funzione e verificarne il
valore quando \func{nice} restituisce $-1$.
Per leggere il valore di \textit{nice} di un processo occorre usare la
azzerare \var{errno} prima della chiamata della funzione e verificarne il
valore quando \func{nice} restituisce $-1$.
Per leggere il valore di \textit{nice} di un processo occorre usare la
\begin{prototype}{sys/resource.h}
{int getpriority(int which, int who)}
Restituisce il valore di \var{nice} per l'insieme dei processi specificati.
\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:
+ \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:
-\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à.
+\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à.
sez.~\ref{sec:sess_proc_group}) o di un utente, specificando un corrispondente
valore per \param{who} secondo la legenda di tab.~\ref{tab:proc_getpriority};
un valore nullo di quest'ultimo indica il processo, il gruppo di processi o
sez.~\ref{sec:sess_proc_group}) o di un utente, specificando un corrispondente
valore per \param{who} secondo la legenda di tab.~\ref{tab:proc_getpriority};
un valore nullo di quest'ultimo indica il processo, il gruppo di processi o
-La funzione restituisce la priorità più alta (cioè il valore più basso) fra
-quelle dei processi specificati; di nuovo, dato che $-1$ è un valore
-possibile, per poter rilevare una condizione di errore è necessario cancellare
+La funzione restituisce la priorità più alta (cioè il valore più basso) fra
+quelle dei processi specificati; di nuovo, dato che $-1$ è un valore
+possibile, per poter rilevare una condizione di errore è necessario cancellare
-Analoga a \func{getpriority} è la funzione \funcd{setpriority} che permette di
-impostare la priorità di uno o più processi; il suo prototipo è:
+Analoga a \func{getpriority} è la funzione \funcd{setpriority} che 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)}
\begin{prototype}{sys/resource.h}
{int setpriority(int which, int who, int prio)}
- \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:
+ \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore,
+ nel qual caso \var{errno} può assumere i valori:
- \item[\errcode{EINVAL}] il valore di \param{which} non è valido.
- \item[\errcode{EACCES}] si è richiesto un aumento di priorità senza avere
+ \item[\errcode{EINVAL}] il valore di \param{which} non è valido.
+ \item[\errcode{EACCES}] si è richiesto un aumento di priorità senza avere
tutti i processi indicati dagli argomenti \param{which} e \param{who}. In
questo caso come valore di \param{prio} deve essere specificato il valore di
\textit{nice} da assegnare, e non un incremento (positivo o negativo) come nel
tutti i processi indicati dagli argomenti \param{which} e \param{who}. In
questo caso come valore di \param{prio} deve essere specificato il valore di
\textit{nice} da assegnare, e non un incremento (positivo o negativo) come nel
processo con la \itindex{capabilities} \textit{capability}
\const{CAP\_SYS\_NICE}, vedi sez.~\ref{sec:proc_capabilities}.} ha la
processo con la \itindex{capabilities} \textit{capability}
\const{CAP\_SYS\_NICE}, vedi sez.~\ref{sec:proc_capabilities}.} ha la
-possibilità di modificare arbitrariamente le priorità di qualunque
-processo. Un utente normale infatti può modificare solo la priorità dei suoi
+possibilità di modificare arbitrariamente le priorità di qualunque
+processo. Un utente normale infatti può modificare solo la priorità dei suoi
processi ed in genere soltanto diminuirla. Fino alla versione di kernel
2.6.12 Linux ha seguito le specifiche dello standard SUSv3, e come per tutti i
sistemi derivati da SysV veniva richiesto che l'user-ID reale o quello
effettivo del processo chiamante corrispondessero all'user-ID reale (e solo a
processi ed in genere soltanto diminuirla. Fino alla versione di kernel
2.6.12 Linux ha seguito le specifiche dello standard SUSv3, e come per tutti i
sistemi derivati da SysV veniva richiesto che l'user-ID reale o quello
effettivo del processo chiamante corrispondessero all'user-ID reale (e solo a
-quello) del processo di cui si intendeva cambiare la priorità. A partire dalla
-versione 2.6.12 è stata adottata la semantica in uso presso i sistemi derivati
-da BSD (SunOS, Ultrix, *BSD), in cui la corrispondenza può essere anche con
+quello) del processo di cui si intendeva cambiare la priorità. A partire dalla
+versione 2.6.12 è stata adottata la semantica in uso presso i sistemi derivati
+da BSD (SunOS, Ultrix, *BSD), in cui la corrispondenza può essere anche con
-Sempre a partire dal kernel 2.6.12 è divenuto possibile anche per gli utenti
-ordinari poter aumentare la priorità dei propri processi specificando un
-valore di \param{prio} negativo. Questa operazione non è possibile però in
-maniera indiscriminata, ed in particolare può essere effettuata solo
+Sempre a partire dal kernel 2.6.12 è divenuto possibile anche per gli utenti
+ordinari poter aumentare la priorità dei propri processi specificando un
+valore di \param{prio} negativo. Questa operazione non è possibile però in
+maniera indiscriminata, ed in particolare può essere effettuata solo
nell'intervallo consentito dal valore del limite \const{RLIMIT\_NICE}
(torneremo su questo in sez.~\ref{sec:sys_resource_limit}).
nell'intervallo consentito dal valore del limite \const{RLIMIT\_NICE}
(torneremo su questo in sez.~\ref{sec:sys_resource_limit}).
\label{sec:proc_real_time}
Come spiegato in sez.~\ref{sec:proc_sched} lo standard POSIX.1b ha introdotto
\label{sec:proc_real_time}
Come spiegato in sez.~\ref{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
+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
-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
+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
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
+ 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
ritardi non prevedibili riguardo ai tempi di esecuzione di qualunque processo.
Nonostante questo, ed in particolare con una serie di miglioramenti che sono
stati introdotti nello sviluppo del kernel,\footnote{in particolare a partire
dalla versione 2.6.18 sono stati inserite nel kernel una serie di modifiche
ritardi non prevedibili riguardo ai tempi di esecuzione di qualunque processo.
Nonostante questo, ed in particolare con una serie di miglioramenti che sono
stati introdotti nello sviluppo del kernel,\footnote{in particolare a partire
dalla versione 2.6.18 sono stati inserite nel kernel una serie di modifiche
essere fatto, ottenibili attivando in fase di compilazione una fra le
opzioni \texttt{CONFIG\_PREEMPT\_NONE}, \texttt{CONFIG\_PREEMPT\_VOLUNTARY}
essere fatto, ottenibili attivando in fase di compilazione una fra le
opzioni \texttt{CONFIG\_PREEMPT\_NONE}, \texttt{CONFIG\_PREEMPT\_VOLUNTARY}
- e \texttt{CONFIG\_PREEMPT\_DESKTOP}.} si può arrivare ad una ottima
-approssimazione di sistema real-time usando le priorità assolute; occorre
-farlo però 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
+ e \texttt{CONFIG\_PREEMPT\_DESKTOP}.} si può arrivare ad una ottima
+approssimazione di sistema real-time usando le priorità assolute; occorre
+farlo però 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
-Quando c'è un processo con priorità assoluta lo 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
+Quando c'è un processo con priorità assoluta lo 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
scelta; lo standard ne prevede due:
\begin{basedescript}{\desclabelwidth{1.2cm}\desclabelstyle{\nextlinelabel}}
\item[\textsf{FIFO}] \textit{First In First Out}. Il processo viene eseguito
fintanto che non cede volontariamente la CPU (con \func{sched\_yield}), si
scelta; lo standard ne prevede due:
\begin{basedescript}{\desclabelwidth{1.2cm}\desclabelstyle{\nextlinelabel}}
\item[\textsf{FIFO}] \textit{First In First Out}. Il processo viene eseguito
fintanto che non cede volontariamente la CPU (con \func{sched\_yield}), si
- blocca, finisce o viene interrotto da un processo a priorità più alta. Se il
- processo viene interrotto da uno a priorità più alta esso resterà in cima
- alla lista e sarà il primo ad essere eseguito quando i processi a priorità
- più alta diverranno inattivi. Se invece lo si blocca volontariamente sarà
- posto in coda alla lista (ed altri processi con la stessa priorità potranno
+ blocca, finisce o viene interrotto da un processo a priorità più alta. Se il
+ processo viene interrotto da uno a priorità più alta esso resterà in cima
+ alla lista e sarà il primo ad essere eseguito quando i processi a priorità
+ più alta diverranno inattivi. Se invece lo si blocca volontariamente sarà
+ posto in coda alla lista (ed altri processi con la stessa priorità potranno
a quello precedente, con la sola differenza che ciascun processo viene
eseguito al massimo per un certo periodo di tempo (la cosiddetta
\textit{time-slice}) dopo di che viene automaticamente posto in fondo alla
a quello precedente, con la sola differenza che ciascun processo viene
eseguito al massimo per un certo periodo di tempo (la cosiddetta
\textit{time-slice}) dopo di che viene automaticamente posto in fondo alla
\textsl{girotondo}.
\end{basedescript}
Lo standard POSIX.1-2001 prevede una funzione che consenta sia di modificare
le politiche di scheduling, passando da real-time a ordinarie o viceversa, che
\textsl{girotondo}.
\end{basedescript}
Lo standard POSIX.1-2001 prevede una funzione che consenta sia di modificare
le politiche di scheduling, passando da real-time a ordinarie o viceversa, che
-di specificare, in caso di politiche real-time, la eventuale priorità statica;
-la funzione è \funcd{sched\_setscheduler} ed il suo prototipo è:
+di specificare, in caso di politiche real-time, la eventuale priorità statica;
+la funzione è \funcd{sched\_setscheduler} ed il suo prototipo è:
\begin{prototype}{sched.h}
{int sched\_setscheduler(pid\_t pid, int policy, const struct sched\_param *p)}
\begin{prototype}{sched.h}
{int sched\_setscheduler(pid\_t pid, int policy, const struct sched\_param *p)}
- \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:
+ \bodydesc{La funzione ritorna 0 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
\begin{errlist}
\item[\errcode{ESRCH}] il processo \param{pid} non esiste.
\item[\errcode{EINVAL}] il valore di \param{policy} non esiste o il
La funzione esegue l'impostazione per il processo specificato dall'argomento
\param{pid}; un valore nullo di questo argomento esegue l'impostazione per il
La funzione esegue l'impostazione per il processo specificato dall'argomento
\param{pid}; un valore nullo di questo argomento esegue l'impostazione per il
dall'argomento \param{policy} i cui possibili valori sono riportati in
tab.~\ref{tab:proc_sched_policy}; la parte alta della tabella indica le
politiche real-time, quella bassa le politiche ordinarie. Un valore negativo
dall'argomento \param{policy} i cui possibili valori sono riportati in
tab.~\ref{tab:proc_sched_policy}; la parte alta della tabella indica le
politiche real-time, quella bassa le politiche ordinarie. Un valore negativo
\const{SCHED\_OTHER}& Scheduling ordinario.\\
\const{SCHED\_BATCH}& Scheduling ordinario con l'assunzione ulteriore di
lavoro \textit{CPU intensive}.\footnotemark\\
\const{SCHED\_OTHER}& Scheduling ordinario.\\
\const{SCHED\_BATCH}& Scheduling ordinario con l'assunzione ulteriore di
lavoro \textit{CPU intensive}.\footnotemark\\
varianti sulla politica di scheduling tradizionale per alcuni carichi di
lavoro specifici, queste due nuove politiche sono specifiche di Linux e non
devono essere usate se si vogliono scrivere programmi portabili.
varianti sulla politica di scheduling tradizionale per alcuni carichi di
lavoro specifici, queste due nuove politiche sono specifiche di Linux e non
devono essere usate se si vogliono scrivere programmi portabili.
-delle priorità dinamiche fatto dallo scheduler, il cosiddetto bonus di
-interattività che mira a favorire i processi che si svegliano dallo stato di
+delle priorità dinamiche fatto dallo scheduler, il cosiddetto bonus di
+interattività che mira a favorire i processi che si svegliano dallo stato di
\textbf{Sleep}.\footnote{cosa che accade con grande frequenza per i processi
interattivi, dato che essi sono per la maggior parte del tempo in attesa di
dati in ingresso da parte dell'utente.} La si usa pertanto, come indica il
\textbf{Sleep}.\footnote{cosa che accade con grande frequenza per i processi
interattivi, dato che essi sono per la maggior parte del tempo in attesa di
dati in ingresso da parte dell'utente.} La si usa pertanto, come indica il
-La politica \const{SCHED\_IDLE} invece è una politica dedicata ai processi che
-si desidera siano eseguiti con la più bassa priorità possibile, ancora più
+La politica \const{SCHED\_IDLE} invece è una politica dedicata ai processi che
+si desidera siano eseguiti con la più bassa priorità possibile, ancora più
statica viene impostato attraverso la struttura \struct{sched\_param},
riportata in fig.~\ref{fig:sig_sched_param}, il cui solo campo attualmente
statica viene impostato attraverso la struttura \struct{sched\_param},
riportata in fig.~\ref{fig:sig_sched_param}, il cui solo campo attualmente
-definito è \var{sched\_priority}. Il campo deve contenere il valore della
-priorità statica da assegnare al processo; lo standard prevede che questo
+definito è \var{sched\_priority}. Il campo deve contenere il valore della
+priorità statica da assegnare al processo; lo standard prevede che questo
debba essere assegnato all'interno di un intervallo fra un massimo ed un
minimo che nel caso di Linux sono rispettivamente 1 e 99.
debba essere assegnato all'interno di un intervallo fra un massimo ed un
minimo che nel caso di Linux sono rispettivamente 1 e 99.
impostato con le funzioni viste in precedenza.
Lo standard POSIX.1b prevede comunque che i due valori della massima e minima
impostato con le funzioni viste in precedenza.
Lo standard POSIX.1b prevede comunque che i due valori della massima e minima
scheduling \textit{real-time}, tramite le due funzioni
\funcd{sched\_get\_priority\_max} e \funcd{sched\_get\_priority\_min}, i cui
prototipi sono:
scheduling \textit{real-time}, tramite le due funzioni
\funcd{sched\_get\_priority\_max} e \funcd{sched\_get\_priority\_min}, i cui
prototipi sono:
- \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:
+ \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:
-per un processo o se ne cambia la priorità statica questo viene messo in cima
-alla lista dei processi con la stessa priorità; questo comporta che verrà
-eseguito subito, interrompendo eventuali altri processi con la stessa priorità
+per un processo o se ne cambia la priorità statica questo viene messo in cima
+alla lista dei processi con la stessa priorità; questo comporta che verrà
+eseguito subito, interrompendo eventuali altri processi con la stessa priorità
esegue sempre il primo della lista, mentre un nuovo processo che torna in
stato \textbf{Runnable} viene sempre inserito in coda alla lista. Se la
esegue sempre il primo della lista, mentre un nuovo processo che torna in
stato \textbf{Runnable} viene sempre inserito in coda alla lista. Se la
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 \textbf{Runnable}
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 \textbf{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.
+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.
-Solo un processo con i privilegi di amministratore\footnote{più precisamente
- con la \itindex{capabilities} capacità \const{CAP\_SYS\_NICE}, vedi
- sez.~\ref{sec:proc_capabilities}.} può impostare senza restrizioni priorità
+Solo un processo con i privilegi di amministratore\footnote{più precisamente
+ con la \itindex{capabilities} capacità \const{CAP\_SYS\_NICE}, vedi
+ sez.~\ref{sec:proc_capabilities}.} può impostare senza restrizioni priorità
-\const{SCHED\_RR}. Un utente normale può modificare solo le priorità di
-processi che gli appartengono; è cioè richiesto che l'user-ID effettivo del
+\const{SCHED\_RR}. Un utente normale può modificare solo le priorità di
+processi che gli appartengono; è cioè richiesto che l'user-ID effettivo del
processo chiamante corrisponda all'user-ID reale o effettivo del processo
indicato con \param{pid}.
Fino al kernel 2.6.12 gli utenti normali non potevano impostare politiche
processo chiamante corrisponda all'user-ID reale o effettivo del processo
indicato con \param{pid}.
Fino al kernel 2.6.12 gli utenti normali non potevano impostare politiche
-real-time o modificare la eventuale priorità statica di un loro processo. A
-partire da questa versione è divenuto possibile anche per gli utenti normali
-usare politiche real-time fintanto che la priorità assoluta che si vuole
-impostare è inferiore al limite \const{RLIMIT\_RTPRIO} (vedi
+real-time o modificare la eventuale priorità statica di un loro processo. A
+partire da questa versione è divenuto possibile anche per gli utenti normali
+usare politiche real-time fintanto che la priorità assoluta che si vuole
+impostare è inferiore al limite \const{RLIMIT\_RTPRIO} (vedi
-\const{RLIMIT\_RTPRIO}. Inoltre, in caso di processo già sottoposto ad una
-politica real-time, un utente può sempre, indipendentemente dal valore di
-\const{RLIMIT\_RTPRIO}, diminuirne la priorità o portarlo ad una politica
+\const{RLIMIT\_RTPRIO}. Inoltre, in caso di processo già sottoposto ad una
+politica real-time, un utente può sempre, indipendentemente dal valore di
+\const{RLIMIT\_RTPRIO}, diminuirne la priorità o portarlo ad una politica
usare le due funzioni \funcd{sched\_setparam} e \funcd{sched\_getparam} che
consentono rispettivamente di impostarne e leggerne il valore, i loro
prototipi sono:
usare le due funzioni \funcd{sched\_setparam} e \funcd{sched\_getparam} che
consentono rispettivamente di impostarne e leggerne il valore, i loro
prototipi sono:
\begin{errlist}
\item[\errcode{ESRCH}] il processo \param{pid} non esiste.
\item[\errcode{EINVAL}] il valore di \param{param} non ha senso per la
\begin{errlist}
\item[\errcode{ESRCH}] il processo \param{pid} non esiste.
\item[\errcode{EINVAL}] il valore di \param{param} non ha senso per la
argomento \param{policy} uguale a -1. Come per \func{sched\_setscheduler}
specificando 0 come valore dell'argomento \param{pid} si opera sul processo
argomento \param{policy} uguale a -1. Come per \func{sched\_setscheduler}
specificando 0 come valore dell'argomento \param{pid} si opera sul processo
-primi la priorità statica può essere soltanto nulla. La disponibilità di
-entrambe le funzioni può essere verificata controllando la macro
-\macro{\_POSIX\_PRIORITY\_SCHEDULING} che è definita nell'header
+primi la priorità statica può essere soltanto nulla. La disponibilità di
+entrambe le funzioni può essere verificata controllando la macro
+\macro{\_POSIX\_PRIORITY\_SCHEDULING} che è definita nell'header
-Se invece si vuole sapere quale è politica di scheduling di un processo si può
-usare la funzione \funcd{sched\_getscheduler}, il cui prototipo è:
+Se invece si vuole sapere quale è politica di scheduling di un processo si può
+usare la 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
\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
\begin{errlist}
\item[\errcode{ESRCH}] il processo \param{pid} non esiste.
\item[\errcode{EPERM}] non si hanno privilegi sufficienti per eseguire
\begin{errlist}
\item[\errcode{ESRCH}] il processo \param{pid} non esiste.
\item[\errcode{EPERM}] non si hanno privilegi sufficienti per eseguire
La funzione restituisce il valore, secondo quanto elencato in
tab.~\ref{tab:proc_sched_policy}, della politica di scheduling per il processo
La funzione restituisce il valore, secondo quanto elencato in
tab.~\ref{tab:proc_sched_policy}, della politica di scheduling per il processo
relativo al processo chiamante.
L'ultima funzione che permette di leggere le informazioni relative ai processi
relativo al processo chiamante.
L'ultima funzione che permette di leggere le informazioni relative ai processi
\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 0 in caso di successo e -1 in caso di errore,
\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 0 in caso di successo e -1 in caso di errore,
\end{errlist}}
\end{prototype}
La funzione restituisce il valore dell'intervallo di tempo usato per la
politica \textit{round robin} in una struttura \struct{timespec}, (la cui
\end{errlist}}
\end{prototype}
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 fig.~\ref{fig:sys_timeval_struct}). In realtà
-dato che in Linux questo intervallo di tempo è prefissato e non modificabile,
+definizione si può trovare in fig.~\ref{fig:sys_timeval_struct}). In realtà
+dato che in Linux questo intervallo di tempo è prefissato e non modificabile,
questa funzione ritorna sempre un valore di 150 millisecondi, e non importa
specificare il PID di un processo reale.
questa funzione ritorna sempre un valore di 150 millisecondi, e non importa
specificare il PID di un processo reale.
-real-time, e serve a far sì che il processo corrente rilasci la CPU, in modo
-da essere rimesso in coda alla lista dei processi con la stessa priorità per
-permettere ad un altro di essere eseguito; se però il processo è l'unico ad
-essere presente sulla coda l'esecuzione non sarà interrotta. In genere usano
+real-time, e serve a far sì che il processo corrente rilasci la CPU, in modo
+da essere rimesso in coda alla lista dei processi con la stessa priorità per
+permettere ad un altro di essere eseguito; se però il processo è l'unico ad
+essere presente sulla coda l'esecuzione non sarà interrotta. In genere usano
-La funzione può essere utilizzata anche con processi che usano lo scheduling
-ordinario, ma in questo caso il comportamento non è ben definito, e dipende
+La funzione può essere utilizzata anche con processi che usano lo scheduling
+ordinario, ma in questo caso il comportamento non è ben definito, e dipende
ripristinare un comportamento analogo al precedente scrivendo il valore 1
nel file \texttt{/proc/sys/kernel/sched\_compat\_yield}.}
ripristinare un comportamento analogo al precedente scrivendo il valore 1
nel file \texttt{/proc/sys/kernel/sched\_compat\_yield}.}
\label{sec:proc_sched_multiprocess}
Infine con il supporto dei sistemi multiprocessore sono state introdotte delle
\label{sec:proc_sched_multiprocess}
Infine con il supporto dei sistemi multiprocessore sono state introdotte delle
-che si pongono nei sistemi multiprocessore è infatti quello del cosiddetto
-\index{effetto~ping-pong} \textsl{effetto ping-pong}. Può accadere cioè che lo
+che si pongono nei sistemi multiprocessore è infatti quello del cosiddetto
+\index{effetto~ping-pong} \textsl{effetto ping-pong}. Può accadere cioè che lo
scheduler, quando riavvia un processo precedentemente interrotto scegliendo il
primo processore disponibile, lo faccia eseguire da un processore diverso
rispetto a quello su cui era stato eseguito in precedenza. Se il processo
scheduler, quando riavvia un processo precedentemente interrotto scegliendo il
primo processore disponibile, lo faccia eseguire da un processore diverso
rispetto a quello su cui era stato eseguito in precedenza. Se il processo
-\textit{cache}) contenente i dati più usati, che permette di evitare di
-eseguire un accesso (molto più lento) alla memoria principale sulla scheda
-madre. Chiaramente un processo sarà favorito se i suoi dati sono nella cache
-del processore, ma è ovvio che questo può essere vero solo per un processore
-alla volta, perché in presenza di più copie degli stessi dati su più
+\textit{cache}) contenente i dati più usati, che permette di evitare di
+eseguire un accesso (molto più lento) alla memoria principale sulla scheda
+madre. Chiaramente un processo sarà favorito se i suoi dati sono nella cache
+del processore, ma è ovvio che questo può essere vero solo per un processore
+alla volta, perché in presenza di più copie degli stessi dati su più
processori, non si potrebbe determinare quale di questi ha la versione dei
dati aggiornata rispetto alla memoria principale.
Questo comporta che quando un processore inserisce un dato nella sua cache,
tutti gli altri processori che hanno lo stesso dato devono invalidarlo, e
processori, non si potrebbe determinare quale di questi ha la versione dei
dati aggiornata rispetto alla memoria principale.
Questo comporta che quando un processore inserisce un dato nella sua cache,
tutti gli altri processori che hanno lo stesso dato devono invalidarlo, e
diventa serio quando si verifica l'\textsl{effetto ping-pong}, in tal caso
infatti un processo \textsl{rimbalza} continuamente da un processore all'altro
e si ha una continua invalidazione della cache, che non diventa mai
diventa serio quando si verifica l'\textsl{effetto ping-pong}, in tal caso
infatti un processo \textsl{rimbalza} continuamente da un processore all'altro
e si ha una continua invalidazione della cache, che non diventa mai
-Per ovviare a questo tipo di problemi è nato il concetto di \textsl{affinità
- di processore} (o \textit{CPU affinity}); la possibilità cioè di far sì che
+Per ovviare a questo tipo di problemi è nato il concetto di \textsl{affinità
+ di processore} (o \textit{CPU affinity}); la possibilità cioè di far sì che
un processo possa essere assegnato per l'esecuzione sempre allo stesso
processore. Lo scheduler dei kernel della serie 2.4.x aveva una scarsa
\textit{CPU affinity}, e \index{effetto~ping-pong} l'effetto ping-pong era
un processo possa essere assegnato per l'esecuzione sempre allo stesso
processore. Lo scheduler dei kernel della serie 2.4.x aveva una scarsa
\textit{CPU affinity}, e \index{effetto~ping-pong} l'effetto ping-pong era
-comune; con il nuovo scheduler dei kernel della 2.6.x questo problema è stato
-risolto ed esso cerca di mantenere il più possibile ciascun processo sullo
+comune; con il nuovo scheduler dei kernel della 2.6.x questo problema è stato
+risolto ed esso cerca di mantenere il più possibile ciascun processo sullo
sempre eseguito dallo stesso processore,\footnote{quella che viene detta
\textit{hard CPU affinity}, in contrasto con quella fornita dallo scheduler,
detta \textit{soft CPU affinity}, che di norma indica solo una preferenza,
non un requisito assoluto.} e per poter risolvere questo tipo di
problematiche nei nuovi kernel\footnote{le due system call per la gestione
della \textit{CPU affinity} sono state introdotte nel kernel 2.5.8, e le
sempre eseguito dallo stesso processore,\footnote{quella che viene detta
\textit{hard CPU affinity}, in contrasto con quella fornita dallo scheduler,
detta \textit{soft CPU affinity}, che di norma indica solo una preferenza,
non un requisito assoluto.} e per poter risolvere questo tipo di
problematiche nei nuovi kernel\footnote{le due system call per la gestione
della \textit{CPU affinity} sono state introdotte nel kernel 2.5.8, e le
l'opportuna infrastruttura ed una nuova system call che permette di impostare
su quali processori far eseguire un determinato processo attraverso una
l'opportuna infrastruttura ed una nuova system call che permette di impostare
su quali processori far eseguire un determinato processo attraverso una
-\textsl{maschera di affinità}. La corrispondente funzione di libreria è
-\funcd{sched\_setaffinity} ed il suo prototipo è:
+\textsl{maschera di affinità}. La corrispondente funzione di libreria è
+\funcd{sched\_setaffinity} ed il suo prototipo è:
\begin{prototype}{sched.h}
{int sched\_setaffinity (pid\_t pid, unsigned int cpusetsize, const
cpu\_set\_t *cpuset)}
\begin{prototype}{sched.h}
{int sched\_setaffinity (pid\_t pid, unsigned int cpusetsize, const
cpu\_set\_t *cpuset)}
\begin{errlist}
\item[\errcode{ESRCH}] il processo \param{pid} non esiste.
\item[\errcode{EINVAL}] il valore di \param{cpuset} contiene riferimenti a
\begin{errlist}
\item[\errcode{ESRCH}] il processo \param{pid} non esiste.
\item[\errcode{EINVAL}] il valore di \param{cpuset} contiene riferimenti a
maschera binaria. Quando le funzioni vennero incluse nelle \acr{glibc}
assunsero invece il prototipo appena mostrato. A complicare la cosa si
aggiunge il fatto che nella versione 2.3.3 delle \acr{glibc} l'argomento
maschera binaria. Quando le funzioni vennero incluse nelle \acr{glibc}
assunsero invece il prototipo appena mostrato. A complicare la cosa si
aggiunge il fatto che nella versione 2.3.3 delle \acr{glibc} l'argomento
-\param{cpusetsize} è stato eliminato, per poi essere ripristinato nella
-versione 2.3.4.\footnote{pertanto se la vostra pagina di manuale non è
+\param{cpusetsize} è stato eliminato, per poi essere ripristinato nella
+versione 2.3.4.\footnote{pertanto se la vostra pagina di manuale non è
nella versione corrente (maggio 2008) delle pagine di manuale e
corrispondente alla definizione presente in \file{sched.h}.}
nella versione corrente (maggio 2008) delle pagine di manuale e
corrispondente alla definizione presente in \file{sched.h}.}
processo identificato tramite il valore passato in \param{pid}. Come in
precedenza il valore nullo di \param{pid} indica il processo corrente. Per
poter utilizzare questa funzione sono richiesti i privilegi di amministratore
processo identificato tramite il valore passato in \param{pid}. Come in
precedenza il valore nullo di \param{pid} indica il processo corrente. Per
poter utilizzare questa funzione sono richiesti i privilegi di amministratore
-(è necessaria la capacità \const{CAP\_SYS\_NICE}) altrimenti essa fallirà con
-un errore di \errcode{EPERM}. Una volta impostata una maschera di affinità,
+(è necessaria la capacità \const{CAP\_SYS\_NICE}) altrimenti essa fallirà con
+un errore di \errcode{EPERM}. Una volta impostata una maschera di affinità,
questa viene ereditata attraverso una \func{fork}, in questo modo diventa
possibile legare automaticamente un gruppo di processi ad un singolo
processore.
Nell'uso comune, almeno con i kernel della serie 2.6.x, l'uso di questa
questa viene ereditata attraverso una \func{fork}, in questo modo diventa
possibile legare automaticamente un gruppo di processi ad un singolo
processore.
Nell'uso comune, almeno con i kernel della serie 2.6.x, l'uso di questa
-funzione non è necessario, in quanto è lo scheduler stesso che provvede a
-mantenere al meglio l'affinità di processore. Esistono però esigenze
-particolari, ad esempio quando un processo (o un gruppo di processi) è
+funzione non è necessario, in quanto è lo scheduler stesso che provvede a
+mantenere al meglio l'affinità di processore. Esistono però esigenze
+particolari, ad esempio quando un processo (o un gruppo di processi) è
interfaccia diventa possibile selezionare gruppi di processori utilizzabili in
maniera esclusiva. Lo stesso dicasi quando l'accesso a certe risorse (memoria
interfaccia diventa possibile selezionare gruppi di processori utilizzabili in
maniera esclusiva. Lo stesso dicasi quando l'accesso a certe risorse (memoria
avviene nelle architetture NUMA (\textit{Non-Uniform Memory Access}).
Infine se un gruppo di processi accede alle stesse risorse condivise (ad
avviene nelle architetture NUMA (\textit{Non-Uniform Memory Access}).
Infine se un gruppo di processi accede alle stesse risorse condivise (ad
senso usare lo stesso processore in modo da sfruttare meglio l'uso della sua
cache; questo ovviamente riduce i benefici di un sistema multiprocessore
nell'esecuzione contemporanea dei \itindex{thread} \textit{thread}, ma in
certi casi (quando i \itindex{thread} \textit{thread} sono inerentemente
serializzati nell'accesso ad una risorsa) possono esserci sufficienti vantaggi
senso usare lo stesso processore in modo da sfruttare meglio l'uso della sua
cache; questo ovviamente riduce i benefici di un sistema multiprocessore
nell'esecuzione contemporanea dei \itindex{thread} \textit{thread}, ma in
certi casi (quando i \itindex{thread} \textit{thread} sono inerentemente
serializzati nell'accesso ad una risorsa) possono esserci sufficienti vantaggi
estensione specifica delle \acr{glibc}, da attivare definendo la macro
\macro{\_GNU\_SOURCE}, non esiste infatti una standardizzazione per
questo tipo di interfaccia e POSIX al momento non prevede nulla al
estensione specifica delle \acr{glibc}, da attivare definendo la macro
\macro{\_GNU\_SOURCE}, non esiste infatti una standardizzazione per
questo tipo di interfaccia e POSIX al momento non prevede nulla al
- riguardo.} che permette di identificare un insieme di processori. Il dato è
-una maschera binaria: in generale è un intero a 32 bit in cui ogni bit
+ riguardo.} che permette di identificare un insieme di processori. Il dato è
+una maschera binaria: in generale è un intero a 32 bit in cui ogni bit
-numero di bit di un intero può non essere sufficiente, è stata creata questa
-che è una interfaccia generica che permette di usare a basso livello un tipo
+numero di bit di un intero può non essere sufficiente, è stata creata questa
+che è una interfaccia generica che permette di usare a basso livello un tipo
di dato qualunque rendendosi indipendenti dal numero di bit e dalla loro
disposizione.
Questa interfaccia, oltre alla definizione del tipo di dato apposito, prevede
anche una serie di macro di preprocessore per la manipolazione dello stesso,
che consentono di svuotare un insieme, aggiungere o togliere un processore da
di dato qualunque rendendosi indipendenti dal numero di bit e dalla loro
disposizione.
Questa interfaccia, oltre alla definizione del tipo di dato apposito, prevede
anche una serie di macro di preprocessore per la manipolazione dello stesso,
che consentono di svuotare un insieme, aggiungere o togliere un processore da
Rimuove il processore \param{cpu} nell'insieme.
\funcdecl{int \macro{CPU\_ISSET}(int cpu, cpu\_set\_t *set)}
Rimuove il processore \param{cpu} nell'insieme.
\funcdecl{int \macro{CPU\_ISSET}(int cpu, cpu\_set\_t *set)}
\const{CPU\_SETSIZE} che indica il numero massimo di processori che possono
far parte dell'insieme, e che costituisce un limite massimo al valore
dell'argomento \param{cpu}.
\const{CPU\_SETSIZE} che indica il numero massimo di processori che possono
far parte dell'insieme, e che costituisce un limite massimo al valore
dell'argomento \param{cpu}.
-In generale la maschera di affinità è preimpostata in modo che un processo
-possa essere eseguito su qualunque processore, se può comunque leggere il
+In generale la maschera di affinità è preimpostata in modo che un processo
+possa essere eseguito su qualunque processore, se può comunque leggere il
\begin{prototype}{sched.h}
{int sched\_getaffinity (pid\_t pid, unsigned int cpusetsize,
const cpu\_set\_t *cpuset)}
\begin{prototype}{sched.h}
{int sched\_getaffinity (pid\_t pid, unsigned int cpusetsize,
const cpu\_set\_t *cpuset)}
-La funzione restituirà all'indirizzo specificato da \param{cpuset} il valore
-della maschera di affinità del processo, così da poterla riutilizzare per una
+La funzione restituirà all'indirizzo specificato da \param{cpuset} il valore
+della maschera di affinità del processo, così da poterla riutilizzare per una
-A lungo l'unica priorità usata per i processi è stata quella relativa
-all'assegnazione dell'uso del processore. Ma il processore non è l'unica
+A lungo l'unica priorità usata per i processi è stata quella relativa
+all'assegnazione dell'uso del processore. Ma il processore non è l'unica
stati introdotti diversi \textit{I/O scheduler} in grado di distribuire in
maniera opportuna questa risorsa ai vari processi. Fino al kernel 2.6.17 era
possibile soltanto differenziare le politiche generali di gestione, scegliendo
di usare un diverso \textit{I/O scheduler}; a partire da questa versione, con
stati introdotti diversi \textit{I/O scheduler} in grado di distribuire in
maniera opportuna questa risorsa ai vari processi. Fino al kernel 2.6.17 era
possibile soltanto differenziare le politiche generali di gestione, scegliendo
di usare un diverso \textit{I/O scheduler}; a partire da questa versione, con
-diverse priorità di accesso per i singoli processi.\footnote{al momento
- (kernel 2.6.31), le priorità di I/O sono disponibili soltanto per questo
+diverse priorità di accesso per i singoli processi.\footnote{al momento
+ (kernel 2.6.31), le priorità di I/O sono disponibili soltanto per questo
scheduler disponibili sono mostrati dal contenuto dello stesso file che
riporta fra parentesi quadre quello attivo, il default in tutti i kernel
scheduler disponibili sono mostrati dal contenuto dello stesso file che
riporta fra parentesi quadre quello attivo, il default in tutti i kernel
-recenti è proprio il \texttt{cfq},\footnote{nome con cui si indica appunto lo
- scheduler \textit{Completely Fair Queuing}.} che supporta le priorità. Per i
+recenti è proprio il \texttt{cfq},\footnote{nome con cui si indica appunto lo
+ scheduler \textit{Completely Fair Queuing}.} che supporta le priorità. Per i
dettagli sulle caratteristiche specifiche degli altri scheduler, la cui
discussione attiene a problematiche di ambito sistemistico, si consulti la
documentazione nella directory \texttt{Documentation/block/} dei sorgenti del
kernel.
Una volta che si sia impostato lo scheduler CFQ ci sono due specifiche system
dettagli sulle caratteristiche specifiche degli altri scheduler, la cui
discussione attiene a problematiche di ambito sistemistico, si consulti la
documentazione nella directory \texttt{Documentation/block/} dei sorgenti del
kernel.
Una volta che si sia impostato lo scheduler CFQ ci sono due specifiche system
- utilizzo non avrà alcun effetto.} Dato che non esiste una interfaccia
-diretta nelle \acr{glibc} per queste due funzioni occorrerà invocarle tramite
+ utilizzo non avrà alcun effetto.} Dato che non esiste una interfaccia
+diretta nelle \acr{glibc} per queste due funzioni occorrerà invocarle tramite
la funzione \func{syscall} (come illustrato in
sez.~\ref{sec:intro_syscall}). Le due funzioni sono \funcd{ioprio\_get} ed
\funcd{ioprio\_set}; i rispettivi prototipi sono:
la funzione \func{syscall} (come illustrato in
sez.~\ref{sec:intro_syscall}). Le due funzioni sono \funcd{ioprio\_get} ed
\funcd{ioprio\_set}; i rispettivi prototipi sono:
\funcdecl{int ioprio\_get(int which, int who)}
\funcdecl{int ioprio\_set(int which, int who, int ioprio)}
\funcdecl{int ioprio\_get(int which, int who)}
\funcdecl{int ioprio\_set(int which, int who, int ioprio)}
- (indicante la priorità) o 0 in caso di successo e $-1$ in caso di errore,
- nel qual caso \var{errno} può assumere i valori:
+ (indicante la priorità) o 0 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 esiste il processo indicato.
\item[\errcode{EINVAL}] i valori di \param{which} e \param{who} non sono
\begin{errlist}
\item[\errcode{ESRCH}] non esiste il processo indicato.
\item[\errcode{EINVAL}] i valori di \param{which} e \param{who} non sono
\func{setpriority}. Anche in questo caso si deve specificare il valore
di \param{which} tramite le opportune costanti riportate in
tab.~\ref{tab:ioprio_args} che consentono di indicare un singolo processo, i
\func{setpriority}. Anche in questo caso si deve specificare il valore
di \param{which} tramite le opportune costanti riportate in
tab.~\ref{tab:ioprio_args} che consentono di indicare un singolo processo, i
composta da due parti, una che esprime la \textsl{classe} di scheduling di I/O
del processo, l'altra che esprime, quando la classe di scheduling lo prevede,
composta da due parti, una che esprime la \textsl{classe} di scheduling di I/O
del processo, l'altra che esprime, quando la classe di scheduling lo prevede,
-la priorità del processo all'interno della classe stessa. Questo stesso
-formato viene utilizzato per indicare il valore della priorità da impostare
+la priorità del processo all'interno della classe stessa. Questo stesso
+formato viene utilizzato per indicare il valore della priorità da impostare
impostano usando queste macro. Le prime due si usano con il valore restituito
da \func{ioprio\_get} e per ottenere rispettivamente la classe di
scheduling\footnote{restituita dalla macro con i valori di
impostano usando queste macro. Le prime due si usano con il valore restituito
da \func{ioprio\_get} e per ottenere rispettivamente la classe di
scheduling\footnote{restituita dalla macro con i valori di
- tab.~\ref{tab:IOsched_class}.} e l'eventuale valore della priorità. La terza
-macro viene invece usata per creare un valore di priorità da usare come
+ tab.~\ref{tab:IOsched_class}.} e l'eventuale valore della priorità. La terza
+macro viene invece usata per creare un valore di priorità da usare come
-diverse modalità di distribuzione delle risorse analoghe a quelle già adottate
-anche nel funzionamento dello scheduler del processore. Ciascuna di esse è
+diverse modalità di distribuzione delle risorse analoghe a quelle già adottate
+anche nel funzionamento dello scheduler del processore. Ciascuna di esse è
identificata tramite una opportuna costante, secondo quanto riportato in
tab.~\ref{tab:IOsched_class}.
identificata tramite una opportuna costante, secondo quanto riportato in
tab.~\ref{tab:IOsched_class}.
questa classe riescono ad accedere a disco soltanto quando nessun altro
processo richiede l'accesso. Occorre pertanto usarla con molta attenzione,
questa classe riescono ad accedere a disco soltanto quando nessun altro
processo richiede l'accesso. Occorre pertanto usarla con molta attenzione,
ci sono altri processi in una qualunque delle altre due classi che stanno
accedendo al disco. Quando si usa questa classe non ha senso indicare un
ci sono altri processi in una qualunque delle altre due classi che stanno
accedendo al disco. Quando si usa questa classe non ha senso indicare un
-valore di priorità, dato che in questo caso non esiste nessuna gerarchia e la
-priorità è identica, la minima possibile, per tutti i processi.
+valore di priorità, dato che in questo caso non esiste nessuna gerarchia e la
+priorità è identica, la minima possibile, per tutti i processi.
\hline
\const{IOPRIO\_CLASS\_RT} & Scheduling di I/O \textit{real time}.\\
\const{IOPRIO\_CLASS\_BE} & Scheduling di I/O ordinario.\\
\hline
\const{IOPRIO\_CLASS\_RT} & Scheduling di I/O \textit{real time}.\\
\const{IOPRIO\_CLASS\_BE} & Scheduling di I/O ordinario.\\
-La seconda classe di priorità di I/O è \const{IOPRIO\_CLASS\_BE} (il nome sta
-per \textit{best-effort}) che è quella usata ordinariamente da tutti
-processi. In questo caso esistono priorità diverse che consentono di
+La seconda classe di priorità di I/O è \const{IOPRIO\_CLASS\_BE} (il nome sta
+per \textit{best-effort}) che è quella usata ordinariamente da tutti
+processi. In questo caso esistono priorità diverse che consentono di
assegnazione di una maggiore banda passante nell'accesso a disco ad un
processo rispetto agli altri, con meccanismo simile a quello dei valori di
assegnazione di una maggiore banda passante nell'accesso a disco ad un
processo rispetto agli altri, con meccanismo simile a quello dei valori di
-\textit{nice} in cui si evita che un processo a priorità più alta possa
-bloccare indefinitamente quelli a priorità più bassa. In questo caso però le
-diverse priorità sono soltanto otto, indicate da un valore numerico fra 0 e 7
-e come per \textit{nice} anche in questo caso un valore più basso indica una
-priorità maggiore.
+\textit{nice} in cui si evita che un processo a priorità più alta possa
+bloccare indefinitamente quelli a priorità più bassa. In questo caso però le
+diverse priorità sono soltanto otto, indicate da un valore numerico fra 0 e 7
+e come per \textit{nice} anche in questo caso un valore più basso indica una
+priorità maggiore.
-Infine la classe di priorità di I/O \textit{real-time}
-\const{IOPRIO\_CLASS\_RT} ricalca le omonime priorità di processore: un
+Infine la classe di priorità di I/O \textit{real-time}
+\const{IOPRIO\_CLASS\_RT} ricalca le omonime priorità di processore: un
processo in questa classe ha sempre la precedenza nell'accesso a disco
rispetto a tutti i processi delle altre classi e di un processo nella stessa
processo in questa classe ha sempre la precedenza nell'accesso a disco
rispetto a tutti i processi delle altre classi e di un processo nella stessa
-classe ma con priorità inferiore, ed è pertanto in grado di bloccare
-completamente tutti gli altri. Anche in questo caso ci sono 8 priorità diverse
-con un valore numerico fra 0 e 7, con una priorità più elevata per valori più
+classe ma con priorità inferiore, ed è pertanto in grado di bloccare
+completamente tutti gli altri. Anche in questo caso ci sono 8 priorità diverse
+con un valore numerico fra 0 e 7, con una priorità più elevata per valori più
viene impostata in maniera automatica nella classe \const{IOPRIO\_CLASS\_BE}
con un valore ottenuto a partire dal corrispondente valore di \textit{nice}
tramite la formula: $\mathtt{\mathit{prio}}=(\mathtt{\mathit{nice}}+20)/5$. Un
viene impostata in maniera automatica nella classe \const{IOPRIO\_CLASS\_BE}
con un valore ottenuto a partire dal corrispondente valore di \textit{nice}
tramite la formula: $\mathtt{\mathit{prio}}=(\mathtt{\mathit{nice}}+20)/5$. Un
-utente ordinario può modificare con \func{ioprio\_set} soltanto le priorità
-dei processi che gli appartengono,\footnote{per la modifica delle priorità di
+utente ordinario può modificare con \func{ioprio\_set} soltanto le priorità
+dei processi che gli appartengono,\footnote{per la modifica delle priorità di
- capacità \const{CAP\_SYS\_NICE} (vedi sez.~\ref{sec:proc_capabilities}).}
-cioè quelli il cui user-ID reale corrisponde all'user-ID reale o effettivo del
-chiamante. Data la possibilità di ottenere un blocco totale dello stesso, solo
-l'amministratore\footnote{o un processo con la capacità
- \const{CAP\_SYS\_ADMIN} (vedi sez.~\ref{sec:proc_capabilities}).} può
-impostare un processo ad una priorità di I/O nella classe
+ capacità \const{CAP\_SYS\_NICE} (vedi sez.~\ref{sec:proc_capabilities}).}
+cioè quelli il cui user-ID reale corrisponde all'user-ID reale o effettivo del
+chiamante. Data la possibilità di ottenere un blocco totale dello stesso, solo
+l'amministratore\footnote{o un processo con la capacità
+ \const{CAP\_SYS\_ADMIN} (vedi sez.~\ref{sec:proc_capabilities}).} può
+impostare un processo ad una priorità di I/O nella classe
% vedi man numa e le pagine di manuale relative
% vedere anche dove metterle...
\section{Problematiche di programmazione multitasking}
\label{sec:proc_multi_prog}
% vedi man numa e le pagine di manuale relative
% vedere anche dove metterle...
\section{Problematiche di programmazione multitasking}
\label{sec:proc_multi_prog}
indipendenti l'uno dall'altro, nella programmazione in un sistema multitasking
occorre tenere conto di una serie di problematiche che normalmente non
esistono quando si ha a che fare con un sistema in cui viene eseguito un solo
programma alla volta.
indipendenti l'uno dall'altro, nella programmazione in un sistema multitasking
occorre tenere conto di una serie di problematiche che normalmente non
esistono quando si ha a che fare con un sistema in cui viene eseguito un solo
programma alla volta.
-Pur essendo questo argomento di carattere generale, ci è parso opportuno
-introdurre sinteticamente queste problematiche, che ritroveremo a più riprese
+Pur essendo questo argomento di carattere generale, ci è parso opportuno
+introdurre sinteticamente queste problematiche, che ritroveremo a più riprese
in capitoli successivi, in questa sezione conclusiva del capitolo in cui
abbiamo affrontato la gestione dei processi.
in capitoli successivi, in questa sezione conclusiva del capitolo in cui
abbiamo affrontato la gestione dei processi.
\label{sec:proc_atom_oper}
La nozione di \textsl{operazione atomica} deriva dal significato greco della
\label{sec:proc_atom_oper}
La nozione di \textsl{operazione atomica} deriva dal significato greco della
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 \itindex{race~condition} \textit{race
condition} (vedi sez.~\ref{sec:proc_race_cond}) derivanti da operazioni
interrotte in una fase in cui non erano ancora state completate.
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 \itindex{race~condition} \textit{race
condition} (vedi sez.~\ref{sec:proc_race_cond}) derivanti da operazioni
interrotte in una fase in cui non erano ancora state completate.
-Nel caso dell'interazione fra processi la situazione è molto più semplice, ed
-occorre preoccuparsi della atomicità delle operazioni solo quando si ha a che
+Nel caso dell'interazione fra processi la situazione è molto più semplice, ed
+occorre preoccuparsi della atomicità delle operazioni solo quando si ha a che
fare con meccanismi di intercomunicazione (che esamineremo in dettaglio in
cap.~\ref{cha:IPC}) o nelle operazioni con i file (vedremo alcuni esempi in
sez.~\ref{sec:file_atomic}). In questi casi in genere l'uso delle appropriate
fare con meccanismi di intercomunicazione (che esamineremo in dettaglio in
cap.~\ref{cha:IPC}) o nelle operazioni con i file (vedremo alcuni esempi in
sez.~\ref{sec:file_atomic}). In questi casi in genere l'uso delle appropriate
-funzioni di libreria per compiere le operazioni necessarie è garanzia
-sufficiente di atomicità in quanto le system call con cui esse sono realizzate
+funzioni di libreria per compiere le operazioni necessarie è garanzia
+sufficiente di atomicità in quanto le system call con cui esse sono realizzate
stesso processo, e pure alcune system call, possono essere interrotti in
qualunque momento, e le operazioni di un eventuale \textit{signal handler}
sono compiute nello stesso spazio di indirizzi del processo. Per questo, anche
stesso processo, e pure alcune system call, possono essere interrotti in
qualunque momento, e le operazioni di un eventuale \textit{signal handler}
sono compiute nello stesso spazio di indirizzi del processo. Per questo, anche
operazioni atomiche (torneremo su questi aspetti in
sez.~\ref{sec:sig_adv_control}).
In questo caso il sistema provvede un tipo di dato, il \type{sig\_atomic\_t},
operazioni atomiche (torneremo su questi aspetti in
sez.~\ref{sec:sig_adv_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
+il cui accesso è assicurato essere atomico. In pratica comunque si può
+assumere che, in ogni piattaforma su cui è implementato Linux, il tipo
-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
+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
\direct{volatile} le variabili che possono essere interessate ad accesso
condiviso, onde evitare problemi con le ottimizzazioni del codice.
\direct{volatile} le variabili che possono essere interessate ad accesso
condiviso, onde evitare problemi con le ottimizzazioni del codice.
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
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
+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
-Dato che in un sistema multitasking ogni processo può essere interrotto in
-qualunque momento per farne subentrare un altro in esecuzione, niente può
+Dato che in un sistema multitasking ogni processo può essere interrotto in
+qualunque momento per farne subentrare un altro in esecuzione, niente può
assicurare un preciso ordine di esecuzione fra processi diversi o che una
sezione di un programma possa essere eseguita senza interruzioni da parte di
altri. Queste situazioni comportano pertanto errori estremamente subdoli e
difficili da tracciare, in quanto nella maggior parte dei casi tutto
assicurare un preciso ordine di esecuzione fra processi diversi o che una
sezione di un programma possa essere eseguita senza interruzioni da parte di
altri. Queste situazioni comportano pertanto errori estremamente subdoli e
difficili da tracciare, in quanto nella maggior parte dei casi tutto
-che l'unico modo per evitarle è quello di riconoscerle come tali e prendere
-gli adeguati provvedimenti per far sì che non si verifichino. Casi tipici di
+che l'unico modo per evitarle è quello di riconoscerle come tali e prendere
+gli adeguati provvedimenti per far sì 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
\textit{race condition} si hanno quando diversi processi accedono allo stesso
file, o nell'accesso a meccanismi di intercomunicazione come la memoria
atomicamente le operazioni necessarie, occorre che quelle parti di codice in
cui si compiono le operazioni sulle risorse condivise (le cosiddette
\index{sezione~critica} \textsl{sezioni critiche}) del programma, siano
atomicamente le operazioni necessarie, occorre che quelle parti di codice in
cui si compiono le operazioni sulle risorse condivise (le cosiddette
\index{sezione~critica} \textsl{sezioni critiche}) del programma, siano
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. Per
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. Per
-definizione un \textit{deadlock} è una situazione in cui due o più processi
-non sono più in grado di proseguire perché ciascuno aspetta il risultato di
+definizione un \textit{deadlock} è una situazione in cui due o più processi
+non sono più in grado di proseguire perché ciascuno aspetta il risultato di
-L'esempio tipico di una situazione che può condurre ad un
-\textit{deadlock} è quello in cui un flag di
+L'esempio tipico di una situazione che può condurre ad un
+\textit{deadlock} è quello in cui un flag di
visto in sez.~\ref{sec:proc_atom_oper}; questi problemi infatti possono essere
risolti soltanto assicurandosi, quando essa sia richiesta, che sia possibile
eseguire in maniera atomica le operazioni necessarie.
visto in sez.~\ref{sec:proc_atom_oper}; questi problemi infatti possono essere
risolti soltanto assicurandosi, quando essa sia richiesta, che sia possibile
eseguire in maniera atomica le operazioni necessarie.
qualunque punto della sua esecuzione ed essere chiamata una seconda volta da
un altro \itindex{thread} \textit{thread} di esecuzione senza che questo
qualunque punto della sua esecuzione ed essere chiamata una seconda volta da
un altro \itindex{thread} \textit{thread} di esecuzione senza che questo
comune nella programmazione \itindex{thread} \textit{multi-thread}, ma si
hanno gli stessi problemi quando si vogliono chiamare delle funzioni
all'interno dei gestori dei segnali.
comune nella programmazione \itindex{thread} \textit{multi-thread}, ma si
hanno gli stessi problemi quando si vogliono chiamare delle funzioni
all'interno dei gestori dei segnali.
queste infatti vengono allocate nello \itindex{stack} \textit{stack}, ed
un'altra invocazione non fa altro che allocarne un'altra copia. Una funzione
queste infatti vengono allocate nello \itindex{stack} \textit{stack}, ed
un'altra invocazione non fa altro che allocarne un'altra copia. Una funzione
-può non essere rientrante quando opera su memoria che non è nello
-\itindex{stack} \textit{stack}. Ad esempio una funzione non è mai rientrante
+può non essere rientrante quando opera su memoria che non è nello
+\itindex{stack} \textit{stack}. Ad esempio una funzione non è mai rientrante
se usa una variabile globale o statica.
Nel caso invece la funzione operi su un oggetto allocato dinamicamente, la
se usa una variabile globale o statica.
Nel caso invece la funzione operi su un oggetto allocato dinamicamente, la
-cosa viene a dipendere da come avvengono le operazioni: se l'oggetto è creato
-ogni volta e ritornato indietro la funzione può essere rientrante, se invece
+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.
esso viene individuato dalla funzione stessa due chiamate alla stessa funzione
potranno interferire quando entrambe faranno riferimento allo stesso oggetto.
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
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
disposizione due macro di compilatore,\footnote{si ricordi quanto illustrato
in sez.~\ref{sec:intro_gcc_glibc_std}.} \macro{\_REENTRANT} e
\macro{\_THREAD\_SAFE}, la cui definizione attiva le versioni rientranti di
disposizione due macro di compilatore,\footnote{si ricordi quanto illustrato
in sez.~\ref{sec:intro_gcc_glibc_std}.} \macro{\_REENTRANT} e
\macro{\_THREAD\_SAFE}, la cui definizione attiva le versioni rientranti di