+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
+presenza di eventuali interrupt il kernel interrompe l'esecuzione di un
+processo qualsiasi sia la sua priorità,\footnote{questo a meno che non si
+ siano installate le patch di RTLinux, RTAI o Adeos, con i quali è possibile
+ ottenere un sistema effettivamente hard real-time. In tal caso infatti gli
+ interrupt vengono intercettati dall'interfaccia real-time (o nel caso di
+ Adeos gestiti dalle code del nano-kernel), in modo da poterli controllare
+ direttamente qualora ci sia la necessità di avere un processo con priorità
+ più elevata di un \textit{interrupt handler}.} mentre con l'incorrere in un
+\itindex{page~fault} \textit{page fault} si possono avere ritardi non
+previsti. Se l'ultimo problema può essere aggirato attraverso l'uso delle
+funzioni di controllo della memoria virtuale (vedi
+sez.~\ref{sec:proc_mem_lock}), il primo non è superabile e può comportare
+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
+ che consentono di avvicinarsi sempre di più ad un vero e proprio sistema
+ \textit{real-time} estendendo il concetto di \textit{preemption} alle
+ operazioni dello stesso kernel; esistono vari livelli a cui questo può
+ 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
+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 e
+tocca al kernel decidere quale deve essere eseguito. Il meccanismo con cui
+vengono gestiti questi processi dipende dalla politica di scheduling che 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
+ essere eseguiti).
+\item[\textsf{RR}] \textit{Round Robin}. Il comportamento è del tutto analogo
+ 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
+ coda dei processi con la stessa priorità. In questo modo si ha comunque una
+ esecuzione a turno di tutti i processi, da cui il nome della politica. Solo
+ i processi con la stessa priorità ed in stato \textbf{Runnable} entrano nel
+ \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 è:
+\begin{prototype}{sched.h}
+{int sched\_setscheduler(pid\_t pid, int policy, const struct sched\_param *p)}
+ Imposta priorità e politica di scheduling.
+
+ \bodydesc{La funzione ritorna la priorità in caso di successo e $-$1 in caso
+ di errore, nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{ESRCH}] il processo \param{pid} non esiste.
+ \item[\errcode{EINVAL}] il valore di \param{policy} non esiste o il
+ relativo valore di \param{p} non è valido.
+ \item[\errcode{EPERM}] il processo non ha i privilegi per attivare la
+ politica richiesta.
+ \end{errlist}}
+\end{prototype}
+
+La funzione esegue l'impostazione per il processo specificato dall'argomento
+\param{pid}; un valore nullo di questo argomento esegue l'impostazione per il
+processo corrente. La politica di scheduling è specificata
+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
+per \param{policy} mantiene la politica di scheduling corrente.
+
+\begin{table}[htb]
+ \centering
+ \footnotesize
+ \begin{tabular}[c]{|l|l|}
+ \hline
+ \textbf{Policy} & \textbf{Significato} \\
+ \hline
+ \hline
+ \const{SCHED\_FIFO} & Scheduling real-time con politica \textit{FIFO}. \\
+ \const{SCHED\_RR} & Scheduling real-time con politica \textit{Round
+ Robin}. \\
+ \hline
+ \const{SCHED\_OTHER}& Scheduling ordinario.\\
+ \const{SCHED\_BATCH}& Scheduling ordinario con l'assunzione ulteriore di
+ lavoro \textit{CPU intensive}.\footnotemark\\
+ \const{SCHED\_IDLE} & Scheduling di priorità estremamente
+ bassa.\footnotemark\\
+ \hline
+ \end{tabular}
+ \caption{Valori dell'argomento \param{policy} per la funzione
+ \func{sched\_setscheduler}.}
+ \label{tab:proc_sched_policy}
+\end{table}
+
+\footnotetext[41]{introdotto con il kernel 2.6.16.}
+\footnotetext{introdotto con il kernel 2.6.23.}
+
+Con le versioni più recenti del kernel sono state introdotte anche delle
+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.
+
+La politica \const{SCHED\_BATCH} è una variante della politica ordinaria con
+la sola differenza che i processi ad essa soggetti non ottengono, nel calcolo
+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
+nome, per processi che usano molta CPU (come programmi di calcolo) che in
+questo modo sono leggermente sfavoriti rispetto ai processi interattivi che
+devono rispondere a dei dati in ingresso, pur non perdendo il loro valore di
+\textit{nice}.
+
+La politica \const{SCHED\_IDLE} invece è una politica dedicata ai processi che
+si desidera siano eseguiti con la più bassa priorità possibile, ancora più
+bassa di un processo con il minimo valore di \textit{nice}. In sostanza la si
+può utilizzare per processi che devono essere eseguiti se non c'è niente altro
+da fare. Va comunque sottolineato che anche un processo \const{SCHED\_IDLE}
+avrà comunque una sua possibilità di utilizzo della CPU, sia pure in
+percentuale molto bassa.
+
+Qualora si sia richiesta una politica real-time il valore della priorità
+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
+debba essere assegnato all'interno di un intervallo fra un massimo ed un
+minimo che nel caso di Linux sono rispettivamente 1 e 99.
+
+\begin{figure}[!bht]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \includestruct{listati/sched_param.c}
+ \end{minipage}
+ \normalsize
+ \caption{La struttura \structd{sched\_param}.}
+ \label{fig:sig_sched_param}
+\end{figure}
+
+I processi con politica di scheduling ordinaria devono sempre specificare un
+valore nullo di \var{sched\_priority} altrimenti si avrà un errore
+\errcode{EINVAL}, questo valore infatti non ha niente a che vedere con la
+priorità dinamica determinata dal valore di \textit{nice}, che deve essere
+impostato con le funzioni viste in precedenza.
+
+Lo standard POSIX.1b prevede comunque che i due valori della massima e minima
+priorità statica possano essere ottenuti, per ciascuna delle politiche di
+scheduling \textit{real-time}, tramite le due funzioni
+\funcd{sched\_get\_priority\_max} e \funcd{sched\_get\_priority\_min}, i cui
+prototipi sono:
+\begin{functions}
+ \headdecl{sched.h}
+
+ \funcdecl{int sched\_get\_priority\_max(int policy)} Legge il valore
+ massimo della priorità statica per la politica di scheduling \param{policy}.
+
+
+ \funcdecl{int sched\_get\_priority\_min(int policy)} Legge il valore minimo
+ della priorità statica per la politica di scheduling \param{policy}.
+
+ \bodydesc{La funzioni ritornano il valore della priorità in caso di successo
+ e $-1$ in caso di errore, nel qual caso \var{errno} può assumere i valori:
+ \begin{errlist}
+ \item[\errcode{EINVAL}] il valore di \param{policy} non è valido.
+ \end{errlist}}
+\end{functions}
+
+Si tenga presente che quando si imposta una politica di scheduling real-time
+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à
+in quel momento in esecuzione.
+
+Il kernel mantiene i processi con la stessa priorità assoluta in una lista, ed
+esegue sempre il primo della lista, mentre un nuovo processo che torna in
+stato \textbf{Runnable} viene sempre inserito in coda alla lista. Se la
+politica scelta è \const{SCHED\_FIFO} quando il processo viene eseguito viene
+automaticamente rimesso in coda alla lista, e la sua esecuzione continua
+fintanto che non viene bloccato da una richiesta di I/O, o non rilascia
+volontariamente la CPU (in tal caso, tornando nello stato \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.
+
+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à
+assolute diverse da zero o politiche \const{SCHED\_FIFO} e
+\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
+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
+sez.~\ref{sec:sys_resource_limit}) ad essi assegnato. Unica eccezione a questa
+possibilità sono i processi \const{SCHED\_IDLE}, che non possono cambiare
+politica di scheduling indipendentemente dal valore di
+\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
+ordinaria.
+
+Se si intende operare solo sulla priorità statica di un processo si possono
+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{functions}
+ \headdecl{sched.h}
+
+ \funcdecl{int sched\_setparam(pid\_t pid, const struct sched\_param *param)}
+ Imposta la priorità statica del processo \param{pid}.
+
+ \funcdecl{int sched\_getparam(pid\_t pid, struct sched\_param *param)}
+ Legge la priorità statica del processo \param{pid}.
+
+ \bodydesc{Entrambe le funzioni ritornano 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{param} non ha senso per la
+ politica usata dal processo.
+ \item[\errcode{EPERM}] non si hanno privilegi sufficienti per eseguire
+ l'operazione.
+ \end{errlist}}
+\end{functions}
+
+L'uso di \func{sched\_setparam}, compresi i controlli di accesso che vi si
+applicano, è del tutto equivalente a quello di \func{sched\_setscheduler} con
+argomento \param{policy} uguale a -1. Come per \func{sched\_setscheduler}
+specificando 0 come valore dell'argomento \param{pid} si opera sul processo
+corrente. Benché la funzione sia utilizzabile anche con processi sottoposti a
+politica ordinaria essa ha senso soltanto per quelli real-time, dato che per i
+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
+\file{sched.h}.
+
+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
+ 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{EPERM}] non si hanno privilegi sufficienti per eseguire
+ l'operazione.
+ \end{errlist}}
+\end{prototype}
+
+La funzione restituisce il valore, secondo quanto elencato in
+tab.~\ref{tab:proc_sched_policy}, della politica di scheduling per il processo
+specificato; se l'argomento \param{pid} è nullo viene restituito il valore
+relativo al processo chiamante.
+
+L'ultima funzione che permette di leggere le informazioni relative ai processi
+real-time è \funcd{sched\_rr\_get\_interval}, che permette di ottenere la
+lunghezza della \textit{time-slice} usata dalla politica \textit{round robin};
+il suo prototipo è:
+\begin{prototype}{sched.h}
+ {int sched\_rr\_get\_interval(pid\_t pid, struct timespec *tp)} Legge in
+ \param{tp} la durata della \textit{time-slice} per il processo \param{pid}.
+
+ \bodydesc{La funzione ritorna 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{ENOSYS}] la system call non è stata implementata.
+ \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,
+questa funzione ritorna sempre un valore di 150 millisecondi, e non importa
+specificare il PID di un processo reale.
+
+Come accennato ogni processo può rilasciare volontariamente la CPU in modo da
+consentire agli altri processi di essere eseguiti; la funzione che consente di
+fare tutto ciò è \funcd{sched\_yield}, il cui prototipo è:
+\begin{prototype}{sched.h}
+ {int sched\_yield(void)}
+
+ Rilascia volontariamente l'esecuzione.
+
+ \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore,
+ nel qual caso \var{errno} viene impostata opportunamente.}
+\end{prototype}
+
+Questa funzione ha un utilizzo effettivo soltanto quando si usa lo scheduling
+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
+questa funzione i processi con politica \const{SCHED\_FIFO}, per permettere
+l'esecuzione degli altri processi con pari priorità quando la sezione più
+urgente è finita.
+
+La funzione può essere utilizzata anche con processi che usano lo scheduling
+ordinario, ma in questo caso il comportamento non è ben definito, e dipende
+dall'implementazione. Fino al kernel 2.6.23 questo comportava che i processi
+venissero messi in fondo alla coda di quelli attivi, con la possibilità di
+essere rimessi in esecuzione entro breve tempo, con l'introduzione del
+\textit{Completely Fair Scheduler} questo comportamento è cambiato ed un
+processo che chiama la funzione viene inserito nella lista dei processi
+inattivo, con un tempo molto maggiore.\footnote{è comunque possibile
+ ripristinare un comportamento analogo al precedente scrivendo il valore 1
+ nel file \texttt{/proc/sys/kernel/sched\_compat\_yield}.}
+
+
+
+\subsection{Il controllo dello \textit{scheduler} per i sistemi
+ multiprocessore}
+\label{sec:proc_sched_multiprocess}
+
+Infine con il supporto dei sistemi multiprocessore sono state introdotte delle
+funzioni che permettono di controllare in maniera più dettagliata la scelta di
+quale processore utilizzare per eseguire un certo programma. Uno dei problemi
+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
+passa da un processore all'altro in questo modo (cosa che avveniva abbastanza
+di frequente con i kernel della seria 2.4.x) si ha l'\textsl{effetto
+ ping-pong}.
+
+Questo tipo di comportamento può generare dei seri problemi di prestazioni;
+infatti tutti i processori moderni utilizzano una memoria interna (la
+\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
+questa operazione è molto costosa in termini di prestazioni. Il problema
+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
+disponibile.
+
+\itindbeg{CPU~affinity}
+
+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
+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
+stesso processore.
+
+In certi casi però resta l'esigenza di poter essere sicuri che un processo sia
+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
+ funzioni di libreria nelle \textsl{glibc} 2.3.} è stata introdotta
+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 è:
+\begin{prototype}{sched.h}
+ {int sched\_setaffinity (pid\_t pid, unsigned int cpusetsize, const
+ cpu\_set\_t *cpuset)}
+ Imposta la maschera di affinità del processo \param{pid}.
+
+ \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{cpuset} contiene riferimenti a
+ processori non esistenti nel sistema.
+ \item[\errcode{EPERM}] il processo non ha i privilegi sufficienti per
+ eseguire l'operazione.
+ \end{errlist}
+ ed inoltre anche \errval{EFAULT}.}
+\end{prototype}
+