From 2dd133c40d690671199991d8972169c32cbe19a7 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sat, 13 Jun 2009 17:55:03 +0000 Subject: [PATCH] Aggiornamento dello scheduling realtime, con varie correzioni. --- biblio.bib | 34 +++- prochand.tex | 434 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 302 insertions(+), 166 deletions(-) diff --git a/biblio.bib b/biblio.bib index 53ba68a..65f7318 100644 --- a/biblio.bib +++ b/biblio.bib @@ -317,4 +317,36 @@ OPTyear = {}, OPTnote = {}, OPTannote = {} -} \ No newline at end of file +} +@Book{LinuxNutshell, + author = {Greg Kroah-Hartman}, + editor = {O'Reilly}, + title = {Linux kernel in a Nutshell}, + publisher = {O'Reilly}, + year = {2006}, + OPTkey = {}, + OPTvolume = {1}, + OPTnumber = {}, + OPTseries = {}, + OPTaddress = {}, + OPTedition = {}, + OPTmonth = {}, + OPTnote = {}, + OPTannote = {} +} +@Book{LinKernDev, + author = {Robert Love}, + editor = {O'Reilly}, + title = {Linux Kernel Development}, + publisher = {O'Reilly}, + year = {}, + OPTkey = {}, + OPTvolume = {1}, + OPTnumber = {}, + OPTseries = {}, + OPTaddress = {}, + OPTedition = {}, + OPTmonth = {}, + OPTnote = {}, + OPTannote = {} +} diff --git a/prochand.tex b/prochand.tex index 27d7d06..510e69a 100644 --- a/prochand.tex +++ b/prochand.tex @@ -494,7 +494,7 @@ avendo cos viene utilizzato solo quando necessario. Quanto detto in precedenza vale allora soltanto per i kernel fino al 2.4; per mantenere la portabilità è però opportuno non fare affidamento su questo comportamento, che non si riscontra -in altri Unix e nelle versioni del kernel precendenti a quella indicata. +in altri Unix e nelle versioni del kernel precedenti a quella indicata. Si noti inoltre che essendo i segmenti di memoria utilizzati dai singoli processi completamente separati, le modifiche delle variabili nei processi @@ -629,7 +629,8 @@ comune dopo l'esecuzione di una \func{fork} \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 le priorità real-time e le affinità di processore (vedi +\item il valori di \textit{nice}, le priorità real-time e le affinità di + 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*} @@ -2230,7 +2231,8 @@ bisogno della CPU. \subsection{Il meccanismo di \textit{scheduling} standard} \label{sec:proc_sched_stand} -A meno che non si abbiano esigenze 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 nella programmazione. Come accennato in Linux i processi ordinari hanno tutti @@ -2247,39 +2249,49 @@ Il meccanismo usato da Linux 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 al volo, che comunque non è incluso nel kernel - ufficiale).} ma a grandi linee si può dire che ad ogni processo è assegnata -una \textit{time-slice}, cioè un intervallo di tempo (letteralmente una fetta) -per il quale, a meno di eventi esterni, esso viene eseguito senza essere -interrotto. Il valore della \textit{time-slice} è stabilito dalla sua -cosiddetta \textit{nice} (o \textit{niceness}) del processo. Questo è un -valore, che di default è nullo, e che oltre a essere associato alla lunghezza -della \textit{timesllce} viene anche sommato alla priorità dinamica di ciascun -processo. Questa viene calcolata dallo scheduler e viene \textsl{diminuita} -tutte le volte che un processo è in stato \textbf{Runnable} ma non viene posto -in esecuzione. Lo scheduler infatti mette sempre in esecuzione, fra tutti i -processi in stato \textbf{Runnable}, quello che ha la priorità dinamica più -bassa.\footnote{in realtà il calcolo della priorità dinamica e la scelta di - quale processo mettere in esecuzione avviene con un algoritmo più - complicato, (per una buona trattazione vedi \cite{XXX}), ad esempio nei - sistemi multiprocessore viene favorito un processo eseguito sulla stessa - CPU.} - -La priorità di un processo è così controllata attraverso il valore di -\var{nice}, che stabilisce la durata della \textit{time-slice} (un valore di -nice più alto corrisponde ad una \textit{time-sl}, ed ovviamente -più questa è ampia e più lungo sarà il tempo che esso resta in esecuzione in -esecuzione. Ma per il meccanismo appana descritto, andando a sommarsi alla -priorità dianamica, essa tenderà anche a sfavorire (o fa - -per il -meccanismo appena descritto infatti un valore più lungo assicura una maggiore -attribuzione di CPU. L'origine del nome di questo parametro sta nel 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 \funcd{nice}, il cui prototipo è: + 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 +tempo (letteralmente una fetta) per il quale, a meno di eventi esterni, esso +viene eseguito senza essere interrotto. Inoltre la priorità dinamica viene +calcolata dallo scheduler a partire da un valore iniziale che viene +\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à + dinamica e la conseguente scelta di quale processo mettere in esecuzione + avviene con un algoritmo molto più complicato, che tiene conto anche della + \textsl{interattività} del processo, utilizzando diversi fattori, questa è + una brutale semplificazione per rendere l'idea del funzionamento, per una + trattazione più dettagliata, anche se non aggiornatissima, dei meccanismi 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 +diminuito quando un processo non viene posto in esecuzione pur essendo pronto, +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. + +Sia la dimensione della \textit{time-slice} che il valore di partenza della +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 + \texttt{static\_prio} come per le priorità statiche.} L'origine del nome di +questo parametro sta nel fatto che generalmente questo viene usato per +\textsl{diminuire} la priorità di un processo, come misura di cortesia nei +confronti degli altri. I processi infatti vengono creati dal sistema con un +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. + +Esistono diverse funzioni che consentono di modificare la \textit{niceness} di +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. @@ -2288,42 +2300,50 @@ attraverso la funzione \funcd{nice}, il cui prototipo di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\errcode{EPERM}] un processo senza i privilegi di amministratore ha - specificato un valore di \param{inc} negativo. + \item[\errcode{EPERM}] non si ha il permesso di specificare 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 \const{PRIO\_MIN} e -\const{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à, ogni utente può solo innalzare il valore di un suo processo. Solo -l'amministratore può specificare valori negativi che permettono di aumentare -la priorità di un processo. +L'argomento \param{inc} indica l'incremento da effettuare rispetto al valore +di \var{nice} corrente: quest'ultimo può assumere valori compresi fra +\const{PRIO\_MIN} e \const{PRIO\_MAX}; nel caso di Linux sono fra $-20$ e +$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 +il risultato nell'intervallo consentito. Valori positivi comportano maggiore +\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 + 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, questo perché $-1$ è -un valore di \var{nice} legittimo e questo comporta una confusione con una -eventuale condizione di errore. - +convenzione e restituisce sempre 0 in caso di successo e $-1$ in caso di +errore; questo perché $-1$ è un valore di \var{nice} legittimo e questo +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 -valore ottenuto dalla system call, violando lo standard, per cui per ottenere -il nuovo valore occorreva una successiva chiamata alla funzione +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 come funzione di libreria, e restituisce il valore di -\var{nice} come richiesto dallo standard.\footnote{questo viene fatto - chiamando al suo interno \func{getpriority}, ed è questo il motivo delle due - possibilità per i valori di ritorno citati nella descrizione del prototipo.} -In questo caso l'unico modo per rilevare in maniera affidabile una condizione -di errore è quello di azzerare \var{errno} prima della chiamata della funzione -e verificarne il valore quando \func{nice} restituisce $-1$. - - -Per leggere il valore di nice di un processo occorre usare la funzione -\funcd{getpriority}, derivata da BSD; il suo prototipo è: +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 +per rilevare in maniera affidabile una condizione di errore è quello di +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 +funzione \funcd{getpriority}, derivata da BSD; il suo prototipo è: \begin{prototype}{sys/resource.h} {int getpriority(int which, int who)} @@ -2386,22 +2406,43 @@ impostare la priorit \item[\errcode{ESRCH}] non c'è nessun processo che corrisponda ai valori di \param{which} e \param{who}. \item[\errcode{EINVAL}] il valore di \param{which} non è valido. + \item[\errcode{EACCES}] si è richiesto un aumento di priorità senza avere + sufficienti privilegi. \item[\errcode{EPERM}] un processo senza i privilegi di amministratore ha - specificato un valore di \param{inc} negativo. - \item[\errcode{EACCES}] un processo senza i privilegi di amministratore ha cercato di modificare la priorità di un processo di un altro utente. \end{errlist}} \end{prototype} La funzione imposta la priorità al valore specificato da \param{prio} per -tutti i processi indicati dagli argomenti \param{which} e \param{who}. La -gestione dei permessi dipende dalle varie implementazioni; in Linux, secondo -le specifiche dello standard SUSv3, e come avviene per tutti i sistemi che -derivano da SysV, è richiesto che l'user-ID reale o effettivo del processo -chiamante corrispondano al real user-ID (e solo quello) del processo di cui si -vuole cambiare la priorità; per i sistemi derivati da BSD invece (SunOS, -Ultrix, *BSD) la corrispondenza può essere anche con l'user-ID effettivo. - +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 +caso di \func{nice}. La funzione restituisce il valore di \textit{nice} +assegnato in caso di successo e $-1$ in caso di errore, e come per \func{nice} +anche in questo caso per rilevare un errore occorre sempre porre a zero +\var{errno} prima della chiamata della funzione, essendo $-1$ un valore di +\textit{nice} valido. + +Si tenga presente che solo l'amministratore\footnote{o più precisamente un + 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 +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 +l'user-ID effettivo. + +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}). \subsection{Il meccanismo di \textit{scheduling real-time}} @@ -2424,14 +2465,24 @@ 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. -Occorre usare le priorità assolute 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. +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à @@ -2452,21 +2503,22 @@ scelta; lo standard ne prevede due: \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 + \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 \textit{runnable} entrano nel + i processi con la stessa priorità ed in stato \textbf{Runnable} entrano nel \textsl{girotondo}. \end{basedescript} -La funzione per impostare le politiche di scheduling (sia real-time che -ordinarie) ed i relativi parametri è \funcd{sched\_setscheduler}; il suo -prototipo è: +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 + \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. @@ -2478,14 +2530,12 @@ prototipo \end{prototype} La funzione esegue l'impostazione per il processo specificato dall'argomento -\param{pid}; un valore nullo 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}; un valore -negativo per \param{policy} mantiene la politica di scheduling corrente. Solo -un processo con i privilegi di amministratore\footnote{più precisamente con la - \itindex{capabilities} \textit{capability} \const{CAP\_SYS\_NICE}, vedi - sez.~\ref{sec:proc_capabilities}.} può impostare priorità assolute diverse -da zero o politiche \const{SCHED\_FIFO} e \const{SCHED\_RR}. +\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 @@ -2498,10 +2548,12 @@ da zero o politiche \const{SCHED\_FIFO} e \const{SCHED\_RR}. \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.\\ + \const{SCHED\_IDLE} & Scheduling di priorità estremamente + bassa.\footnotemark\\ \hline \end{tabular} \caption{Valori dell'argomento \param{policy} per la funzione @@ -2509,14 +2561,41 @@ da zero o politiche \const{SCHED\_FIFO} e \const{SCHED\_RR}. \label{tab:proc_sched_policy} \end{table} -\footnotetext{introdotto con il kernel 2.6.16.} - -Il valore della priorità è passato attraverso la struttura -\struct{sched\_param} (riportata in fig.~\ref{fig:sig_sched_param}), il cui -solo campo attualmente definito è \var{sched\_priority}, che nel caso delle -priorità assolute deve essere specificato nell'intervallo fra un valore -massimo ed uno minimo, che nel caso sono rispettivamente 1 e 99; il valore -nullo è legale, ma indica i processi normali. +\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 @@ -2528,11 +2607,11 @@ nullo \label{fig:sig_sched_param} \end{figure} -Si tenga presente che quando si imposta una politica di scheduling real-time -per un processo (o se ne cambia la priorità con \func{sched\_setparam}) 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. +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 @@ -2550,90 +2629,112 @@ prototipi sono: 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: + 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} - -I processi con politica di scheduling \const{SCHED\_OTHER} devono specificare -un valore nullo (altrimenti si avrà un errore \errcode{EINVAL}), questo valore -infatti non ha niente a che vedere con la priorità dinamica determinata dal -valore di \var{nice}, che deve essere impostato con le funzioni viste in -precedenza. +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 \textit{runnable} viene sempre inserito in coda alla lista. Se la +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 \textit{runnable} +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. -Se si intende operare solo sulla priorità assoluta di un processo si possono -usare le funzioni \funcd{sched\_setparam} e \funcd{sched\_getparam}, i cui +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 *p)} - Imposta la priorità assoluta del processo \param{pid}. + \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 *p)} - Legge la priorità assoluta del processo \param{pid}. + \funcdecl{int sched\_getparam(pid\_t pid, struct sched\_param *param)} + Legge la priorità statica del processo \param{pid}. - \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{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{p} non ha senso per la - politica scelta. - \item[\errcode{EPERM}] il processo non ha i privilegi sufficienti per - eseguire l'operazione. + \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} che è del tutto equivalente a -\func{sched\_setscheduler} con \param{priority} uguale a -1. Come per -\func{sched\_setscheduler} specificando 0 come valore di \param{pid} si opera -sul processo corrente. La disponibilità di entrambe le funzioni può essere -verificata controllando la macro \macro{\_POSIX\_PRIORITY\_SCHEDULING} che è -definita nell'header \file{sched.h}. - -Si tenga presente che per eseguire la funzione il processo chiamante deve -avere un user-ID effettivo uguale all'user-ID reale o a quello effettivo del -processo di cui vuole cambiare la priorità, oppure deve avere i privilegi di -amministratore (con la capacità \const{CAP\_SYS\_NICE}). - -La priorità assoluta può essere riletta indietro dalla funzione -\funcd{sched\_getscheduler}, il cui prototipo è: +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: + 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{pid} è negativo. - \end{errlist}} + \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 \param{pid} è nullo viene restituito quello del processo -chiamante. +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}; +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}. + \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: @@ -2650,9 +2751,9 @@ dato che in Linux questo intervallo di tempo questa funzione ritorna sempre un valore di 150 millisecondi, e non importa specificare il PID di un processo reale. -Come accennato ogni processo che usa lo scheduling real-time può rilasciare -volontariamente la CPU; questo viene fatto attraverso la funzione -\funcd{sched\_yield}, il cui prototipo è: +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)} @@ -2662,12 +2763,14 @@ volontariamente la CPU; questo viene fatto attraverso la funzione nel qual caso \var{errno} viene impostata opportunamente.} \end{prototype} -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 -in modalità \textit{fifo}, per permettere l'esecuzione degli altri processi -con pari priorità quando la sezione più urgente è finita. +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 in modalità \textit{fifo}, per permettere +l'esecuzione degli altri processi con pari priorità quando la sezione più +urgente è finita. % TODO: con il 2.6.23 il comportamento è stato leggermente modificato ed è % stato introdotto /proc/sys/kernel/sched_compat_yield da mettere a 1 per aver @@ -3030,7 +3133,7 @@ varie funzioni di libreria, che sono identificate aggiungendo il suffisso % LocalWords: parent kernel init pstree keventd kswapd table struct linux call % LocalWords: nell'header scheduler system interrupt timer HZ asm Hertz clock % LocalWords: l'alpha tick fork wait waitpid exit exec image glibc int pgid ps -% LocalWords: sid thread Ingo Molnar ppid getpid getppid sys unistd LD +% LocalWords: sid thread Ingo Molnar ppid getpid getppid sys unistd LD threads % LocalWords: void ForkTest tempnam pathname sibling cap errno EAGAIN ENOMEM % LocalWords: stack read only copy write tab client spawn forktest sleep PATH % LocalWords: source LIBRARY scheduling race condition printf descriptor dup @@ -3072,9 +3175,10 @@ varie funzioni di libreria, che sono identificate aggiungendo il suffisso % LocalWords: waitid NOCLDSTOP ENOCHLD WIFCONTINUED ifdef endif idtype siginfo % LocalWords: infop ALL WEXITED WSTOPPED WNOWAIT signo CLD EXITED KILLED page % LocalWords: CONTINUED sources forking Spawned successfully executing exiting +% LocalWords: next cat for COMMAND pts bash defunct TRAPPED DUMPED Killable PR +% LocalWords: SIGKILL static RLIMIT preemption PREEMPT VOLUNTARY IDLE %%% Local Variables: %%% mode: latex %%% TeX-master: "gapil" %%% End: -% LocalWords: next cat for COMMAND pts bash defunct -- 2.30.2