X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=prochand.tex;h=ba6161970ba489b275e26698814deba11adcc262;hp=5daea9ee0b54c3c567d19fc27cfe403b8dc00da4;hb=d7305d300866c1e6909dd23743060632b3718178;hpb=7d7a13c839bb996fe2d82bb03453bd93a980e5c2 diff --git a/prochand.tex b/prochand.tex index 5daea9e..ba61619 100644 --- a/prochand.tex +++ b/prochand.tex @@ -137,10 +137,12 @@ system call ed ad ogni interrupt,\footnote{pi 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}. 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. +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 @@ -377,7 +379,9 @@ int main(int argc, char *argv[]) Normalmente la chiamata a \func{fork} può fallire solo per due ragioni, o ci sono già troppi processi nel sistema (il che di solito è sintomo che qualcos'altro non sta andando per il verso giusto) o si è ecceduto il limite -sul numero totale di processi permessi all'utente (vedi \secref{sec:sys_xxx}). +sul numero totale di processi permessi all'utente (vedi +\secref{sec:sys_resource_limit}, ed in particolare +\tabref{tab:sys_rlimit_values}). 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 @@ -606,11 +610,11 @@ comune dopo l'esecuzione di una \func{fork} \item la directory di lavoro e la directory radice (vedi \secref{sec:file_work_dir} e \secref{sec:file_chroot}). \item la maschera dei permessi di creazione (vedi \secref{sec:file_umask}). -\item la maschera dei segnali bloccati (vedi \secref{sec:sig_sigpending}) e le +\item la maschera dei segnali bloccati (vedi \secref{sec:sig_sigmask}) e le azioni installate (vedi \secref{sec:sig_gen_beha}). \item i segmenti di memoria condivisa agganciati al processo (vedi \secref{sec:ipc_xxx}). -\item i limiti sulle risorse (vedi \secref{sec:sys_xxx}). +\item i limiti sulle risorse (vedi \secref{sec:sys_resource_limit}). \item le variabili di ambiente (vedi \secref{sec:proc_environ}). \end{itemize*} le differenze fra padre e figlio dopo la \func{fork} invece sono: @@ -619,8 +623,8 @@ le differenze fra padre e figlio dopo la \func{fork} invece sono: \item il \textit{process id}. \item il \textit{parent process id} (quello del figlio viene settato al \acr{pid} del padre). -\item i valori dei tempi di esecuzione (vedi \secref{sec:sys_xxx}) che - nel figlio sono posti a zero. +\item i valori dei tempi di esecuzione della struttura \var{tms} (vedi + \secref{sec:sys_cpu_times}) che nel figlio sono posti a zero. \item i \textit{file lock} (vedi \secref{sec:file_locking}), che non vengono ereditati dal figlio. \item gli allarmi ed i segnali pendenti (vedi \secref{sec:sig_gen_beha}), che @@ -1015,15 +1019,14 @@ sulle risorse usate dal processo terminato e dai vari figli. I prototipi di queste funzioni, che diventano accessibili definendo la costante \macro{\_USE\_BSD}, sono: \begin{functions} - \headdecl{sys/times.h} - \headdecl{sys/types.h} - \headdecl{sys/wait.h} - \headdecl{sys/resource.h} + \headdecl{sys/times.h} \headdecl{sys/types.h} \headdecl{sys/wait.h} + \headdecl{sys/resource.h} + \funcdecl{pid\_t wait4(pid\_t pid, int * status, int options, struct rusage - * rusage)} - È identica a \func{waitpid} sia per comportamento che per i - valori dei parametri, ma restituisce in \param{rusage} un sommario delle - risorse usate dal processo (per i dettagli vedi \secref{sec:sys_xxx}) + * rusage)} + È identica a \func{waitpid} sia per comportamento che per i valori dei + parametri, ma restituisce in \param{rusage} un sommario delle risorse usate + dal processo. \funcdecl{pid\_t wait3(int *status, int options, struct rusage *rusage)} Prima versione, equivalente a \code{wait4(-1, \&status, opt, rusage)} è @@ -1031,16 +1034,9 @@ queste funzioni, che diventano accessibili definendo la costante \end{functions} \noindent la struttura \type{rusage} è definita in \file{sys/resource.h}, e viene -utilizzata anche dalla funzione \func{getrusage} (vedi \secref{sec:sys_xxx}) -per ottenere le risorse di sistema usate da un processo; la sua definizione è -riportata in \figref{fig:sys_rusage_struct}. - -In genere includere esplicitamente \file{} non è più -necessario, ma aumenta la portabilità, e serve in caso si debba accedere -ai campi di \var{rusage} definiti come \type{struct timeval}. La -struttura è ripresa da BSD 4.3, attualmente (con il kernel 2.4.x) i soli -campi che sono mantenuti sono: \var{ru\_utime}, \var{ru\_stime}, -\var{ru\_minflt}, \var{ru\_majflt}, e \var{ru\_nswap}. +utilizzata anche dalla funzione \func{getrusage} (vedi +\secref{sec:sys_resource_use}) per ottenere le risorse di sistema usate da un +processo; la sua definizione è riportata in \figref{fig:sys_rusage_struct}. \subsection{Le funzioni \func{exec}} @@ -1216,10 +1212,10 @@ la lista completa \secref{sec:file_umask}) ed i \textit{lock} sui file (vedi \secref{sec:file_locking}). \item i segnali sospesi (\textit{pending}) e la maschera dei segnali (si veda - \secref{sec:sig_sigpending}). -\item i limiti sulle risorse (vedi \secref{sec:sys_limits}). + \secref{sec:sig_sigmask}). +\item i limiti sulle risorse (vedi \secref{sec:sys_resource_limit}). \item i valori delle variabili \var{tms\_utime}, \var{tms\_stime}, - \var{tms\_cutime}, \var{tms\_ustime} (vedi \secref{sec:xxx_xxx}). + \var{tms\_cutime}, \var{tms\_ustime} (vedi \secref{sec:sys_cpu_times}). \end{itemize*} Inoltre i segnali che sono stati settati per essere ignorati nel processo @@ -1858,11 +1854,11 @@ 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. +Si tenga conto poi che i processi non devono solo eseguire del codice: ad +esempio molto spesso saranno impegnati in operazioni di I/O, o potranno +venire bloccati da un comando dal terminale, o 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 @@ -1892,7 +1888,7 @@ fintanto che esso si trova in uno qualunque degli altri stati. \hline \end{tabular} \caption{Elenco dei possibili stati di un processo in Linux, nella colonna - \texttt{STAT} si è riportata la corripondente lettera usata dal comando + \texttt{STAT} si è riportata la corrispondente lettera usata dal comando \cmd{ps} nell'omonimo campo.} \label{tab:proc_proc_states} \end{table} @@ -2019,7 +2015,7 @@ 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. +Restituisce il valore di \var{nice} 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: @@ -2067,8 +2063,7 @@ zero. Analoga a \func{getpriority} la funzione \func{setpriority} permette di settare la priorità di uno o più processi; il suo prototipo è: \begin{prototype}{sys/resource.h} -{int setpriority(int which, int who, int prio)} - +{int setpriority(int which, int who, int prio)} Setta la priorità per l'insieme dei processi specificati. \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso di @@ -2085,34 +2080,252 @@ settare la priorit \end{prototype} La funzione setta la priorità al valore specificato da \param{prio} per tutti -i processi indicati dagli argomenti \parm{which} e \param{who}. La gestione +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 per tutti i sistemi che derivano da -SYSV, è richiesto che il real o l'effective user id del processo chiamante -corrispondano al real user id (e solo quello) del processo di cui si vuole -cambiare la prorità; per i sistemi derivati da BSD invece (SunOS, Ultrix, -*BSD) la corrispondenza può essere anche con l'effective user id. +specifiche dello standard SUSv3, e come avviene per tutti i sistemi che +derivano da SYSV, è richiesto che il real o l'effective user id 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'effective user id. + \subsection{Il meccanismo di \textit{scheduling real-time}} \label{sec:proc_real_time} +Come spiegato in \secref{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 o RTAI, 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 + 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 si possono avere ritardi non previsti. Se l'ultimo problema può +essere aggirato attraverso l'uso delle funzioni di controllo della memoria +virtuale (vedi \secref{sec:proc_mem_lock}), il primo non è superabile e può +comportare ritardi non prevedibili riguardo ai tempi di esecuzione di +qualunque processo. + +In ogni caso 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. + +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. + + + +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. +\end{basedescript} + +La funzione per settare le politiche di scheduling (sia real-time che +ordinarie) ed i relativi parametri è \func{sched\_setscheduler}; il suo +prototipo è: +\begin{prototype}{sched.h} +{int sched\_setscheduler(pid\_t pid, int policy, const struct sched\_param *p)} + Setta priorità e politica di scheduling per il 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: + \begin{errlist} + \item[\macro{ESRCH}] il processo \param{pid} non esiste. + \item[\macro{EINVAL}] il valore di \param{policy} non esiste o il relativo + valore di \param{p} non è valido. + \item[\macro{EPERM}] il processo non ha i privilegi per attivare la + politica richiesta (vale solo per \macro{SCHED\_FIFO} e + \macro{SCHED\_RR}). + \end{errlist}} +\end{prototype} + +La funzione esegue il settaggio per il processo specificato; un valore nullo +di \param{pid} esegue il settaggio per il processo corrente, solo un processo +con i privilegi di amministratore può settare delle priorità assolute diverse +da zero. La politica di scheduling è specificata dall'argomento \param{policy} +i cui possibili valori sono riportati in \tabref{tab:proc_sched_policy}; un +valore negativo per \param{policy} mantiene la politica di scheduling +corrente. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|c|l|} + \hline + \textbf{Policy} & \textbf{Significato} \\ + \hline + \hline + \macro{SCHED\_FIFO} & Scheduling real-time con politica \textit{FIFO} \\ + \macro{SCHED\_RR} & Scheduling real-time con politica \textit{Round + Robin} \\ + \macro{SCHED\_OTHER}& Scheduling ordinario\\ + \hline + \end{tabular} + \caption{Valori dell'argomento \param{policy} per la funzione + \func{sched\_setscheduler}. } + \label{tab:proc_sched_policy} +\end{table} + +Il valore della priorità è passato attraverso la struttura \var{sched\_param} +(riportata in \figref{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 1 e 99 (il valore zero è legale, ma +indica i processi normali). Lo standard POSIX.1b prevede comunque che questi +due valori possano essere ottenuti per ciascuna politica di scheduling dalle +funzioni \func{sched\_get\_priority\_max} e \func{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[\macro{EINVAL}] il valore di \param{policy} è invalido. + \end{errlist}} +\end{functions} + -Come spiegato al paragrafo precedente di norma +I processi con politica di scheduling \macro{SCHED\_OTHER} devono specificare +un valore nullo (altrimenti si avrà un errore \macro{EINVAL}), questo valore +infatti non ha niente a che vedere con la priorità dinamica determinata dal +valore di \var{nice}, che deve essere settato con le funzioni viste in +precedenza. -Per settare le +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} +struct sched_param { + int sched_priority; +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \var{sched\_param}.} + \label{fig:sig_sched_param} +\end{figure} +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 +politica scelta è \macro{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} +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. + +La priorità assoluta può essere riletta indietro dalla funzione +\func{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[\macro{ESRCH}] il processo \param{pid} non esiste. + \item[\macro{EINVAL}] il valore di \param{pid} è negativo. + \end{errlist}} +\end{prototype} + +La funzione restituisce il valore (secondo la quanto elencato in +\tabref{tab:proc_sched_policy}) della politica di scheduling per il processo +specificato; se \param{pid} è nullo viene restituito quello del processo +chiamante. + +Se si intende operare solo sulla priorità assoluta di un processo si possono +usare le funzioni \func{sched\_setparam} e \func{sched\_getparam}, i cui +prototipi sono: + +\begin{functions} + \headdecl{sched.h} -\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.} + \funcdecl{int sched\_setparam(pid\_t pid, const struct sched\_param *p)} + Setta la priorità assoluta del processo \param{pid}. -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. + \funcdecl{int sched\_getparam(pid\_t pid, struct sched\_param *p)} + Legge la priorità assoluta 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: + \begin{errlist} + \item[\macro{ESRCH}] il processo \param{pid} non esiste. + \item[\macro{EINVAL}] il valore di \param{pid} è negativo. + \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 \macro{sched.h}. + +L'ultima funzione che permette di leggere le informazioni relative ai processi +real-time è \func{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 0in caso di successo e -1 in caso di errore, + nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\macro{ESRCH}] il processo \param{pid} non esiste. + \item[\macro{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 \var{timespec}, (la cui +definizione si può trovare in \figref{fig:sys_timeval_struct}). + + +Come accennato ogni processo che usa lo scheduling real-time può rilasciare +volontariamente la CPU; questo viene fatto attraverso la funzione +\func{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 settata opportunamente.} +\end{prototype} +La funzione fa si 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. \section{Problematiche di programmazione multitasking}