Dato che tutti i processi attivi nel sistema sono comunque generati da
\cmd{init} o da uno dei suoi figli\footnote{in realtà questo non è del tutto
- vero, in Linux ci sono alcuni processi che pur comparendo come figli di
- init, o con \acr{pid} successivi, sono in realtà generati direttamente dal
- kernel, (come \cmd{keventd}, \cmd{kswapd}, etc.)} si possono classificare i
-processi con la relazione padre/figlio in 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.
-
+ vero, in Linux ci sono alcuni processi speciali che pur comparendo come
+ figli di \cmd{init}, o con \acr{pid} successivi, sono in realtà generati
+ direttamente dal kernel, (come \cmd{keventd}, \cmd{kswapd}, etc.).} si
+possono classificare i processi con la relazione padre/figlio in
+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.
Il kernel mantiene una tabella dei processi attivi, la cosiddetta
\textit{process table}; per ciascun processo viene mantenuta una voce nella
tabella dei processi costituita da una struttura \type{task\_struct}, che
contiene tutte le informazioni rilevanti per quel processo. Tutte le strutture
usate a questo scopo sono dichiarate nell'header file \file{linux/sched.h}, ed
-uno schema semplificato che riporta la struttura delle principali informazioni
+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.
system call ed ad ogni interrupt, (ma può essere anche attivato
esplicitamente). Il timer di sistema provvede comunque a che esso sia invocato
periodicamente, generando un interrupt periodico secondo la frequenza
-specificata dalla costante \macro{HZ}, definita in \file{asm/param.h} Il
-valore usuale è 100 (è espresso in Hertz), si ha cioè un interrupt dal timer
-ogni centesimo di secondo.
+specificata dalla costante \macro{HZ}, definita in \file{asm/param.h}. Il
+valore usuale è 100\footnote{è così per tutte le architetture eccetto l'alpha,
+ per la quale è 1000} ed è espresso in Hertz. Si ha cioè un interrupt dal
+timer ogni centesimo di secondo.
Ogni volta che viene eseguito, lo \textit{scheduler} effettua il calcolo delle
priorità dei vari processi attivi (torneremo su questo in
\type{int}).
Il \acr{pid} viene assegnato in forma progressiva ogni volta che un nuovo
-processo viene creato, fino ad un limite massimo (in genere essendo detto
-numero memorizzato in un intero a 16 bit si arriva a 32767) oltre il quale si
-riparte dal numero più basso disponibile\footnote{FIXME: verificare, non sono
- sicuro}. Per questo motivo, come visto in \secref{sec:proc_hierarchy}, il
-processo di avvio (\cmd{init}) ha sempre il \acr{pid} uguale a uno.
+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.
Tutti i processi inoltre memorizzano anche il \acr{pid} del genitore da cui
sono stati creati, questo viene chiamato in genere \acr{ppid} (da
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
+ 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
\item ad ogni processo figlio viene assegnato un nuovo padre (in genere
\cmd{init}).
\item viene inviato il segnale \macro{SIGCHLD} al processo padre (vedi
- \secref{sec:sig_xxx}).
+ \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}).
creare molti figli. In questo caso si deve sempre avere cura di far leggere
l'eventuale stato di uscita di tutti i figli (in genere questo si fa
attraverso un apposito \textit{signal handler}, che chiama la funzione
-\func{wait}, vedi \secref{sec:sig_xxx} e \secref{sec:proc_wait}). Questa
+\func{wait}, vedi \secref{sec:sig_sigchld} e \secref{sec:proc_wait}). Questa
operazione è necessaria perché anche se gli \textit{zombie} non consumano
risorse di memoria o processore, occupano comunque una voce nella tabella dei
processi, che a lungo andare potrebbe esaurirsi.
processo figlio termina. Se un figlio è già terminato la funzione ritorna
immediatamente.
-Al ritorno lo stato di termininazione 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
\begin{table}[!htb]
\centering
\footnotesize
- \begin{tabular}[c]{|c|p{10cm}|}
+ \begin{tabular}[c]{|c|c|p{8cm}|}
\hline
- \textbf{Valore} & \textbf{Significato}\\
+ \textbf{Valore} & \textbf{Macro} &\textbf{Significato}\\
\hline
\hline
- $<-1$& attende per un figlio il cui \textit{process group} è uguale al
+ $<-1$& -- & attende per un figlio il cui \textit{process group} è uguale al
valore assoluto di \var{pid}. \\
- $-1$ & attende per un figlio qualsiasi, usata in questa maniera è
- equivalente a \func{wait}.\\
- $0$ & attende per un figlio il cui \textit{process group} è uguale a
- quello del processo chiamante. \\
- $>0$ & attende per un figlio il cui \acr{pid} è uguale al
+ $-1$ & \macro{WAIT\_ANY} & attende per un figlio qualsiasi, usata in
+ questa maniera è equivalente a \func{wait}.\\
+ $0$ & \macro{WAIT\_MYPGRP} & attende per un figlio il cui \textit{process
+ group} è uguale a quello del processo chiamante. \\
+ $>0$ & -- &attende per un figlio il cui \acr{pid} è uguale al
valore di \var{pid}.\\
\hline
\end{tabular}
\macro{WIFSIGNALED} ha restituito un valore non nullo.\\
\macro{WCOREDUMP(s)} & Vera se il processo terminato ha generato un
file si \textit{core dump}. Può essere valutata solo se
- \macro{WIFSIGNALED} ha restituito un valore non nullo\footnote{questa
+ \macro{WIFSIGNALED} ha restituito un valore non nullo.\footnote{questa
macro non è definita dallo standard POSIX.1, ma è presente come estensione
- sia in Linux che in altri unix}.\\
+ sia in Linux che in altri Unix.}\\
\macro{WIFSTOPPED(s)} & Vera se il processo che ha causato il ritorno di
\func{waitpid} è bloccato. L'uso è possibile solo avendo specificato
l'opzione \macro{WUNTRACED}. \\
\item il \textit{session id} ed il \textit{process group id} (vedi
\secref{sec:sess_xxx}).
\item il terminale di controllo (vedi \secref{sec:sess_xxx}).
-\item il tempo restante ad un allarme (vedi \secref{sec:sig_xxx}).
+\item il tempo restante ad un allarme (vedi \secref{sec:sig_alarm_abort}).
\item la directory radice e la directory di lavoro corrente (vedi
\secref{sec:file_work_dir}).
\item la maschera di creazione dei file (\var{umask}, vedi
Come accennato in \secref{sec:intro_multiuser} il modello base\footnote{in
realtà già esistono estensioni di questo modello base, che lo rendono più
flessibile e controllabile, come le \textit{capabilities}, le ACL per i file
- o il \textit{Mandatory Access Control} di SELinux} di sicurezza di un
+ o il \textit{Mandatory Access Control} di SELinux.} di sicurezza di un
sistema unix-like è fondato sui concetti di utente e gruppo, e sulla
separazione fra l'amministratore (\textsl{root}, detto spesso anche
\textit{superuser}) che non è sottoposto a restrizioni, ed il resto degli
\hline
\end{tabular}
\caption{Identificatori di utente e gruppo associati a ciascun processo con
- indicazione dei suffissi usate dalle varie funzioni di manipolazione.}
+ indicazione dei suffissi usati dalle varie funzioni di manipolazione.}
\label{tab:proc_uid_gid}
\end{table}
In questa sezione tratteremo più approfonditamente i meccanismi con il quale
lo \textit{scheduler}\footnote{che è la parte del kernel che si occupa di
stabilire quale processo dovrà essere posto in esecuzione.} assegna la CPU
-ai vari processi attivi. In particolare prendremo in esame i vari meccanismi
+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.
ed oggetto di numerose ricerche; in ogni caso essa dipende in maniera
essenziale anche dal tipo di utilizzo che deve essere fatto del sistema.
-La cosa è resa ancora più complicata dal fatto che con le architetture
-multi-processore si introduce anche la problematica dovuta alla scelta di
-quale sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni
- la presenza di ampie cache può rendere poco efficiente trasferire
- l'esecuzione di un processo da una CPU ad un'altra, per cui occorrono
- meccanismi per determininare quale è la migliore scelta fra le diverse CPU.}
-Tutto questo comunque appartiene alle sottigliezze dell'implementazione del
-kernel, e dal punto di vista dei programmi che girano in user space anche
-quando si hanno più processori, e quindi potenzialmente anche dei processi che
-sono eseguiti davvero in contemporanea, si può pensare alle politiche di
-scheduling come concernenti la risorsa \textsl{tempo di esecuzione}, la cui
-assegnazione sarà governata dagli stessi meccanismi di scelta di priorità,
-solo che nel caso di più processori sarà a disposizione di più di un processo
-alla volta.
-
-Si tenga presente inoltre che l'utilizzo della CPU è soltanto una delle
+Si tenga presente comunque che l'utilizzo della CPU è soltanto una delle
risorse (insieme alla memoria e all'accesso alle periferiche) che sono
necessarie per l'esecuzione di un programma, e spesso non è neanche la più
importante. Per questo non è affatto detto che dare ad un programma la massima
priorità di esecuzione abbia risultati significativi in termini di
prestazioni.
-La politica tradizionale di scheduling di Unix (che tratteremo in
-\secref{sec:proc_sched_stand}) è sempre stata basata su delle priorità
-dinamiche, che assicurassero che tutti i processi, anche i meno importanti,
-potessero ricevere un po' di tempo di CPU.
-
-Lo standard POSIX però per tenere conto dei sistemi real-time,\footnote{per
- sistema real-time si intende un sistema in grado di eseguire operazioni in
- tempo reale; in genere si tende a distinguere fra l'\textit{hard real-time}
- in cui è necessario che i tempi di esecuzione di un programma siano
- determinabili con certezza assoluta, come nel caso di meccanismi di
- controllo di macchine, dove uno sforamento dei tempi avrebbe conseguenze
- disastrose, e \textit{soft-real-time} in cui un occasionale sforamento è
- ritenuto accettabile.} in cui è vitale che i processi che devono essere
-eseguiti in un determinato momento non debbano aspettare la conclusione di
-altri processi che non hanno questa necessità, ha introdotto il concetto di
-\textsl{priorità assoluta}, chimata anche \textsl{priorità statica}, in
-contrapposizione con la normale priorità dinamica.
-
-Il concetto di prorità assoluta dice che quando due processi si contendono
-l'esecuzione, vince sempre quello con la priorità assoluta più alta, anche,
-grazie al \textit{prehemptive scheduling}, se l'altro è in esecuzione.
-Ovviamente questo avviene solo per i processi che sono pronti per essere
-eseguiti (cioè nello stato \textit{runnable}\footnote{lo stato di un processo
+La caratteristica specifica di un sistema multitasking come Linux è quella del
+cosiddetto \textit{prehemptive multitasking}: questo significa che al
+contrario di altri sistemi (che usano invece il cosiddetto \textit{cooperative
+ multitasking}) non sono i singoli processi, ma il kernel stesso a decidere
+quando la CPU deve essere passata ad un altro processo. Come accennato in
+\secref{sec:proc_hierarchy} questa scelta viene eseguita dallo
+\textit{scheduler} il cui scopo è quello di distribuire al meglio il tempo di
+CPU fra i vari processi.
+
+Il Linux un processo può trovarsi in uno degli stati riportati in
+
+
+
+\begin{table}[htb]
+ \centering
+ \begin{tabular}[c]{|l|c|p{8cm}|}
+ \hline
+ \textbf{Stato} & \texttt{STAT} & \textbf{Descrizione} \\
+ \hline
+ \hline
+ \textbf{Runnable} & \texttt{R} & Il processo è in esecuzione o è pronto ad
+ essere eseguito (cioè è in attesa che gli venga assegnata la CPU). \\
+ \textbf{Sleep} & \texttt{S} & Il processo processo è in attesa di un
+ risposta dal sistema, ma può essere interrotto da un segnale. \\
+ \textbf{Uninterrutible Sleep} & \texttt{D} & Il processo è in
+ attesa di un risposta dal sistema (in genere per I/O), e non può essere
+ interrotto in nessuna circostanza. \\
+ \textbf{Stopped} & \texttt{T} & Il processo è stato fermato con un
+ \macro{SIGSTOP}, o è tracciato.\\
+ \textbf{Zombie} & \texttt{Z} & Il processo è terminato ma il suo stato di
+ terminazione non è ancora stato letto dal padre. \\
+ \hline
+ \end{tabular}
+ \caption{Tabella degli stati possibili per processo in Linux, si sono
+ riportati nella colonna \texttt{STAT} i valori dello stato ottenibili
+ dall'output del comando \cmd{ps}.}
+ \label{tab:proc_proc_states}
+\end{table}
+
+
+
+
+Una delle caratteristiche c
+
+,\footnote{lo stato di un processo
è riportato nel campo \texttt{STAT} dell'output del comando \cmd{ps},
abbiamo già visto che lo stato di \textit{zombie} è indicato con \texttt{Z},
gli stati \textit{runnable}, \textit{sleep} e di I/O (\textit{uninteruttible
- sleep}) sono invece indicati con \texttt{R}, \texttt{S} e \texttt{D}.}),
+ sleep}) sono invece indicati con \texttt{R}, \texttt{S} e \texttt{D}.})
la priorità assoluta viene invece ignorata per quelli che sono bloccati su una
-richiesta di I/O o in stato di \textit{sleep}.
-
-Questa viene in genere indicata con un numero
-
+richiesta di I/O o in stato di \textit{sleep}
+La cosa è resa ancora più complicata dal fatto che con le architetture
+multi-processore si introduce anche la problematica dovuta alla scelta di
+quale sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni
+ la presenza di ampie cache può rendere poco efficiente trasferire
+ l'esecuzione di un processo da una CPU ad un'altra, per cui occorrono
+ meccanismi per determinare quale è la migliore scelta fra le diverse CPU.}
+Tutto questo comunque appartiene alle sottigliezze dell'implementazione del
+kernel, e dal punto di vista dei programmi che girano in user space, anche
+quando si hanno più processori (e dei processi che sono eseguiti davvero in
+contemporanea), si può pensare alle politiche di scheduling come concernenti
+la risorsa \textsl{tempo di esecuzione}, la cui assegnazione sarà governata
+dagli stessi meccanismi di scelta di priorità, solo che nel caso di più
+processori sarà a disposizione di più di un processo alla volta.
+
+
+
+
+Il meccanismo tradizionale di scheduling di Unix (che tratteremo in
+\secref{sec:proc_sched_stand}) è sempre stato basato su delle \textsl{priorità
+ dinamiche}, in modo da assicurare che tutti i processi, anche i meno
+importanti, possano ricevere un po' di tempo di CPU. In sostanza quando 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
+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
+ distinguere fra l'\textit{hard real-time} in cui è necessario che i tempi di
+ esecuzione di un programma siano determinabili con certezza assoluta (come
+ nel caso di meccanismi di controllo di macchine, dove uno sforamento dei
+ tempi avrebbe conseguenze disastrose), e \textit{soft-real-time} in cui un
+ occasionale sforamento è ritenuto accettabile.} in cui è vitale che i
+processi che devono essere eseguiti in un determinato momento non debbano
+aspettare la conclusione di altri che non hanno questa necessità.
+
+Il concetto di priorità assoluta dice che quando due processi si contendono
+l'esecuzione, vince sempre quello con la priorità assoluta più alta, anche
+quando l'altro è in esecuzione (grazie al \textit{prehemptive scheduling}).
+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
+priorità maggiore. Su questa politica di scheduling torneremo in
+\secref{sec:proc_real_time}.
+
+In generale quello che succede in tutti gli Unix moderni è che ai processi
+normali viene sempre data una priorità assoluta pari a zero, e la decisione di
+assegnazione della CPU è fatta solo in base ad una priorità dinamica che è
+calcolata indipendentemente. È tuttavia possibile assegnare anche una priorità
+assoluta nel qual caso un processo avrà la precedenza su tutti gli altri di
+priorità inferiore che saranno eseguiti solo quando quest'ultimo non avrà
+bisogno della CPU.
\subsection{Il meccanismo di \textit{scheduling} standard}
\label{sec:proc_sched_stand}
-In Linux tutti i processi hanno sostanzialmente la stessa priorità; benché sia
-possibile specificare una priorità assoluta secondo lo standard POSIX
-(argomento che tratteremo più avanti) l'uso comune segue quello che è il
-meccanismo tradizionale con cui i sistemi
+A meno che non si abbiano esigenze specifiche, l'unico meccanismo di
+scheduling con il quale si avrà a che fare è quello tradizionale, che prevede
+solo priorità dinamiche. È di questo che, di norma, ci si dovrà preoccupare
+nella programmazione.
+
+Come accennato in Linux tutti i processi ordinari hanno la stessa priorità
+assoluta. Quello che determina quale, fra tutti i processi in attesa di
+esecuzione, sarà eseguito per primo, è la priorità dinamica, che è chiamata
+così proprio perché varia nel corso dell'esecuzione di un processo. Oltre a
+questo la priorità dinamica determina quanto a lungo un processo continuerà ad
+essere eseguito, e quando un processo potrà subentrare ad un altro
+nell'esecuzione.
+
+
+
\subsection{Il meccanismo di \textit{scheduling real-time}}
\label{sec:proc_real_time}
Per settare le
+\footnote{a meno che non si siano installate le patch di RTLinux o RTAI, con i
+ quali è possibile ottenere un sistema effettivamente hard real-time.}
+
+in realtà non si tratta di un vero hard real-time, in quanto
+ la presenza di eventuali interrupt o di page fault può sempre interrompere
+ l'esecuzione di un processo, a meno di non installare le estensioni di
+ RTLinux o RTAI, il normale kernel non è real-time.
+
+
+
+
\section{Problematiche di programmazione multitasking}
\label{sec:proc_multi_prog}
qualunque momento, e le operazioni di un eventuale \textit{signal handler}
sono compiute nello stesso spazio di indirizzi del processo. Per questo, anche
il solo accesso o l'assegnazione di una variabile possono non essere più
-operazioni atomiche (torneremo su questi aspetti in \secref{sec:sign_xxx}).
+operazioni atomiche (torneremo su questi aspetti in
+\secref{sec:sign_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ò