X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=prochand.tex;h=0826840cd966782cf3f1c659b23bfa1a4275324e;hp=cc64ab80a9210683e22b5e6b6bf61cdf0a1d6356;hb=fc4a31f12206bfd45aedae964058696121e7decf;hpb=6a94b0f6f36bad6daf7b8d3f52d211661a135836 diff --git a/prochand.tex b/prochand.tex index cc64ab8..0826840 100644 --- a/prochand.tex +++ b/prochand.tex @@ -133,7 +133,8 @@ riprese), Come accennato in \secref{sec:intro_unix_struct} è lo \textit{scheduler} che decide quale processo mettere in esecuzione; esso viene eseguito ad ogni -system call ed ad ogni interrupt, (ma può essere anche attivato +system call ed ad ogni interrupt,\footnote{più in una serie di altre + 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 @@ -937,10 +938,10 @@ In genere in un programma non si vuole essere forzati ad attendere la conclusione di un processo per proseguire, specie se tutto questo serve solo per leggerne lo stato di chiusura (ed evitare la presenza di \textit{zombie}), per questo la modalità più usata per chiamare queste funzioni è quella di -utilizzarle all'interno di un \textit{signal handler} (torneremo sui segnali e -su come gestire \macro{SIGCHLD} in \secref{sec:sig_sigwait_xxx}). In questo -caso infatti, dato che il segnale è generato dalla terminazione di un figlio, -avremo la certezza che la chiamata a \func{wait} non si bloccherà. +utilizzarle all'interno di un \textit{signal handler} (vedremo un esempio di +come gestire \macro{SIGCHLD} con i segnali in \secref{sec:sig_example}). In +questo caso infatti, dato che il segnale è generato dalla terminazione di un +figlio, avremo la certezza che la chiamata a \func{wait} non si bloccherà. \begin{table}[!htb] \centering @@ -1423,11 +1424,11 @@ processo, come copie dell'\textit{effective user id} e dell'\textit{effective fossero utente e gruppo effettivi all'inizio dell'esecuzione di un nuovo programma. -Il \textit{filesystem user id} e il \textit{filesystem group id} sono una -estensione introdotta in Linux per rendere più sicuro l'uso di NFS (torneremo -sull'argomento in \secref{sec:proc_setfsuid}). Essi sono una replica dei -corrispondenti \textit{effective id}, ai quali si sostituiscono per tutte le -operazioni di verifica dei permessi relativi ai file (trattate in +Il \textit{filesystem user id} e il \textit{filesystem group id} sono +un'estensione introdotta in Linux per rendere più sicuro l'uso di NFS +(torneremo sull'argomento in \secref{sec:proc_setfsuid}). Essi sono una +replica dei corrispondenti \textit{effective id}, ai quali si sostituiscono +per tutte le operazioni di verifica dei permessi relativi ai file (trattate in \secref{sec:file_perm_overview}). Ogni cambiamento effettuato sugli \textit{effective id} viene automaticamente riportato su di essi, per cui in condizioni normali se ne può tranquillamente ignorare l'esistenza, in quanto @@ -1587,9 +1588,10 @@ Lo stesso problema di propagazione dei privilegi ad eventuali processi figli si porrebbe per i \textit{saved id}: queste funzioni derivano da un'implementazione che non ne prevede la presenza, e quindi non è possibile usarle per correggere la situazione come nel caso precedente. Per questo -motivo in Linux tutte le volte che vengono usate per modificare uno degli -identificatori ad un valore diverso dal \textit{real id} precedente, il -\textit{saved id} viene sempre settato al valore dell'\textit{effective id}. +motivo in Linux tutte le volte che tali funzioni vengono usate per modificare +uno degli identificatori ad un valore diverso dal \textit{real id} precedente, +il \textit{saved id} viene sempre settato al valore dell'\textit{effective + id}. @@ -1820,11 +1822,9 @@ quando si definisce \macro{\_POSIX\_SOURCE} o si compila con il flag \label{sec:proc_priority} In questa sezione tratteremo più approfonditamente i meccanismi con il quale -lo \textit{scheduler}\footnote{che è la parte del kernel che si occupa di - stabilire quale processo dovrà essere posto in esecuzione.} assegna la CPU -ai vari processi attivi. In particolare prenderemo in esame i vari meccanismi -con cui viene gestita l'assegnazione del tempo di CPU, ed illustreremo le -varie funzioni di gestione. +lo \textit{scheduler} assegna la CPU ai vari processi attivi. In particolare +prenderemo in esame i vari meccanismi con cui viene gestita l'assegnazione del +tempo di CPU, ed illustreremo le varie funzioni di gestione. \subsection{I meccanismi di \textit{scheduling}} @@ -1835,29 +1835,47 @@ 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. +\secref{sec:proc_hierarchy} questa scelta viene eseguita da una sezione +apposita del kernel, lo \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 +La cosa è resa ancora più complicata dal fatto che con le architetture +multi-processore si introduce anche la problematica dovuta alla scelta di +quale sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni + la presenza di ampie cache può rendere poco efficiente trasferire + l'esecuzione di un processo da una CPU ad un'altra, per cui occorrono + meccanismi per determinare quale è la migliore scelta fra le diverse CPU.} +Tutto questo comunque appartiene alle sottigliezze dell'implementazione del +kernel, e dal punto di vista dei programmi che girano in user space, anche +quando si hanno più processori (e dei processi che sono eseguiti davvero in +contemporanea), si può pensare alle politiche di scheduling come concernenti +la risorsa \textsl{tempo di esecuzione}, la cui assegnazione sarà governata +dagli stessi meccanismi di scelta di priorità, solo che nel caso di più +processori sarà a disposizione di più di un processo alla volta. + +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. +Tutte queste possibilità sono caratterizzate da un diverso \textsl{stato} del +processo , + +In Linux un processo può trovarsi in uno degli stati riportati in +\tabref{tab:proc_proc_states}; ma soltanto i processi che sono nello stato +\textit{runnable} concorrono per l'esecuzione. Questo vuol di \begin{table}[htb] \centering - \begin{tabular}[c]{|l|c|p{8cm}|} + \begin{tabular}[c]{|p{3cm}|c|p{8cm}|} \hline \textbf{Stato} & \texttt{STAT} & \textbf{Descrizione} \\ \hline @@ -1875,40 +1893,27 @@ Il Linux un processo pu 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}.} + \caption{Elenco dei possibili stati di un processo in Linux, nella colonna + \texttt{STAT} si è riportata la corripondente lettera usata dal comando + \cmd{ps} nell'omonimo campo.} \label{tab:proc_proc_states} \end{table} +Si deve quindi tenere presente che l'utilizzo della CPU è soltanto una delle +risorse che sono necessarie per l'esecuzione di un programma, e spesso non è +neanche la più importante. Per questo motivo non è affatto detto che dare ad +un programma la massima priorità di esecuzione abbia risultati significativi +in termini di prestazioni. -Una delle caratteristiche c -,\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 - la presenza di ampie cache può rendere poco efficiente trasferire - l'esecuzione di un processo da una CPU ad un'altra, per cui occorrono - meccanismi per determinare quale è la migliore scelta fra le diverse CPU.} -Tutto questo comunque appartiene alle sottigliezze dell'implementazione del -kernel, e dal punto di vista dei programmi che girano in user space, anche -quando si hanno più processori (e dei processi che sono eseguiti davvero in -contemporanea), si può pensare alle politiche di scheduling come concernenti -la risorsa \textsl{tempo di esecuzione}, la cui assegnazione sarà governata -dagli stessi meccanismi di scelta di priorità, solo che nel caso di più -processori sarà a disposizione di più di un processo alla volta. +Una delle caratteristiche c +la priorità assoluta viene invece ignorata per quelli che sono bloccati su una +richiesta di I/O o in stato di \textit{sleep} @@ -1950,6 +1955,7 @@ 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} @@ -2034,7 +2040,7 @@ 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_control}). +\secref{sec:sig_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ò @@ -2047,6 +2053,7 @@ le strutture. In tutti questi casi condiviso, onde evitare problemi con le ottimizzazioni del codice. + \subsection{Le \textit{race condition} e i \textit{deadlock}} \label{sec:proc_race_cond} @@ -2068,7 +2075,7 @@ funzioner Per questo occorre essere ben consapevoli di queste problematiche, e del fatto che l'unico modo per evitarle è quello di riconoscerle come tali e prendere -gli adeguati provvedimenti per far si che non si verifichino. Casi tipici di +gli adeguati provvedimenti per far sì che non si verifichino. Casi tipici di \textit{race condition} si hanno quando diversi processi accedono allo stesso file, o nell'accesso a meccanismi di intercomunicazione come la memoria condivisa. In questi casi, se non si dispone della possibilità di eseguire @@ -2114,12 +2121,12 @@ mai rientrante se usa una variabile globale o statica. Nel caso invece la funzione operi su un oggetto allocato dinamicamente, la cosa viene a dipendere da come avvengono le operazioni: se l'oggetto è creato ogni volta e ritornato indietro la funzione può essere rientrante, se invece -esso viene individuato dalla funzione stessa, due chiamate alla stessa -funzione potranno interferire quando entrambe faranno riferimento allo stesso -oggetto. Allo stesso modo una funzione può non essere rientrante se usa e -modifica un oggetto che le viene fornito dal chiamante: due chiamate possono -interferire se viene passato lo stesso oggetto; in tutti questi casi occorre -molta cura da parte del programmatore. +esso viene individuato dalla funzione stessa due chiamate alla stessa funzione +potranno interferire quando entrambe faranno riferimento allo stesso oggetto. +Allo stesso modo una funzione può non essere rientrante se usa e modifica un +oggetto che le viene fornito dal chiamante: due chiamate possono interferire +se viene passato lo stesso oggetto; in tutti questi casi occorre molta cura da +parte del programmatore. In genere le funzioni di libreria non sono rientranti, molte di esse ad esempio utilizzano variabili statiche, le \acr{glibc} però mettono a