caratteristiche di Unix (che esamineremo in dettaglio più avanti) è che
qualunque processo può a sua volta generarne altri, detti processi figli
(\textit{child process}). Ogni processo è identificato presso il sistema da un
-numero unico, il cosiddetto \textit{process identifier} o, più brevemente,
-\acr{pid}.
+numero univoco, il cosiddetto \textit{process identifier} o, più brevemente,
+\acr{pid}, assengnato in forma progressiva (vedi \secref{sec:proc_pid}) quando
+il processo viene creato.
Una seconda caratteristica di un sistema Unix è che la generazione di un
processo è un'operazione separata rispetto al lancio di un programma. In
possono classificare i processi con la relazione padre/figlio in
un'organizzazione gerarchica ad albero, in maniera analoga a come i file sono
organizzati in un albero di directory (si veda
-\secref{sec:file_organization}); in \curfig\ si è mostrato il risultato del
-comando \cmd{pstree} che permette di visualizzare questa struttura, alla cui
-base c'è \cmd{init} che è progenitore di tutti gli altri processi.
+\secref{sec:file_organization}); in \figref{fig:proc_tree} si è mostrato il
+risultato del comando \cmd{pstree} che permette di visualizzare questa
+struttura, alla cui base c'è \cmd{init} che è progenitore di tutti gli altri
+processi.
Il kernel mantiene una tabella dei processi attivi, la cosiddetta
\textit{process table}; per ciascun processo viene mantenuta una voce nella
usate a questo scopo sono dichiarate nell'header file \file{linux/sched.h}, ed
uno schema semplificato, che riporta la struttura delle principali informazioni
contenute nella \type{task\_struct} (che in seguito incontreremo a più
-riprese), è mostrato in \nfig.
+riprese), è mostrato in \figref{fig:proc_task_struct}.
\begin{figure}[htb]
\centering
\end{figure}
-Come accennato in \secref{sec:intro_unix_struct} è lo \textit{scheduler} che
-decide quale processo mettere in esecuzione; esso viene eseguito ad ogni
-system call ed ad ogni interrupt,\footnote{più in una serie di altre
- occasioni. NDT completare questa parte.} (ma può essere anche attivato
-esplicitamente). Il timer di sistema provvede comunque a che esso sia invocato
-periodicamente, generando un interrupt periodico secondo la frequenza
-specificata dalla costante \macro{HZ}, definita in \file{asm/param.h}, ed il
-cui valore è espresso in Hertz.\footnote{Il valore usuale di questa costante è
- 100, per tutte le architetture eccetto l'alpha, per la quale è 1000. Occorre
- fare attenzione a non confondere questo valore con quello dei clock tick
- (vedi \secref{sec:sys_unix_time}).}
+Come accennato in \secref{sec:intro_unix_struct} è lo
+\textit{scheduler}\index{scheduler} che decide quale processo mettere in
+esecuzione; esso viene eseguito ad ogni system call ed ad ogni
+interrupt,\footnote{più in una serie di altre occasioni. NDT completare questa
+ parte.} (ma può essere anche attivato esplicitamente). Il timer di sistema
+provvede comunque a che esso sia invocato periodicamente, generando un
+interrupt periodico secondo la frequenza specificata dalla costante
+\macro{HZ}, definita in \file{asm/param.h}, ed il cui valore è espresso in
+Hertz.\footnote{Il valore usuale di questa costante è 100, per tutte le
+ architetture eccetto l'alpha, per la quale è 1000. Occorre fare attenzione a
+ non confondere questo valore con quello dei clock tick (vedi
+ \secref{sec:sys_unix_time}).}
%Si ha cioè un interrupt dal timer ogni centesimo di secondo.
-Ogni volta che viene eseguito, lo \textit{scheduler} effettua il calcolo delle
-priorità dei vari processi attivi (torneremo su questo in
+Ogni volta che viene eseguito, lo \textit{scheduler}\index{scheduler} effettua
+il calcolo delle priorità dei vari processi attivi (torneremo su questo in
\secref{sec:proc_priority}) e stabilisce quale di essi debba essere posto in
esecuzione fino alla successiva invocazione.
una system call, Linux però usa un'altra nomenclatura, e la funzione
\func{fork} è basata a sua volta sulla system call \func{\_\_clone}, che viene
usata anche per generare i \textit{thread}. Il processo figlio creato dalla
-\func{fork} è una copia identica del processo processo padre, ma ha nuovo
+\func{fork} è una copia identica del processo processo padre, ma ha un nuovo
\acr{pid} e viene eseguito in maniera indipendente (le differenze fra padre e
figlio sono affrontate in dettaglio in \secref{sec:proc_fork}).
\textit{process image}), le funzioni della famiglia \func{exec} permettono di
caricare un'altro programma da disco sostituendo quest'ultimo all'immagine
corrente; questo fa sì che l'immagine precedente venga completamente
-cancellata. Questo significa che quando il nuovo programma esce, anche il
+cancellata. Questo significa che quando il nuovo programma termina, anche il
processo termina, e non si può tornare alla precedente immagine.
Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto
\label{sec:proc_pid}
Come accennato nell'introduzione, ogni processo viene identificato dal sistema
-da un numero identificativo unico, il \textit{process id} o \acr{pid};
+da un numero identificativo univoco, il \textit{process id} o \acr{pid};
quest'ultimo è un tipo di dato standard, il \type{pid\_t} che in genere è un
intero con segno (nel caso di Linux e delle \acr{glibc} il tipo usato è
\ctyp{int}).
-Il \acr{pid} viene assegnato in forma progressiva ogni volta che un nuovo
-processo viene creato, fino ad un limite che, essendo il \acr{pid} un numero
-positivo memorizzato in un intero a 16 bit, arriva ad un massimo di 32767.
-Oltre questo valore l'assegnazione riparte dal numero più basso disponibile a
-partire da un minimo di 300,\footnote{questi valori sono definiti dalla macro
- \macro{PID\_MAX} in \file{threads.h} e direttamente in \file{fork.c} nei
- sorgenti del kernel.} che serve a riservare i \acr{pid} più bassi ai processi
-eseguiti dal direttamente dal kernel. Per questo motivo, come visto in
-\secref{sec:proc_hierarchy}, il processo di avvio (\cmd{init}) ha sempre il
-\acr{pid} uguale a uno.
+Il \acr{pid} viene assegnato in forma progressiva\footnote{in genere viene
+ assegnato il numero successivo a quello usato per l'ultimo processo creato,
+ a meno che questo numero non sia già utilizzato per un altro \acr{pid},
+ \acr{pgid} o \acr{sid} (vedi \secref{sec:sess_proc_group}).} ogni volta che
+un nuovo processo viene creato, fino ad un limite che, essendo il \acr{pid} un
+numero positivo memorizzato in un intero a 16 bit, arriva ad un massimo di
+32768. Oltre questo valore l'assegnazione riparte dal numero più basso
+disponibile a partire da un minimo di 300,\footnote{questi valori, fino al
+ kernel 2.4.x, sono definiti dalla macro \macro{PID\_MAX} in \file{threads.h}
+ e direttamente in \file{fork.c}, con il kernel 2.5.x e la nuova interfaccia
+ per i thread creata da Ingo Molnar anche il meccanismo di allocazione dei
+ \acr{pid} è stato modificato.} che serve a riservare i \acr{pid} più bassi
+ai processi eseguiti dal direttamente dal kernel. Per questo motivo, come
+visto in \secref{sec:proc_hierarchy}, il processo di avvio (\cmd{init}) ha
+sempre il \acr{pid} uguale a uno.
Tutti i processi inoltre memorizzano anche il \acr{pid} del genitore da cui
sono stati creati, questo viene chiamato in genere \acr{ppid} (da
\end{functions}
Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che
-il processo figlio continuano ad essere eseguiti normalmente all'istruzione
-seguente la \func{fork}; il processo figlio è però una copia del padre, e
-riceve una copia dei segmenti di testo, stack e dati (vedi
+il processo figlio continuano ad essere eseguiti normalmente a partire
+dall'istruzione seccessiva alla \func{fork}; il processo figlio è però una
+copia del padre, e riceve una copia dei segmenti di testo, stack e dati (vedi
\secref{sec:proc_mem_layout}), ed esegue esattamente lo stesso codice del
padre. Si tenga presente però che la memoria è copiata, non condivisa,
pertanto padre e figlio vedono variabili diverse.
-Per quanto riguarda la gestione della memoria in generale il segmento di
-testo, che è identico, è condiviso e tenuto in read-only per il padre e per i
-figli. Per gli altri segmenti Linux utilizza la tecnica del \textit{copy on
- write}\index{copy on write}; questa tecnica comporta che una pagina di
-memoria viene effettivamente copiata per il nuovo processo solo quando ci
-viene effettuata sopra una scrittura (e si ha quindi una reale differenza fra
-padre e figlio). In questo modo si rende molto più efficiente il meccanismo
-della creazione di un nuovo processo, non essendo più necessaria la copia di
-tutto lo spazio degli indirizzi virtuali del padre, ma solo delle pagine di
-memoria che sono state modificate, e solo al momento della modifica stessa.
+Per quanto riguarda la gestione della memoria, in generale il segmento di
+testo, che è identico per i due processi, è condiviso e tenuto in read-only
+per il padre e per i figli. Per gli altri segmenti Linux utilizza la tecnica
+del \textit{copy on write}\index{copy on write}; questa tecnica comporta che
+una pagina di memoria viene effettivamente copiata per il nuovo processo solo
+quando ci viene effettuata sopra una scrittura (e si ha quindi una reale
+differenza fra padre e figlio). In questo modo si rende molto più efficiente
+il meccanismo della creazione di un nuovo processo, non essendo più necessaria
+la copia di tutto lo spazio degli indirizzi virtuali del padre, ma solo delle
+pagine di memoria che sono state modificate, e solo al momento della modifica
+stessa.
La differenza che si ha nei due processi è che nel processo padre il valore di
ritorno della funzione \func{fork} è il \acr{pid} del processo figlio, mentre
L'uso di \func{fork} avviene secondo due modalità principali; la prima è
quella in cui all'interno di un programma si creano processi figli cui viene
affidata l'esecuzione di una certa sezione di codice, mentre il processo padre
-ne esegue un'altra. È il caso tipico dei server (il modello
-\textit{client-server} è illustrato in \secref{sec:net_cliserv}) di rete in
-cui il padre riceve ed accetta le richieste da parte dei client, per ciascuna
-delle quali pone in esecuzione un figlio che è incaricato di fornire il
-servizio.
+ne esegue un'altra. È il caso tipico dei programmi server (il modello
+\textit{client-server} è illustrato in \secref{sec:net_cliserv}) in cui il
+padre riceve ed accetta le richieste da parte dei programmi client, per
+ciascuna delle quali pone in esecuzione un figlio che è incaricato di fornire
+il servizio.
La seconda modalità è quella in cui il processo vuole eseguire un altro
programma; questo è ad esempio il caso della shell. In questo caso il processo
-crea un figlio la cui unica operazione è quella fare una \func{exec} (di cui
-parleremo in \secref{sec:proc_exec}) subito dopo la \func{fork}.
+crea un figlio la cui unica operazione è quella di fare una \func{exec} (di
+cui parleremo in \secref{sec:proc_exec}) subito dopo la \func{fork}.
Alcuni sistemi operativi (il VMS ad esempio) combinano le operazioni di questa
seconda modalità (una \func{fork} seguita da una \func{exec}) in un'unica
relativamente facile intervenire sulle le modalità di esecuzione del nuovo
programma.
-In \figref{fig:proc_fork_code} si è riportato il corpo del codice del
-programma di esempio \cmd{forktest}, che ci permette di illustrare molte
-caratteristiche dell'uso della funzione \func{fork}. Il programma permette di
-creare un numero di figli specificato da linea di comando, e prende anche
-alcune opzioni per indicare degli eventuali tempi di attesa in secondi
-(eseguiti tramite la funzione \func{sleep}) per il padre ed il figlio (con
-\cmd{forktest -h} si ottiene la descrizione delle opzioni); il codice
-completo, compresa la parte che gestisce le opzioni a riga di comando, è
-disponibile nel file \file{ForkTest.c}, distribuito insieme agli altri
-sorgenti degli esempi su \href{http://gapil.firenze.linux.it/gapil_source.tgz}
+In \figref{fig:proc_fork_code} è riportato il corpo del codice del programma
+di esempio \cmd{forktest}, che permette di illustrare molte caratteristiche
+dell'uso della funzione \func{fork}. Il programma crea un numero di figli
+specificato da linea di comando, e prende anche alcune opzioni per indicare
+degli eventuali tempi di attesa in secondi (eseguiti tramite la funzione
+\func{sleep}) per il padre ed il figlio (con \cmd{forktest -h} si ottiene la
+descrizione delle opzioni); il codice completo, compresa la parte che gestisce
+le opzioni a riga di comando, è disponibile nel file \file{ForkTest.c},
+distribuito insieme agli altri sorgenti degli esempi su
+\href{http://gapil.firenze.linux.it/gapil_source.tgz}
{\texttt{http://gapil.firenze.linux.it/gapil\_source.tgz}}.
Decifrato il numero di figli da creare, il ciclo principale del programma
Esaminiamo questo risultato: una prima conclusione che si può trarre è che non
si può dire quale processo fra il padre ed il figlio venga eseguito per
primo\footnote{a partire dal kernel 2.5.2-pre10 è stato introdotto il nuovo
- scheduler di Ingo Molnar che esegue sempre per primo il figlio; per
- mantenere la portabilità è opportuno non fare comunque affidamento su questo
- comportamento.} dopo la chiamata a \func{fork}; dall'esempio si può notare
-infatti come nei primi due cicli sia stato eseguito per primo il padre (con la
-stampa del \acr{pid} del nuovo processo) per poi passare all'esecuzione del
-figlio (completata con i due avvisi di esecuzione ed uscita), e tornare
-all'esecuzione del padre (con la stampa del passaggio al ciclo successivo),
-mentre la terza volta è stato prima eseguito il figlio (fino alla conclusione)
-e poi il padre.
+ scheduler\index{scheduler} di Ingo Molnar che esegue sempre per primo il
+ figlio; per mantenere la portabilità è opportuno non fare comunque
+ affidamento su questo comportamento.} dopo la chiamata a \func{fork};
+dall'esempio si può notare infatti come nei primi due cicli sia stato eseguito
+per primo il padre (con la stampa del \acr{pid} del nuovo processo) per poi
+passare all'esecuzione del figlio (completata con i due avvisi di esecuzione
+ed uscita), e tornare all'esecuzione del padre (con la stampa del passaggio al
+ciclo successivo), mentre la terza volta è stato prima eseguito il figlio
+(fino alla conclusione) e poi il padre.
In generale l'ordine di esecuzione dipenderà, oltre che dall'algoritmo di
scheduling usato dal kernel, dalla particolare situazione in si trova la
proprietà; la lista dettagliata delle proprietà che padre e figlio hanno in
comune dopo l'esecuzione di una \func{fork} è la seguente:
\begin{itemize*}
-\item i file aperti e gli eventuali flag di \textit{close-on-exec} impostati
- (vedi \secref{sec:proc_exec} e \secref{sec:file_fcntl}).
+\item i file aperti e gli eventuali flag di
+ \textit{close-on-exec}\index{close-on-exec} impostati (vedi
+ \secref{sec:proc_exec} e \secref{sec:file_fcntl}).
\item gli identificatori per il controllo di accesso: l'\textsl{userid reale},
il \textsl{groupid reale}, l'\textsl{userid effettivo}, il \textsl{groupid
effettivo} ed i \textit{groupid supplementari} (vedi
\cmd{init}).
\item viene inviato il segnale \macro{SIGCHLD} al processo padre (vedi
\secref{sec:sig_sigchld}).
-\item se il processo è un leader di sessione viene mandato un segnale di
- \macro{SIGHUP} a tutti i processi in background e il terminale di
- controllo viene disconnesso (vedi \secref{sec:sess_xxx}).
+\item se il processo è un leader di sessione ed il suo terminale di controllo
+ è quello della sessione viene mandato un segnale di \macro{SIGHUP} a tutti i
+ processi del gruppo di foreground e il terminale di controllo viene
+ disconnesso (vedi \secref{sec:sess_ctrl_term}).
\item se la conclusione di un processo rende orfano un \textit{process
group} ciascun membro del gruppo viene bloccato, e poi gli vengono
inviati in successione i segnali \macro{SIGHUP} e \macro{SIGCONT}
- (vedi \secref{sec:sess_ctrl_term}).
+ (vedi ancora \secref{sec:sess_ctrl_term}).
\end{itemize*}
Oltre queste operazioni è però necessario poter disporre di un meccanismo
processo figlio termina. Se un figlio è già terminato la funzione ritorna
immediatamente.
-Al ritorno lo stato di terminazione del processo viene salvato nella
+Al ritorno, lo stato di terminazione del processo viene salvato nella
variabile puntata da \var{status} e tutte le informazioni relative al
processo (vedi \secref{sec:proc_termination}) vengono rilasciate. Nel
caso un processo abbia più figli il valore di ritorno permette di
\end{functions}
Per capire meglio le differenze fra le funzioni della famiglia si può fare
-riferimento allo specchietto riportato in \ntab. La prima differenza riguarda
-le modalità di passaggio dei parametri che poi andranno a costituire gli
-argomenti a linea di comando (cioè i valori di \var{argv} e \var{argc} visti
-dalla funzione \func{main} del programma chiamato).
+riferimento allo specchietto riportato in \tabref{tab:proc_exec_scheme}. La
+prima differenza riguarda le modalità di passaggio dei parametri che poi
+andranno a costituire gli argomenti a linea di comando (cioè i valori di
+\var{argv} e \var{argc} visti dalla funzione \func{main} del programma
+chiamato).
Queste modalità sono due e sono riassunte dagli mnemonici \code{v} e \code{l}
che stanno rispettivamente per \textit{vector} e \textit{list}. Nel primo caso
\secref{sec:sig_gen_beha}).
La gestione dei file aperti dipende dal valore che ha il flag di
-\textit{close-on-exec} (trattato in \secref{sec:file_fcntl}) per ciascun file
-descriptor. I file per cui è 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.
+\textit{close-on-exec}\index{close-on-exec} (vedi anche
+\secref{sec:file_fcntl}) per ciascun file descriptor. I file per cui è
+impostato vengono chiusi, tutti gli altri file restano aperti. Questo
+significa che il comportamento predefinito è che i file restano aperti
+attraverso una \func{exec}, a meno di una chiamata esplicita a \func{fcntl}
+che imposti il suddetto flag.
Per le directory, lo standard POSIX.1 richiede che esse vengano chiuse
attraverso una \func{exec}, in genere questo è fatto dalla funzione
\func{opendir} (vedi \secref{sec:file_dir_read}) che effettua da sola
-l'impostazione del flag di \textit{close-on-exec} sulle directory che apre, in
-maniera trasparente all'utente.
+l'impostazione del flag di \textit{close-on-exec}\index{close-on-exec} sulle
+directory che apre, in maniera trasparente all'utente.
Abbiamo detto che l'\textsl{userid reale} ed il \textsl{groupid reale} restano
gli stessi all'esecuzione di \func{exec}; lo stesso vale per l'\textsl{userid
Con la famiglia delle \func{exec} si chiude il novero delle funzioni su cui è
basata la gestione dei processi in Unix: con \func{fork} si crea un nuovo
-processo, con \func{exec} si avvia un nuovo programma, con \func{exit} e
-\func{wait} si effettua e verifica la conclusione dei programmi. Tutte le
-altre funzioni sono ausiliarie e servono la lettura e l'impostazione dei vari
-parametri connessi ai processi.
+processo, con \func{exec} si lancia un nuovo programma, con \func{exit} e
+\func{wait} si effettua e verifica la conclusione dei processi. Tutte le
+altre funzioni sono ausiliarie e servono per la lettura e l'impostazione dei
+vari parametri connessi ai processi.
imposta questi identificatori ai valori corrispondenti all'utente che entra
nel sistema.
-Al secondo gruppo appartengono l'\textsl{userid effettivo} e l'\textsl{groupid
- effettivo} (a cui si aggiungono gli eventuali \textsl{groupid supplementari}
-dei gruppi dei quali l'utente fa parte). Questi sono invece gli
-identificatori usati nella verifiche dei permessi del processo e per il
+Al secondo gruppo appartengono lo \textsl{userid effettivo} ed il
+\textsl{groupid effettivo} (a cui si aggiungono gli eventuali \textsl{groupid
+ supplementari} dei gruppi dei quali l'utente fa parte). Questi sono invece
+gli identificatori usati nella verifiche dei permessi del processo e per il
controllo di accesso ai file (argomento affrontato in dettaglio in
\secref{sec:file_perm_overview}).
\macro{EPERM}).
Come accennato l'uso principale di queste funzioni è quello di poter
-consentire ad un programma con i bit \acr{suid} o \acr{sgid} impostati di
-riportare l'\textsl{userid effettivo} a quello dell'utente che ha lanciato il
-programma, effettuare il lavoro che non necessita di privilegi aggiuntivi, ed
-eventualmente tornare indietro.
+consentire ad un programma con i bit \acr{suid} o \acr{sgid} impostati (vedi
+\secref{sec:file_suid_sgid}) di riportare l'\textsl{userid effettivo} a quello
+dell'utente che ha lanciato il programma, effettuare il lavoro che non
+necessita di privilegi aggiuntivi, ed eventualmente tornare indietro.
Come esempio per chiarire l'uso di queste funzioni prendiamo quello con cui
viene gestito l'accesso al file \file{/var/log/utmp}. In questo file viene
qualunque. Specificando un argomento di valore -1 l'identificatore
corrispondente verrà lasciato inalterato.
-Con queste funzione si possono scambiare fra loro gli userid reale e
+Con queste funzioni si possono scambiare fra loro gli userid reale e
effettivo, e pertanto è possibile implementare un comportamento simile a
quello visto in precedenza per \func{setgid}, cedendo i privilegi con un primo
scambio, e recuperandoli, eseguito il lavoro non privilegiato, con un secondo
\label{sec:proc_priority}
In questa sezione tratteremo più approfonditamente i meccanismi con il quale
-lo \textit{scheduler} assegna la CPU ai vari processi attivi. In particolare
-prenderemo in esame i vari meccanismi con cui viene gestita l'assegnazione del
-tempo di CPU, ed illustreremo le varie funzioni di gestione.
+lo \textit{scheduler}\index{scheduler} assegna la CPU ai vari processi attivi.
+In particolare prenderemo in esame i vari meccanismi con cui viene gestita
+l'assegnazione del tempo di CPU, ed illustreremo le varie funzioni di
+gestione.
\subsection{I meccanismi di \textit{scheduling}}
multitasking}) non sono i singoli processi, ma il kernel stesso a decidere
quando la CPU deve essere passata ad un altro processo. Come accennato in
\secref{sec:proc_hierarchy} questa scelta viene eseguita da una sezione
-apposita del kernel, lo \textit{scheduler}, il cui scopo è quello di
-distribuire al meglio il tempo di CPU fra i vari processi.
+apposita del kernel, lo \textit{scheduler}\index{scheduler}, il cui scopo è
+quello di distribuire al meglio il tempo di CPU fra i vari processi.
La cosa è resa ancora più complicata dal fatto che con le architetture
multi-processore si deve anche scegliere quale sia la CPU più opportuna da
fintanto che esso si trova in uno qualunque degli altri stati.
\begin{table}[htb]
+ \footnotesize
\centering
\begin{tabular}[c]{|p{2.8cm}|c|p{10cm}|}
\hline
aspettare la conclusione di altri che non hanno questa necessità.
Il concetto di priorità assoluta dice che quando due processi si contendono
-l'esecuzione, vince sempre quello con la priorità assoluta più alta, anche
-quando l'altro è in esecuzione (grazie al \textit{prehemptive scheduling}).
+l'esecuzione, vince sempre quello con la priorità assoluta più alta.
Ovviamente questo avviene solo per i processi che sono pronti per essere
eseguiti (cioè nello stato \textit{runnable}). La priorità assoluta viene in
genere indicata con un numero intero, ed un valore più alto comporta una
processo viene eseguito per la prima volta e diminuito progressivamente ad
ogni interruzione del timer.
-Quando lo scheduler viene eseguito scandisce la coda dei processi in stato
-\textit{runnable} associando, sulla base del valore di \var{counter}, un peso
-a ciascun processo in attesa di esecuzione,\footnote{il calcolo del peso in
- realtà è un po' più complicato, ad esempio nei sistemi multiprocessore viene
- favorito un processo che è eseguito sulla stessa CPU, e a parità del valore
- di \var{counter} viene favorito chi ha una priorità più elevata.} chi ha il
-peso più alto verrà posto in esecuzione, ed il precedente processo sarà
-spostato in fondo alla coda. Dato che ad ogni interruzione del timer il
-valore di \var{counter} del processo corrente viene diminuito, questo assicura
-che anche i processi con priorità più bassa verranno messi in esecuzione.
+Quando lo scheduler\index{scheduler} viene eseguito scandisce la coda dei
+processi in stato \textit{runnable} associando, sulla base del valore di
+\var{counter}, un peso a ciascun processo in attesa di esecuzione,\footnote{il
+ calcolo del peso in realtà è un po' più complicato, ad esempio nei sistemi
+ multiprocessore viene favorito un processo che è eseguito sulla stessa CPU,
+ e a parità del valore di \var{counter} viene favorito chi ha una priorità
+ più elevata.} chi ha il peso più alto verrà posto in esecuzione, ed il
+precedente processo sarà spostato in fondo alla coda. Dato che ad ogni
+interruzione del timer il valore di \var{counter} del processo corrente viene
+diminuito, questo assicura che anche i processi con priorità più bassa
+verranno messi in esecuzione.
La priorità di un processo è così controllata attraverso il valore di
\var{nice}, che stabilisce la durata della \textit{time-slice}; per il
meccanismo appena descritto infatti un valore più lungo infatti assicura una
maggiore attribuzione di CPU. L'origine del nome di questo parametro sta nel
-fatto che in genere esso viene generalmente usato per diminuire la priorità di
-un processo, come misura di cortesia nei confronti degli altri.
-I processi infatti vengono creati dal sistema con lo stesso valore di
-\var{nice} (nullo) e nessuno è privilegiato rispetto agli altri; il valore può
-essere modificato solo attraverso la funzione \func{nice}, il cui prototipo è:
+fatto che generalmente questo viene usato per diminuire la priorità di un
+processo, come misura di cortesia nei confronti degli altri. I processi
+infatti vengono creati dal sistema con lo stesso valore di \var{nice} (nullo)
+e nessuno è privilegiato rispetto agli altri; il valore può essere modificato
+solo attraverso la funzione \func{nice}, il cui prototipo è:
\begin{prototype}{unistd.h}
{int nice(int inc)}
Aumenta il valore di \var{nice} per il processo corrente.
nel caso di Linux non si tratta di un vero hard real-time, in quanto in
presenza di eventuali interrupt il kernel interrompe l'esecuzione di un
processo qualsiasi sia la sua priorità,\footnote{questo a meno che non si
- siano installate le patch di RTLinux o RTAI, con i quali è possibile
+ 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, e gestiti
+ interrupt vengono intercettati dall'interfaccia real-time (o nel caso di
+ Adeos gestiti dalle code del nano-kernel), in modo da poterlo controllare
direttamente qualora ci sia la necessità di avere un processo con priorità
più elevata di un \textit{interrupt handler}.} mentre con l'incorrere in un
page fault\index{page fault} si possono avere ritardi non previsti. Se
shell cui si sia assegnata la massima priorità assoluta, in modo da poter
essere comunque in grado di rientrare nel sistema.
-Quando c'è un processo con priorità assoluta lo scheduler 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
-tocca al kernel decidere quale deve essere eseguito.
-
-
+Quando c'è un processo con priorità assoluta lo scheduler\index{scheduler} lo
+metterà in esecuzione prima di ogni processo normale. In caso di più processi
+sarà eseguito per primo quello con priorità assoluta più alta. Quando ci sono
+più processi con la stessa priorità assoluta questi vengono tenuti in una coda
+tocca al kernel decidere quale deve essere eseguito.
Il meccanismo con cui vengono gestiti questi processi dipende dalla politica
di scheduling che si è scelto; lo standard ne prevede due:
-\begin{basedescript}{\desclabelwidth{3cm}\desclabelstyle{\nextlinelabel}}
-\item[\textit{FIFO}] il processo viene eseguito fintanto che non cede
- volontariamente la CPU, si blocca, finisce o viene interrotto da un processo
- a priorità più alta.
-\item[\textit{Round Robin}] ciascun processo viene eseguito a turno per un
- certo periodo di tempo (una \textit{time slice}). Solo i processi con la
- stessa priorità ed in stato \textit{runnable} entrano nel circolo.
+\begin{basedescript}{\desclabelwidth{2cm}\desclabelstyle{\nextlinelabel}}
+\item[\textit{FIFO}] \textit{First In First Out}. Il processo viene eseguito
+ fintanto che non cede volontariamente la CPU, si blocca, finisce o viene
+ interrotto da un processo a priorità più alta.
+\item[\textit{RR}] \textit{Round Robin}. Ciascun processo viene eseguito a
+ turno per un certo periodo di tempo (una \textit{time slice}). Solo i
+ processi con la stessa priorità ed in stato \textit{runnable} entrano nel
+ circolo.
\end{basedescript}
La funzione per impostare le politiche di scheduling (sia real-time che
nel qual caso \var{errno} viene impostata opportunamente.}
\end{prototype}
-La funzione fa si che il processo rilasci la CPU, in modo da essere rimesso in
+La funzione fa sì che il processo rilasci la CPU, in modo da essere rimesso in
coda alla lista dei processi da eseguire, e permettere l'esecuzione di un
altro processo; se però il processo è l'unico ad essere presente sulla coda
l'esecuzione non sarà interrotta. In genere usano questa funzione i processi
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.
+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
+una operazione che dovrebbe essere eseguita dall'altro.
+
+
L'esempio tipico di una situazione che può condurre ad un \textit{deadlock} è
quello in cui un flag di ``occupazione'' viene rilasciato da un evento
asincrono (come un segnale o un altro processo) fra il momento in cui lo si è