Quattro chiacchiere sullo scheduling standard di unix (nice &C).
[gapil.git] / prochand.tex
index 0826840cd966782cf3f1c659b23bfa1a4275324e..d4b7de25d080ae3667520c12cce3e7ef3947ded4 100644 (file)
@@ -1063,8 +1063,8 @@ famiglia di funzioni) che possono essere usate per questo compito, in realt
 {int execve(const char *filename, char *const argv[], char *const envp[])}
   Esegue il programma contenuto nel file \param{filename}.
   
-  \bodydesc{La funzione ritorna -1 solo in caso di errore, nel qual caso
-    caso la \var{errno} può assumere i valori:
+  \bodydesc{La funzione ritorna solo in caso di errore, restituendo -1; nel
+    qual caso \var{errno} può assumere i valori:
   \begin{errlist}
   \item[\macro{EACCES}] il file non è eseguibile, oppure il filesystem è
     montato in \cmd{noexec}, oppure non è un file normale o un interprete.
@@ -1832,9 +1832,9 @@ tempo di CPU, ed illustreremo le varie funzioni di gestione.
 
 La scelta di un meccanismo che sia in grado di distribuire in maniera efficace
 il tempo di CPU per l'esecuzione dei processi è sempre una questione delicata,
-ed oggetto di numerose ricerche; in ogni caso essa dipende in maniera
-essenziale anche dal tipo di utilizzo che deve essere fatto del sistema.
-
+ed oggetto di numerose ricerche; in generale essa dipende in maniera
+essenziale anche dal tipo di utilizzo che deve essere fatto del sistema, per
+cui non esiste un meccanismo che sia valido per tutti gli usi.
 
 La caratteristica specifica di un sistema multitasking come Linux è quella del
 cosiddetto \textit{prehemptive multitasking}: questo significa che al
@@ -1846,32 +1846,30 @@ apposita del kernel, lo \textit{scheduler}, il cui scopo 
 distribuire al meglio il tempo di CPU fra i vari processi.
 
 La cosa è resa ancora più complicata dal fatto che con le architetture
-multi-processore si introduce anche la problematica dovuta alla scelta di
-quale sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni
-  la presenza di ampie cache può rendere poco efficiente trasferire
-  l'esecuzione di un processo da una CPU ad un'altra, per cui occorrono
-  meccanismi per determinare quale è la migliore scelta fra le diverse CPU.}
-Tutto questo comunque appartiene alle sottigliezze dell'implementazione del
-kernel, e dal punto di vista dei programmi che girano in user space, anche
-quando si hanno più processori (e dei processi che sono eseguiti davvero in
-contemporanea), si può pensare alle politiche di scheduling come concernenti
-la risorsa \textsl{tempo di esecuzione}, la cui assegnazione sarà governata
-dagli stessi meccanismi di scelta di priorità, solo che nel caso di più
-processori sarà a disposizione di più di un processo alla volta.
+multi-processore si deve anche scegliere quale sia la CPU più opportuna da
+utilizzare.\footnote{nei processori moderni la presenza di ampie cache può
+  rendere poco efficiente trasferire l'esecuzione di un processo da una CPU ad
+  un'altra, per cui effettuare la migliore scelta fra le diverse CPU non è
+  banale.}  Tutto questo comunque appartiene alle sottigliezze
+dell'implementazione del kernel; dal punto di vista dei programmi che girano
+in user space, anche quando si hanno più processori (e dei processi che sono
+eseguiti davvero in contemporanea), le politiche di scheduling riguardano
+semplicemente l'allocazione della risorsa \textsl{tempo di esecuzione}, la cui
+assegnazione sarà governata dai meccanismi di scelta delle priorità che
+restano gli stessi indipendentemente dal numero di processori.
 
 I processi non devono solo eseguire del codice, ad esempio molto spesso
-saranno impegnati in operazioni di I/O, possono venire bloccati da un
-comando dal terminale, sospesi per un certo periodo di tempo. In tutti questi
-casi la CPU diventa disponibile ed è compito dello kernel provvedere a mettere
-in esecuzione un altro processo.
+saranno impegnati in operazioni di I/O, possono venire bloccati da un comando
+dal terminale, sospesi per un certo periodo di tempo. In tutti questi casi la
+CPU diventa disponibile ed è compito dello kernel provvedere a mettere in
+esecuzione un altro processo.
 
 Tutte queste possibilità sono caratterizzate da un diverso \textsl{stato} del
-processo ,
-
-In Linux un processo può trovarsi in uno degli stati riportati in
+processo, in Linux un processo può trovarsi in uno degli stati riportati in
 \tabref{tab:proc_proc_states}; ma soltanto i processi che sono nello stato
-\textit{runnable} concorrono per l'esecuzione. Questo vuol di
-
+\textit{runnable} concorrono per l'esecuzione. Questo vuol dire che, qualunque
+sia la sua priorità, un processo non potrà mai essere messo in esecuzione
+fintanto che esso si trova in uno qualunque degli altri stati.
 
 \begin{table}[htb]
   \centering
@@ -1899,23 +1897,12 @@ In Linux un processo pu
   \label{tab:proc_proc_states}
 \end{table}
 
-
-
 Si deve quindi tenere presente che l'utilizzo della CPU è soltanto una delle
-risorse che sono necessarie per l'esecuzione di un programma, e spesso non è
-neanche la più importante. Per questo motivo non è affatto detto che dare ad
-un programma la massima priorità di esecuzione abbia risultati significativi
-in termini di prestazioni.
-
-
-
-
-Una delle caratteristiche c
-
-la priorità assoluta viene invece ignorata per quelli che sono bloccati su una
-richiesta di I/O o in stato di \textit{sleep}
-
-
+risorse che sono necessarie per l'esecuzione di un programma, e a seconda
+dello scopo del programma non è detto neanche che sia la più importante (molti
+programmi dipendono in maniera molto più critica dall'I/O). Per questo motivo
+non è affatto detto che dare ad un programma la massima priorità di esecuzione
+abbia risultati significativi in termini di prestazioni.
 
 Il meccanismo tradizionale di scheduling di Unix (che tratteremo in
 \secref{sec:proc_sched_stand}) è sempre stato basato su delle \textsl{priorità
@@ -1949,10 +1936,10 @@ priorit
 
 In generale quello che succede in tutti gli Unix moderni è che ai processi
 normali viene sempre data una priorità assoluta pari a zero, e la decisione di
-assegnazione della CPU è fatta solo in base ad una priorità dinamica che è
-calcolata indipendentemente. È tuttavia possibile assegnare anche una priorità
-assoluta nel qual caso un processo avrà la precedenza su tutti gli altri di
-priorità inferiore che saranno eseguiti solo quando quest'ultimo non avrà
+assegnazione della CPU è fatta solo con il meccanismo tradizionale della
+priorità dinamica. In Linux tuttavia è possibile assegnare anche una priorità
+assoluta, nel qual caso un processo avrà la precedenza su tutti gli altri di
+priorità inferiore, che saranno eseguiti solo quando quest'ultimo non avrà
 bisogno della CPU.
 
 
@@ -1972,6 +1959,78 @@ questo la priorit
 essere eseguito, e quando un processo potrà subentrare ad un altro
 nell'esecuzione.
 
+Il meccanismo usato da Linux è piuttosto semplice, ad ogni processo è
+assegnata una \textit{time-slice}, cioè in intervallo di tempo (letteralmente
+una fetta) per il quale esso deve essere eseguito. Il valore della
+\textit{time-slice} è controllato dalla cosiddetta \textit{nice} (o
+\textit{niceness}) del processo.  Essa è contenuta nel campo \var{nice} di
+\var{task\_struct}; tutti i processi vengono creati con lo stesso valore, ed
+essa specifica il valore della durata iniziale della \textit{time-slice} che
+viene assegnato ad un altro campo della struttura (\var{counter}) quando il
+processo viene eseguito per la prima volta e diminuito progressivamente ad
+ogni interruzione del timer.
+
+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.
+
+La priorità di un processo è così controllata attraverso il valore di
+\var{nice}, che stabilisce la durata della \textit{time-slice}; un valore più
+lungo infatti assicura una maggiore attribuzione di CPU.  L'origine di questo
+parametro sta nel fatto che in genere esso viene generalmente usato come per
+diminuire la priorità di un processo, come termine di cortesia (da cui il
+nome) nei confronti degli altri.
+
+I processi infatti vengono creati dal sistema con lo stesso valore di
+\var{nice} (nullo) e nessuno è privilegiato; 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.
+  
+  \bodydesc{La funzione ritorna zero in caso di successo e -1 in caso di
+    errore, nel qual caso \var{errno} può assumere i valori:
+  \begin{errlist}
+  \item[\macro{EPERM}] un utente normale ha specificato un valore di
+    \param{inc} negativo.
+  \end{errlist}}
+\end{prototype}
+
+L'argomento \param{inc} indica l'incremento del valore di \var{nice}:
+quest'ultimo può assumere valori compresi fra \macro{PRIO\_MIN} e
+\macro{PRIO\_MAX} (che nel caso di Linux sono $-19$ e $20$) , ma per
+\param{inc} si può specificare un valore qualunque, positivo o negativo, ed il
+sistema provvederà a troncare il risultato nell'intervallo consentito. Valori
+positivi comportano maggiore \textit{cortesia} e cioè una diminuzione della
+priorità. Solo l'amministratore può specificare valori negativi che permettono
+di aumentare la priorità di un processo.
+
+
+In SUSv2 la funzione ritorna il nuovo valore di \var{nice}; Linux non segue
+questa convenzione, per leggere il nuovo valore di occorre invece usare la
+funzione \func{getpriority}, derivata da BSD, il cui prototipo è:
+\begin{prototype}{sys/resource.h}
+{int getpriority(int which, int who)}
+  
+  Restituisce la priorità per l'insieme dei processi specificati.
+
+  \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso di
+    errore, nel qual caso \var{errno} può assumere i valori:
+  \begin{errlist}
+  \item[\macro{ESRCH}] non c'è nessun processo che corrisponda ai valori di
+  \param{which} e \param{who}.
+  \item[\macro{ESRCH}] il valore di \param{which} non è valido.
+  \end{errlist}}
+\end{prototype}
+
+