X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=prochand.tex;h=cc64ab80a9210683e22b5e6b6bf61cdf0a1d6356;hp=4f72ab526b705cd57ce08d9814e9ad3da38a7e27;hb=6a94b0f6f36bad6daf7b8d3f52d211661a135836;hpb=cf7518466ba15ab1759e5bc312ab99798b8cf2ac diff --git a/prochand.tex b/prochand.tex index 4f72ab5..cc64ab8 100644 --- a/prochand.tex +++ b/prochand.tex @@ -113,13 +113,12 @@ organizzati in un albero di directory (si veda 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. @@ -137,9 +136,10 @@ decide quale processo mettere in esecuzione; esso viene eseguito ad ogni 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 @@ -453,7 +453,7 @@ si pu 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 @@ -682,7 +682,7 @@ eseguite alla chiusura di un processo \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}). @@ -799,7 +799,7 @@ si scrive un programma che deve essere mantenuto in esecuzione a lungo e 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. @@ -894,18 +894,18 @@ specchietto riportato in \ntab: \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} @@ -964,9 +964,9 @@ avremo la certezza che la chiamata a \func{wait} non si bloccher \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}. \\ @@ -1208,7 +1208,7 @@ la lista completa \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 @@ -1286,7 +1286,7 @@ problematiche connesse ad una gestione accorta dei privilegi. 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 @@ -1347,7 +1347,7 @@ rispettivamente \textit{real} ed \textit{effective}. \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} @@ -1835,6 +1835,66 @@ il tempo di CPU per l'esecuzione dei processi ed oggetto di numerose ricerche; in ogni caso essa dipende in maniera essenziale anche dal tipo di utilizzo che deve essere fatto del sistema. +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 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}.}) +la priorità assoluta viene invece ignorata per quelli che sono bloccati su una +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 @@ -1849,12 +1909,8 @@ la risorsa \textsl{tempo di esecuzione}, la cui assegnazione sar 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 -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. + + Il meccanismo tradizionale di scheduling di Unix (che tratteremo in \secref{sec:proc_sched_stand}) è sempre stato basato su delle \textsl{priorità @@ -1868,29 +1924,23 @@ Lo standard POSIX.1b per 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 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 che non hanno questa necessità. + 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}\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}.}), -la priorità assoluta viene invece ignorata per quelli che sono bloccati su una -richiesta di I/O o in stato di \textit{sleep}. La priorità assoluta viene in +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}. +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 @@ -1900,19 +1950,21 @@ assoluta nel qual caso un processo avr 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} -A meno che non si abbiano specifiche esigenze, l'unico meccanismo di -scheduling con il quale si avrà a che fare è quello tradizionale che prevede -solo priorità dinamiche, ed è di questo che di norma ci si dovrà preoccupare +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é viene varia nel corso dell'esecuzione di un processo. +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. @@ -1981,7 +2033,8 @@ stesso processo, e pure alcune system call, possono essere interrotti in 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ò