X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=prochand.tex;h=16c51de94a0fd6fb69ff199b94eeaf5a0d346948;hp=fc5e47746ad46a1fbb237faa2d368bee530d915c;hb=ae7bf994013ffbf4f5ef1d5a38a18033593e3e9f;hpb=b0f9e84fb388f894bf26c87ffa304847bddfa3b0 diff --git a/prochand.tex b/prochand.tex index fc5e477..16c51de 100644 --- a/prochand.tex +++ b/prochand.tex @@ -39,12 +39,12 @@ terminazione dei processi, e per la messa in esecuzione degli altri programmi. \subsection{L'architettura della gestione dei processi} \label{sec:proc_hierarchy} -A differenza di quanto avviene in altri sistemi, ad esempio nel VMS la +A differenza di quanto avviene in altri sistemi, ad esempio nel VMS, dove la generazione di nuovi processi è un'operazione privilegiata, una delle caratteristiche fondanti di Unix, che esamineremo in dettaglio più avanti, è che qualunque processo può a sua volta generarne altri. Ogni processo è identificato presso il sistema da un numero univoco, il cosiddetto -\textit{Process ID} o, più brevemente, \ids{PID}, assegnato in forma +\textit{Process ID}, o più brevemente \ids{PID}, assegnato in forma progressiva (vedi sez.~\ref{sec:proc_pid}) quando il processo viene creato. Una seconda caratteristica di un sistema unix-like è che la generazione di un @@ -57,16 +57,16 @@ indichiamo nella linea di comando. Una terza caratteristica del sistema è che ogni processo è sempre stato generato da un altro processo, il processo generato viene chiamato \textit{processo figlio} (\textit{child process}) mentre quello che lo ha -viene chiamato \textsl{processo padre} (\textit{parent process}). Questo vale -per tutti i processi, con una sola eccezione, dato che ci deve essere un punto -di partenza esiste un processo speciale (che normalmente è \cmd{/sbin/init}), -che come abbiamo accennato in sez.~\ref{sec:intro_kern_and_sys} viene lanciato -dal kernel alla conclusione della fase di avvio. Essendo questo il primo -processo lanciato dal sistema ha sempre il \ids{PID} uguale a 1 e non è figlio -di nessun altro processo. - -Ovviamente \cmd{init} è un processo speciale che in genere si occupa di far -partire tutti gli altri processi necessari al funzionamento del sistema, +generato viene chiamato \textsl{processo padre} (\textit{parent + process}). Questo vale per tutti i processi, con una sola eccezione; dato +che ci deve essere un punto di partenza esiste un processo iniziale (che +normalmente è \cmd{/sbin/init}), che come accennato in +sez.~\ref{sec:intro_kern_and_sys} viene lanciato dal kernel alla conclusione +della fase di avvio. Essendo questo il primo processo lanciato dal sistema ha +sempre \ids{PID} uguale a 1 e non è figlio di nessun altro processo. + +Ovviamente \cmd{init} è un processo particolare che in genere si occupa di +lanciare tutti gli altri processi necessari al funzionamento del sistema, inoltre \cmd{init} è essenziale per svolgere una serie di compiti amministrativi nelle operazioni ordinarie del sistema (torneremo su alcuni di essi in sez.~\ref{sec:proc_termination}) e non può mai essere terminato. La @@ -138,10 +138,10 @@ Il kernel mantiene una tabella dei processi attivi, la cosiddetta questa tabella, costituita da una struttura \kstruct{task\_struct}, che contiene tutte le informazioni rilevanti per quel processo. Tutte le strutture usate a questo scopo sono dichiarate nell'\textit{header file} -\file{linux/sched.h}, ed uno schema semplificato, che riporta la struttura -delle principali informazioni contenute nella \texttt{task\_struct} (che in -seguito incontreremo a più riprese), è mostrato in -fig.~\ref{fig:proc_task_struct}. +\file{linux/sched.h}, ed in fig.~\ref{fig:proc_task_struct} si è riportato uno +schema semplificato che mostra la struttura delle principali informazioni +contenute nella \texttt{task\_struct}, che in seguito incontreremo a più +riprese. \begin{figure}[!htb] \centering \includegraphics[width=14cm]{img/task_struct} @@ -179,7 +179,7 @@ su macchine che non stanno facendo nulla, con un forte risparmio nell'uso dell'energia da parte del processore che può essere messo in stato di sospensione anche per lunghi periodi di tempo. -Indipendentemente dalle motivazioni per cui questo avviene, ogni volta che +Ma, indipendentemente dalle motivazioni per cui questo avviene, ogni volta che viene eseguito lo \textit{scheduler} effettua il calcolo delle priorità dei vari processi attivi (torneremo su questo in sez.~\ref{sec:proc_priority}) e stabilisce quale di essi debba essere posto in esecuzione fino alla successiva @@ -194,7 +194,7 @@ invocazione. Come accennato nella sezione precedente ogni processo viene identificato dal sistema da un numero identificativo univoco, il \textit{process ID} o -\ids{PID}. Questo è un tipo di dato standard, \type{pid\_t} che in genere è un +\ids{PID}. Questo è un tipo di dato standard, \type{pid\_t}, che in genere è un intero con segno (nel caso di Linux e della \acr{glibc} il tipo usato è \ctyp{int}). @@ -272,7 +272,7 @@ sez.~\ref{sec:proc_perms}. \subsection{La funzione \func{fork} e le funzioni di creazione dei processi} \label{sec:proc_fork} -La funzione di sistema \funcd{fork} è la funzione fondamentale della gestione +La funzione di sistema \func{fork} è la funzione fondamentale della gestione dei processi: come si è detto tradizionalmente l'unico modo di creare un nuovo processo era attraverso l'uso di questa funzione,\footnote{in realtà oggi la \textit{system call} usata da Linux per creare nuovi processi è \func{clone} @@ -282,18 +282,19 @@ processo era attraverso l'uso di questa funzione,\footnote{in realtà oggi la migliore interazione coi \textit{thread}.} essa quindi riveste un ruolo centrale tutte le volte che si devono scrivere programmi che usano il multitasking.\footnote{oggi questa rilevanza, con la diffusione dell'uso dei - \textit{thread} che tratteremo al cap.~\ref{cha:threads}, è in parte minore, - ma \func{fork} resta comunque la funzione principale per la creazione di - processi.} Il prototipo della funzione è: + \textit{thread}\unavref{ che tratteremo al cap.~\ref{cha:threads}}, è in + parte minore, ma \func{fork} resta comunque la funzione principale per la + creazione di processi.} Il prototipo di \funcd{fork} è: \begin{funcproto}{ \fhead{unistd.h} \fdecl{pid\_t fork(void)} \fdesc{Crea un nuovo processo.} } -{La funzione ritorna il \ids{PID} del figlio al padre e $0$ al figlio in caso - di successo e $-1$ al padre senza creare il figlio per un errore, - nel qual caso \var{errno} assumerà uno dei valori: + +{La funzione ritorna in caso di successo il \ids{PID} del figlio nel padre e + $0$ nel figlio mentre ritorna $-1$ nel padre, senza creare il figlio, per un + errore, al caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EAGAIN}] non ci sono risorse sufficienti per creare un altro processo (per allocare la tabella delle pagine e le strutture del task) o @@ -304,7 +305,7 @@ multitasking.\footnote{oggi questa rilevanza, con la diffusione dell'uso dei \end{funcproto} Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che -il processo figlio continuano ad essere eseguiti normalmente a partire +il processo figlio continuano ad essere eseguiti normalmente, a partire dall'istruzione successiva alla \func{fork}. Il processo figlio è una copia del padre, e riceve una copia dei segmenti di testo, dati e dello \textit{stack} (vedi sez.~\ref{sec:proc_mem_layout}), ed esegue esattamente lo @@ -334,11 +335,11 @@ eseguito dal padre o dal figlio. Si noti come la funzione \func{fork} ritorni due volte, una nel padre e una nel figlio. La scelta di questi valori di ritorno non è casuale, un processo infatti può -avere più figli, ed il valore di ritorno di \func{fork} è l'unico modo che gli -permette di identificare quello appena creato. Al contrario un figlio ha -sempre un solo padre, il cui \ids{PID} può sempre essere ottenuto con -\func{getppid}, come spiegato in sez.~\ref{sec:proc_pid}, per cui si usa il -valore nullo, che non è il \ids{PID} di nessun processo. +avere più figli, ed il valore di ritorno di \func{fork} è l'unico che gli +permette di identificare qual è quello appena creato. Al contrario un figlio +ha sempre un solo padre il cui \ids{PID}, come spiegato in +sez.~\ref{sec:proc_pid}, può sempre essere ottenuto con \func{getppid}; per +questo si ritorna un valore nullo, che non è il \ids{PID} di nessun processo. 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 @@ -353,7 +354,7 @@ ne esegue un'altra. È il caso tipico dei programmi server (il modello \textit{client-server} è illustrato in sez.~\ref{sec:net_cliserv}) in cui il padre riceve ed accetta le richieste da parte dei programmi client, per ciascuna delle quali pone in esecuzione un figlio che è incaricato di fornire -il servizio. +le risposte associate al servizio. La seconda modalità è quella in cui il processo vuole eseguire un altro programma; questo è ad esempio il caso della shell. In questo caso il processo @@ -393,8 +394,8 @@ degli eventuali tempi di attesa in secondi (eseguiti tramite la funzione \func{sleep}) per il padre ed il figlio (con \cmd{forktest -h} si ottiene la descrizione delle opzioni). Il codice completo, compresa la parte che gestisce le opzioni a riga di comando, è disponibile nel file \file{fork\_test.c}, -distribuito insieme agli altri sorgenti degli esempi su -\url{http://gapil.truelite.it/gapil_source.tgz}. +distribuito insieme agli altri sorgenti degli esempi della guida su +\url{http://gapil.gnulinux.it}. Decifrato il numero di figli da creare, il ciclo principale del programma (\texttt{\small 24-40}) esegue in successione la creazione dei processi figli @@ -460,36 +461,36 @@ In realtà con l'introduzione dei kernel della serie 2.6 lo \textit{scheduler} risultati precedenti infatti sono stati ottenuti usando un kernel della serie 2.4.} Questa è una ottimizzazione adottata per evitare che il padre, effettuando per primo una operazione di scrittura in memoria, attivasse il -meccanismo del \textit{copy on write}, operazione inutile qualora il figlio -venga creato solo per eseguire una \func{exec} su altro programma che scarta -completamente lo spazio degli indirizzi e rende superflua la copia della -memoria modificata dal padre. Eseguendo sempre per primo il figlio la -\func{exec} verrebbe effettuata subito, con la certezza di utilizzare +meccanismo del \textit{copy on write}, operazione inutile quando il figlio +viene creato solo per eseguire una \func{exec} per lanciare un altro programma +che scarta completamente lo spazio degli indirizzi e rende superflua la copia +della memoria modificata dal padre. Eseguendo sempre per primo il figlio la +\func{exec} verrebbe effettuata subito, con la certezza di utilizzare il \textit{copy on write} solo quando necessario. Con il kernel 2.6.32 però il comportamento è stato nuovamente cambiato, stavolta facendo eseguire per primo sempre il padre. Si è realizzato infatti -che l'eventualità prospettata per la scelta precedente era comunque molto -improbabile, mentre l'esecuzione immediata del padre presenta sempre il +che l'eventualità prospettata per la scelta precedente era comunque poco +probabile, mentre l'esecuzione immediata del padre presenta sempre il vantaggio di poter utilizzare immediatamente tutti i dati che sono nella cache -della CPU e nella unità di gestione della memoria virtuale senza doverli +della CPU e nell'unità di gestione della memoria virtuale, senza doverli invalidare, cosa che per i processori moderni, che hanno linee di cache interne molto profonde, avrebbe un forte impatto sulle prestazioni. -Allora anche se quanto detto in precedenza vale come comportamento effettivo -dei programmi soltanto per i kernel fino alla serie 2.4, per mantenere la -portabilità con altri kernel unix-like, e con i diversi comportamenti adottati -dalle Linux nelle versioni successive, è opportuno non fare affidamento su -nessun tipo comportamento predefinito e non dare per assunta l'esecuzione -preventiva del padre o del figlio. - -Si noti poi come dopo la \func{fork}, essendo i segmenti di memoria utilizzati -dai singoli processi completamente indipendenti, le modifiche delle variabili -nei processi figli, come l'incremento di \var{i} in (\texttt{\small 31}), sono -visibili solo a loro, (ogni processo vede solo la propria copia della -memoria), e non hanno alcun effetto sul valore che le stesse variabili hanno -nel processo padre ed in eventuali altri processi figli che eseguano lo stesso -codice. +Allora anche se quanto detto in precedenza si verifica nel comportamento +effettivo dei programmi soltanto per i kernel fino alla serie 2.4, per +mantenere la portabilità con altri kernel unix-like e con i diversi +comportamenti adottati dalle Linux nella sua evoluzione, è comunque opportuno +non fare nessuna assunzione sull'ordine di esecuzione di padre e figlio dopo +la chiamata a \func{fork}. + +Si noti infine come dopo la \func{fork}, essendo i segmenti di memoria +utilizzati dai singoli processi completamente indipendenti, le modifiche delle +variabili nei processi figli, come l'incremento di \var{i} in (\texttt{\small + 31}), sono visibili solo a loro, (ogni processo vede solo la propria copia +della memoria), e non hanno alcun effetto sul valore che le stesse variabili +hanno nel processo padre ed in eventuali altri processi figli che eseguano lo +stesso codice. Un secondo aspetto molto importante nella creazione dei processi figli è quello dell'interazione dei vari processi con i file. Ne parleremo qui anche @@ -528,15 +529,15 @@ che come si vede è completamente diverso da quanto ottenevamo sul terminale. Il comportamento delle varie funzioni di interfaccia con i file è analizzato in gran dettaglio in sez.~\ref{sec:file_unix_interface} per l'interfaccia nativa Unix ed in sez.~\ref{sec:files_std_interface} per la standardizzazione -adottata nelle librerie del linguaggio C e valida per qualunque sistema -operativo. +adottata nelle librerie del linguaggio C, valida per qualunque sistema +operativo. Qui basta accennare che si sono usate le funzioni standard della libreria del C che prevedono l'output bufferizzato. Il punto è che questa bufferizzazione (che tratteremo in dettaglio in sez.~\ref{sec:file_buffering}) varia a seconda che si tratti di un file su disco, in cui il buffer viene scaricato su disco solo quando necessario, o di un terminale, in cui il buffer viene scaricato ad -ogni carattere di a capo. +ogni carattere di ``a capo''. Nel primo esempio allora avevamo che, essendovi un a capo nella stringa stampata, ad ogni chiamata a \func{printf} il buffer veniva scaricato, per cui @@ -566,19 +567,19 @@ viene rediretto come si è fatto nell'esempio, lo stesso avviene anche per tutti i figli. La funzione \func{fork} infatti ha la caratteristica di duplicare nei processi figli tutti i \textit{file descriptor} (vedi sez.~\ref{sec:file_fd}) dei file aperti nel processo padre (allo stesso modo -in cui lo fa la funzione \func{dup}, trattata in sez.~\ref{sec:file_dup}), il -che comporta che padre e figli condividono le stesse voci della \textit{file - table} (tratteremo in dettaglio questi termini in sez.~\ref{sec:file_fd} e -sez.~\ref{sec:file_shared_access}) fra cui c'è anche la posizione corrente nel -file. - -In questo modo se un processo scrive su un file aggiornerà la posizione -corrente sulla \textit{file table}, e tutti gli altri processi, che vedono la -stessa \textit{file table}, vedranno il nuovo valore. In questo modo si evita, -in casi come quello appena mostrato in cui diversi processi scrivono sullo -stesso file, che l'output successivo di un processo vada a sovrapporsi a -quello dei precedenti: l'output potrà risultare mescolato, ma non ci saranno -parti perdute per via di una sovrascrittura. +in cui lo fa la funzione \func{dup}, trattata in sez.~\ref{sec:file_dup}). Ciò +fa si che padre e figli condividano le stesse voci della \textit{file table} +(tratteremo in dettaglio questi termini in sez.~\ref{sec:file_fd} e +sez.~\ref{sec:file_shared_access}) fra le quali c'è anche la posizione +corrente nel file. + +Quando un processo scrive su un file la posizione corrente viene aggiornata +sulla \textit{file table}, e tutti gli altri processi, che vedono la stessa +\textit{file table}, vedranno il nuovo valore. In questo modo si evita, in +casi come quello appena mostrato in cui diversi figli scrivono sullo stesso +file usato dal padre, che una scrittura eseguita in un secondo tempo da un +processo vada a sovrapporsi a quelle precedenti: l'output potrà risultare +mescolato, ma non ci saranno parti perdute per via di una sovrascrittura. Questo tipo di comportamento è essenziale in tutti quei casi in cui il padre crea un figlio e attende la sua conclusione per proseguire, ed entrambi @@ -586,9 +587,9 @@ scrivono sullo stesso file. Un caso tipico di questo comportamento è la shell quando lancia un programma. In questo modo, anche se lo standard output viene rediretto, il padre potrà sempre continuare a scrivere in coda a quanto scritto dal figlio in maniera automatica; se così non fosse ottenere questo -comportamento sarebbe estremamente complesso necessitando di una qualche forma -di comunicazione fra i due processi per far riprendere al padre la scrittura -al punto giusto. +comportamento sarebbe estremamente complesso, necessitando di una qualche +forma di comunicazione fra i due processi per far riprendere al padre la +scrittura al punto giusto. In generale comunque non è buona norma far scrivere più processi sullo stesso file senza una qualche forma di sincronizzazione in quanto, come visto anche @@ -771,10 +772,12 @@ terminato; si potrebbe avere cioè quello che si chiama un processo \textsl{orfano}. Questa complicazione viene superata facendo in modo che il processo orfano -venga \textsl{adottato} da \cmd{init}, o meglio dal processo con \ids{PID} 1, -cioè quello lanciato direttamente dal kernel all'avvio, che sta alla base -dell'albero dei processi visto in sez.~\ref{sec:proc_hierarchy} e che anche -per questo motivo ha un ruolo essenziale nel sistema e non può mai +venga \textsl{adottato} da \cmd{init}, o meglio dal processo con \ids{PID} +1,\footnote{anche se, come vedremo in sez.~\ref{sec:process_prctl}, a partire + dal kernel 3.4 è diventato possibile delegare questo compito anche ad un + altro processo.} cioè quello lanciato direttamente dal kernel all'avvio, che +sta alla base dell'albero dei processi visto in sez.~\ref{sec:proc_hierarchy} +e che anche per questo motivo ha un ruolo essenziale nel sistema e non può mai terminare.\footnote{almeno non senza un blocco completo del sistema, in caso di terminazione o di non esecuzione di \cmd{init} infatti il kernel si blocca con un cosiddetto \textit{kernel panic}, dato che questo è un errore @@ -814,9 +817,10 @@ terminano, e come si può notare in questo caso, al contrario di quanto visto in precedenza, essi riportano 1 come \ids{PPID}. Altrettanto rilevante è il caso in cui il figlio termina prima del padre, -perché non è detto che il padre possa ricevere immediatamente lo stato di -terminazione, quindi il kernel deve comunque conservare una certa quantità di -informazioni riguardo ai processi che sta terminando. +perché non è detto che il padre sia in esecuzione e possa ricevere +immediatamente lo stato di terminazione, quindi il kernel deve comunque +conservare una certa quantità di informazioni riguardo ai processi che sta +terminando. Questo viene fatto mantenendo attiva la voce nella tabella dei processi, e memorizzando alcuni dati essenziali, come il \ids{PID}, i tempi di CPU usati @@ -985,9 +989,9 @@ sistema, \funcd{waitpid}, il cui prototipo è: La prima differenza fra le due funzioni è che con \func{waitpid} si può specificare in maniera flessibile quale processo attendere, sulla base del -valore fornito dall'argomento \param{pid}, questo può assumere diversi valori, +valore fornito dall'argomento \param{pid}. Questo può assumere diversi valori, secondo lo specchietto riportato in tab.~\ref{tab:proc_waidpid_pid}, dove si -sono riportate anche le costanti definite per indicare alcuni di essi. +sono riportate anche le costanti definite per indicare alcuni di essi. \begin{table}[!htb] \centering @@ -1024,8 +1028,8 @@ tabella si sono riportati anche alcune opzioni non standard specifiche di Linux, che consentono un controllo più dettagliato per i processi creati con la \textit{system call} generica \func{clone} (vedi sez.~\ref{sec:process_clone}) e che vengono usati principalmente per la -gestione della terminazione dei \textit{thread} (vedi -sez.~\ref{sec:thread_xxx}). +gestione della terminazione dei \textit{thread}\unavref{ (vedi +sez.~\ref{sec:thread_xxx})}. \begin{table}[!htb] \centering @@ -1040,8 +1044,7 @@ sez.~\ref{sec:thread_xxx}). \const{WUNTRACED} & Ritorna anche quando un processo figlio è stato fermato.\\ \const{WCONTINUED}& Ritorna anche quando un processo figlio che era stato - fermato ha ripreso l'esecuzione (disponibile solo a - partire dal kernel 2.6.10).\\ + fermato ha ripreso l'esecuzione (dal kernel 2.6.10).\\ \hline \constd{\_\_WCLONE}& Attende solo per i figli creati con \func{clone} (vedi sez.~\ref{sec:process_clone}), vale a dire @@ -1051,7 +1054,7 @@ sez.~\ref{sec:thread_xxx}). processi figli ordinari ignorando quelli creati da \func{clone}.\\ \constd{\_\_WALL} & Attende per qualunque figlio, sia ordinario che creato - con \func{clone}, se specificata insieme a + con \func{clone}, se specificata con \const{\_\_WCLONE} quest'ultima viene ignorata. \\ \constd{\_\_WNOTHREAD}& Non attende per i figli di altri \textit{thread} dello stesso \textit{thread group}, questo era il @@ -1087,12 +1090,12 @@ Nel caso di \const{WUNTRACED} la funzione ritorna, restituendone il \ids{PID}, quando un processo figlio entra nello stato \textit{stopped}\footnote{in realtà viene notificato soltanto il caso in cui il processo è stato fermato da un segnale di stop (vedi sez.~\ref{sec:sess_ctrl_term}), e non quello in - cui lo stato \textit{stopped} è dovuto all'uso di \func{ptrace} (vedi - sez.~\ref{sec:process_ptrace}).} (vedi tab.~\ref{tab:proc_proc_states}), -mentre con \const{WCONTINUED} la funzione ritorna quando un processo in stato -\textit{stopped} riprende l'esecuzione per la ricezione del segnale -\signal{SIGCONT} (l'uso di questi segnali per il controllo di sessione è -trattato in sez.~\ref{sec:sess_ctrl_term}). + cui lo stato \textit{stopped} è dovuto all'uso di \func{ptrace}\unavref{ + (vedi sez.~\ref{sec:process_ptrace})}.} (vedi +tab.~\ref{tab:proc_proc_states}), mentre con \const{WCONTINUED} la funzione +ritorna quando un processo in stato \textit{stopped} riprende l'esecuzione per +la ricezione del segnale \signal{SIGCONT} (l'uso di questi segnali per il +controllo di sessione è trattato in sez.~\ref{sec:sess_ctrl_term}). \constend{WUNTRACED} \constend{WCONTINUED} @@ -1102,10 +1105,10 @@ con \func{waitpid}) è chiaramente un evento asincrono rispetto all'esecuzione di un programma e può avvenire in un qualunque momento. Per questo motivo, come accennato nella sezione precedente, una delle azioni prese dal kernel alla conclusione di un processo è quella di mandare un segnale di -\signal{SIGCHLD} al padre. L'azione predefinita (si veda -sez.~\ref{sec:sig_base}) per questo segnale è di essere ignorato, ma la sua -generazione costituisce il meccanismo di comunicazione asincrona con cui il -kernel avverte il processo padre che uno dei suoi figli è terminato. +\signal{SIGCHLD} al padre. L'azione predefinita per questo segnale (si veda +sez.~\ref{sec:sig_base}) è di essere ignorato, ma la sua generazione +costituisce il meccanismo di comunicazione asincrona con cui il kernel avverte +il processo padre che uno dei suoi figli è terminato. Il comportamento delle funzioni è però cambiato nel passaggio dal kernel 2.4 al kernel 2.6, quest'ultimo infatti si è adeguato alle prescrizioni dello @@ -1139,15 +1142,15 @@ la chiamata a \func{waitpid} non si bloccherà. Come accennato sia \func{wait} che \func{waitpid} restituiscono lo stato di terminazione del processo tramite il puntatore \param{status}, e se non -interessa memorizzare lo stato si può passare un puntatore nullo. Il valore -restituito da entrambe le funzioni dipende dall'implementazione, ma -tradizionalmente gli 8 bit meno significativi sono riservati per memorizzare -lo stato di uscita del processo, e gli altri per indicare il segnale che ha -causato la terminazione (in caso di conclusione anomala), uno per indicare se -è stato generato un \textit{core dump} (vedi sez.~\ref{sec:sig_standard}), -ecc.\footnote{le definizioni esatte si possono trovare in - \file{} ma questo file non deve mai essere usato - direttamente, esso viene incluso attraverso \file{}.} +interessa memorizzarlo si può passare un puntatore nullo. Il valore restituito +da entrambe le funzioni dipende dall'implementazione, ma tradizionalmente gli +8 bit meno significativi sono riservati per memorizzare lo stato di uscita del +processo, e gli altri per indicare il segnale che ha causato la terminazione +(in caso di conclusione anomala), uno per indicare se è stato generato un +\textit{core dump} (vedi sez.~\ref{sec:sig_standard}), ecc.\footnote{le + definizioni esatte si possono trovare in \file{} ma + questo file non deve mai essere usato direttamente, esso viene incluso + attraverso \file{}.} \begin{table}[!htb] \centering @@ -1391,6 +1394,12 @@ creato un nuovo processo, la funzione semplicemente rimpiazza lo programma letto da disco, eseguendo il \textit{link-loader} con gli effetti illustrati in sez.~\ref{sec:proc_main}. +\begin{figure}[!htb] + \centering \includegraphics[width=8cm]{img/exec_rel} + \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}.} + \label{fig:proc_exec_relat} +\end{figure} + Ci sono sei diverse versioni di \func{exec} (per questo la si è chiamata famiglia di funzioni) che possono essere usate per questo compito, in realtà (come mostrato in fig.~\ref{fig:proc_exec_relat}), tutte queste funzioni sono @@ -1398,7 +1407,7 @@ tutte varianti che consentono di invocare in modi diversi, semplificando il passaggio degli argomenti, la funzione di sistema \funcd{execve}, il cui prototipo è: -\begin{funcproto}{ +\begin{funcproto}{ \fhead{unistd.h} \fdecl{int execve(const char *filename, char *const argv[], char *const envp[])} \fdesc{Esegue un programma.} @@ -1410,15 +1419,18 @@ prototipo è: eseguibili, o il file è su un filesystem montato con l'opzione \cmd{noexec}, o manca il permesso di attraversamento di una delle directory del \textit{pathname}. + \item[\errcode{EAGAIN}] dopo un cambio di \ids{UID} si è ancora sopra il + numero massimo di processi consentiti per l'utente (dal kernel 3.1, per i + dettagli vedi sez.~\ref{sec:proc_setuid}). \item[\errcode{EINVAL}] l'eseguibile ELF ha più di un segmento \const{PT\_INTERP}, cioè chiede di essere eseguito da più di un interprete. \item[\errcode{ELIBBAD}] un interprete ELF non è in un formato riconoscibile. - \item[\errcode{ENOEXEC}] il file è in un formato non eseguibile o non - riconosciuto come tale, o compilato per un'altra architettura. \item[\errcode{ENOENT}] il file o una delle librerie dinamiche o l'interprete necessari per eseguirlo non esistono. + \item[\errcode{ENOEXEC}] il file è in un formato non eseguibile o non + riconosciuto come tale, o compilato per un'altra architettura. \item[\errcode{EPERM}] il file ha i bit \acr{suid} o \acr{sgid} e l'utente non è root, ed il processo viene tracciato, oppure il filesystem è montato con l'opzione \cmd{nosuid}. @@ -1444,8 +1456,8 @@ torneremo in sez.~\ref{sec:sys_res_limits}). In caso di successo la funzione non ritorna, in quanto al posto del programma chiamante viene eseguito il nuovo programma indicato da \param{filename}. Se -il processo corrente è tracciato con \func{ptrace} (vedi -sez.~\ref{sec:process_ptrace}) in caso di successo viene emesso il segnale +il processo corrente è tracciato con \func{ptrace}\unavref{ (vedi +sez.~\ref{sec:process_ptrace})} in caso di successo viene emesso il segnale \signal{SIGTRAP}. Le altre funzioni della famiglia (\funcd{execl}, \funcd{execv}, @@ -1519,12 +1531,6 @@ che deve essere terminata da un puntatore nullo. In entrambi i casi vale la convenzione che il primo argomento (\var{arg0} o \var{argv[0]}) viene usato per indicare il nome del file che contiene il programma che verrà eseguito. -\begin{figure}[!htb] - \centering \includegraphics[width=9cm]{img/exec_rel} - \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}.} - \label{fig:proc_exec_relat} -\end{figure} - La seconda differenza fra le funzioni riguarda le modalità con cui si specifica il programma che si vuole eseguire. Con lo mnemonico ``\texttt{p}'' si indicano le due funzioni che replicano il comportamento della shell nello @@ -1573,10 +1579,8 @@ seguente: \item i limiti sulle risorse (vedi sez.~\ref{sec:sys_resource_limit}); \item i valori delle variabili \var{tms\_utime}, \var{tms\_stime}; \var{tms\_cutime}, \var{tms\_ustime} (vedi sez.~\ref{sec:sys_cpu_times}); -% TODO ===========Importante============= -% TODO questo sotto è incerto, verificare -% TODO ===========Importante============= -\item la maschera dei segnali (si veda sez.~\ref{sec:sig_sigmask}). +\item la maschera dei segnali (si veda sez.~\ref{sec:sig_sigmask}); +\item l'insieme dei segnali pendenti (vedi sez.~\ref{sec:sig_gen_beha}). \end{itemize*} Una serie di proprietà del processo originale, che non avrebbe senso mantenere @@ -1585,8 +1589,6 @@ indirizzi totalmente indipendente e ricreato da zero, vengono perse con l'esecuzione di una \func{exec}. Lo standard POSIX.1-2001 prevede che le seguenti proprietà non vengano preservate: \begin{itemize*} -\item l'insieme dei segnali pendenti (vedi sez.~\ref{sec:sig_gen_beha}), che - viene cancellato; \item gli eventuali stack alternativi per i segnali (vedi sez.~\ref{sec:sig_specific_features}); \item i \textit{directory stream} (vedi sez.~\ref{sec:file_dir_read}), che @@ -1620,9 +1622,9 @@ nell'esecuzione della funzione \func{exec}, queste sono: pendenti vengono cancellate; \item le \textit{capabilities} vengono modificate come illustrato in sez.~\ref{sec:proc_capabilities}; -\item tutti i \textit{thread} tranne il chiamante (vedi - sez.~\ref{sec:thread_xxx}) sono cancellati e tutti gli oggetti ad essi - relativi (vedi sez.~\ref{sec:thread_xxx}) rimossi; +\item tutti i \textit{thread} tranne il chiamante\unavref{ (vedi + sez.~\ref{sec:thread_xxx})} vengono cancellati e tutti gli oggetti ad essi + relativi\unavref{ (vedi sez.~\ref{sec:thread_xxx})} sono rimossi; \item viene impostato il flag \const{PR\_SET\_DUMPABLE} di \func{prctl} (vedi sez.~\ref{sec:process_prctl}) a meno che il programma da eseguire non sia \acr{suid} o \acr{sgid} (vedi sez.~\ref{sec:proc_access_id} e @@ -1898,31 +1900,74 @@ sez.~\ref{sec:proc_access_id} seguono la semantica POSIX che prevede l'esistenza dell'\ids{UID} salvato e del \ids{GID} salvato, sono rispettivamente \funcd{setuid} e \funcd{setgid}; i loro prototipi sono: -\begin{funcproto}{ +\begin{funcproto}{ \fhead{unistd.h} \fhead{sys/types.h} \fdecl{int setuid(uid\_t uid)} -\fdesc{Imposta l'\ids{UID} del processo corrente.} +\fdesc{Imposta l'\ids{UID} del processo corrente.} \fdecl{int setgid(gid\_t gid)} -\fdesc{Imposta il \ids{GID} del processo corrente.} +\fdesc{Imposta il \ids{GID} del processo corrente.} } {Le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, nel qual -caso \var{errno} può assumere solo il valore \errcode{EPERM}. +caso \var{errno} uno dei valori: +\begin{errlist} +\item[\errcode{EAGAIN}] (solo per \func{setuid}) la chiamata cambierebbe + l'\ids{UID} reale ma il kernel non dispone temporaneamente delle risorse per + farlo, oppure, per i kernel precendenti il 3.1, il cambiamento + dell'\ids{UID} reale farebbe superare il limite per il numero dei processi + \const{RLIMIT\_NPROC} (vedi sez.~\ref{sec:sys_resource_limit}). +\item[\errcode{EINVAL}] il valore di dell'argomento non è valido per il + \textit{namespace} corrente (vedi sez.~\ref{sec:process_namespaces}). +\item[\errcode{EPERM}] non si hanno i permessi per l'operazione richiesta. +\end{errlist} } \end{funcproto} Il funzionamento di queste due funzioni è analogo, per cui considereremo solo la prima, la seconda si comporta esattamente allo stesso modo facendo -riferimento al \ids{GID} invece che all'\ids{UID}. Gli eventuali \ids{GID} +riferimento al \ids{GID} invece che all'\ids{UID}. Gli eventuali \ids{GID} supplementari non vengono modificati. L'effetto della chiamata è diverso a seconda dei privilegi del processo; se -l'\ids{UID} effettivo è zero (cioè è quello dell'amministratore di sistema) -allora tutti gli identificatori (\textit{real}, \textit{effective} e -\textit{saved}) vengono impostati al valore specificato da \param{uid}, -altrimenti viene impostato solo l'\ids{UID} effettivo, e soltanto se il valore -specificato corrisponde o all'\ids{UID} reale o all'\ids{UID} salvato. Negli -altri casi viene segnalato un errore con \errcode{EPERM}. +l'\ids{UID} effettivo è zero (cioè è quello dell'amministratore di sistema o +il processo ha la la capacità \const{CAP\_SETUID}) allora tutti gli +identificatori (\textit{real}, \textit{effective} e \textit{saved}) vengono +impostati al valore specificato da \param{uid}, altrimenti viene impostato +solo l'\ids{UID} effettivo, e soltanto se il valore specificato corrisponde o +all'\ids{UID} reale o all'\ids{UID} salvato, ottenendo un errore di +\errcode{EPERM} negli altri casi. + +E' importante notare che la funzione può fallire, con +\errval{EAGAIN},\footnote{non affronteremo qui l'altro caso di errore, che può + avvenire solo quando si esegue la funzione all'interno di un diverso + \textit{user namespace}, argomento su cui torneremo in + sez.~\ref{sec:process_namespaces} ma la considerazione di controllare sempre + lo stato di uscita si applica allo stesso modo.} anche quando viene invocata +da un processo con privilegi di amministratore per cambiare il proprio +l'\ids{UID} reale, sia per una temporanea indisponibilità di risorse del +kernel, sia perché l'utente di cui si vuole assumere l'\ids{UID} andrebbe a +superare un eventuale limite sul numero di processi (il limite +\const{RLIMIT\_NPROC}, che tratteremo in sez.~\ref{sec:sys_resource_limit}), +pertanto occorre sempre verificare lo stato di uscita della funzione. + +Non controllare questo tipo di errori perché si presume che la funzione abbia +sempre successo quando si hanno i privilegi di amministratore può avere +conseguente devastanti per la sicurezza, in particolare quando la si usa per +cedere i suddetti privilegi ed eseguire un programma per conto di un utente +non privilegiato. + +E' per diminuire l'impatto di questo tipo di disattenzioni che a partire dal +kernel 3.1 il comportamento di \func{setuid} e di tutte le analoghe funzioni +che tratteremo nel seguito di questa sezione è stato modificato e nel caso di +superamento del limite sulle risorse esse hanno comunque successo. Quando +questo avviene il processo assume comunque il nuovo \ids{UID} ed il controllo +sul superamento di \const{RLIMIT\_NPROC} viene posticipato ad una eventuale +successiva invocazione di \func{execve} (essendo questo poi il caso d'uso più +comune). In tal caso, se alla chiamata ancora sussiste la situazione di +superamento del limite, sarà \func{execve} a fallire con un errore di +\const{EAGAIN}.\footnote{che pertanto, a partire dal kernel 3.1, può + restituire anche questo errore, non presente in altri sistemi + \textit{unix-like}.} Come accennato l'uso principale di queste funzioni è quello di poter consentire ad un programma con i bit \acr{suid} o \acr{sgid} impostati (vedi @@ -1934,12 +1979,14 @@ Come esempio per chiarire l'uso di queste funzioni prendiamo quello con cui viene gestito l'accesso al file \sysfiled{/var/run/utmp}. In questo file viene registrato chi sta usando il sistema al momento corrente; chiaramente non può essere lasciato aperto in scrittura a qualunque utente, che potrebbe -falsificare la registrazione. Per questo motivo questo file (e l'analogo -\sysfiled{/var/log/wtmp} su cui vengono registrati login e logout) appartengono -ad un gruppo dedicato (in genere \acr{utmp}) ed i programmi che devono -accedervi (ad esempio tutti i programmi di terminale in X, o il programma -\cmd{screen} che crea terminali multipli su una console) appartengono a questo -gruppo ed hanno il bit \acr{sgid} impostato. +falsificare la registrazione. + +Per questo motivo questo file (e l'analogo \sysfiled{/var/log/wtmp} su cui +vengono registrati login e logout) appartengono ad un gruppo dedicato (in +genere \acr{utmp}) ed i programmi che devono accedervi (ad esempio tutti i +programmi di terminale in X, o il programma \cmd{screen} che crea terminali +multipli su una console) appartengono a questo gruppo ed hanno il bit +\acr{sgid} impostato. Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato, la situazione degli identificatori è la seguente: @@ -1981,10 +2028,10 @@ Occorre però tenere conto che tutto questo non è possibile con un processo con i privilegi di amministratore, in tal caso infatti l'esecuzione di una \func{setuid} comporta il cambiamento di tutti gli identificatori associati al processo, rendendo impossibile riguadagnare i privilegi di amministratore. -Questo comportamento è corretto per l'uso che ne fa \cmd{login} una volta che -crea una nuova shell per l'utente, ma quando si vuole cambiare soltanto -l'\ids{UID} effettivo del processo per cedere i privilegi occorre -ricorrere ad altre funzioni. +Questo comportamento è corretto per l'uso che ne fa un programma come +\cmd{login} una volta che crea una nuova shell per l'utente, ma quando si +vuole cambiare soltanto l'\ids{UID} effettivo del processo per cedere i +privilegi occorre ricorrere ad altre funzioni. Le due funzioni di sistema \funcd{setreuid} e \funcd{setregid} derivano da BSD che, non supportando (almeno fino alla versione 4.3+BSD) gli identificatori @@ -2000,7 +2047,7 @@ del gruppo \textit{saved}, le usa per poter scambiare fra di loro \fdesc{Imposta \ids{GID} reale e \ids{GID} effettivo del processo corrente.} } {Le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, nel qual -caso \var{errno} può assumere solo il valore \errcode{EPERM}. +caso \var{errno} assume i valori visti per \func{setuid}/\func{setgid}. } \end{funcproto} @@ -2008,12 +2055,13 @@ Le due funzioni sono identiche, quanto diremo per la prima riguardo gli \ids{UID} si applica alla seconda per i \ids{GID}. La funzione \func{setreuid} imposta rispettivamente l'\ids{UID} reale e l'\ids{UID} effettivo del processo corrente ai valori specificati da \param{ruid} -e \param{euid}. I processi non privilegiati possono impostare solo valori che -corrispondano o al loro \ids{UID} effettivo o a quello reale o a quello -salvato, valori diversi comportano il fallimento della chiamata. -L'amministratore invece può specificare un valore qualunque. Specificando un -argomento di valore $-1$ l'identificatore corrispondente verrà lasciato -inalterato. +e \param{euid}. + +I processi non privilegiati possono impostare solo valori che corrispondano o +al loro \ids{UID} effettivo o a quello reale o a quello salvato, valori +diversi comportano il fallimento della chiamata. L'amministratore invece può +specificare un valore qualunque. Specificando un argomento di valore $-1$ +l'identificatore corrispondente verrà lasciato inalterato. Con queste funzioni si possono scambiare fra loro gli \ids{UID} reale ed effettivo, e pertanto è possibile implementare un comportamento simile a @@ -2052,7 +2100,8 @@ del gruppo \textit{effective} ed i loro prototipi sono: \fdesc{Imposta il \ids{GID} effettivo del processo corrente.} } {Le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, nel qual -caso \var{errno} può assumere solo il valore \errcode{EPERM}. + caso \var{errno} assume i valori visti per \func{setuid}/\func{setgid} + tranne \errval{EAGAIN}. } \end{funcproto} @@ -2079,7 +2128,7 @@ un completo controllo su tutti e tre i gruppi di identificatori \fdesc{Imposta il \ids{GID} reale, effettivo e salvato del processo corrente.} } {Le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, nel qual -caso \var{errno} può assumere solo il valore \errcode{EPERM}. +caso \var{errno} assume i valori visti per \func{setuid}/\func{setgid}. } \end{funcproto} @@ -2088,7 +2137,7 @@ gli \ids{UID} si applica alla seconda per i \ids{GID}. La funzione \func{setresuid} imposta l'\ids{UID} reale, l'\ids{UID} effettivo e l'\ids{UID} salvato del processo corrente ai valori specificati rispettivamente dagli argomenti \param{ruid}, \param{euid} e \param{suid}. I -processi non privilegiati possono cambiare uno qualunque degli\ids{UID} solo +processi non privilegiati possono cambiare uno qualunque degli \ids{UID} solo ad un valore corrispondente o all'\ids{UID} reale, o a quello effettivo o a quello salvato, l'amministratore può specificare i valori che vuole. Un valore di $-1$ per un qualunque argomento lascia inalterato l'identificatore @@ -2154,9 +2203,10 @@ programmi portabili; i loro prototipi sono: \fdecl{int setfsgid(gid\_t fsgid)} \fdesc{Legge il \ids{GID} di filesystem del processo corrente.} } -{Le funzioni restituiscono il nuovo valore dell'identificativo in caso di - successo e quello corrente per un errore, in questo caso non viene però - impostato nessun codice di errore in \var{errno}.} + +{Le funzioni restituiscono sia in caso di successo che di errore il valore + corrente dell'identificativo, e in caso di errore non viene impostato nessun + codice in \var{errno}.} \end{funcproto} Le due funzioni sono analoghe ed usano il valore passato come argomento per @@ -2165,6 +2215,12 @@ solo se il processo chiamante ha i privilegi di amministratore o, per gli altri utenti, se il valore specificato coincide con uno dei di quelli del gruppo \textit{real}, \textit{effective} o \textit{saved}. +Il problema di queste funzioni è che non restituiscono un codice di errore e +non c'è modo di sapere (con una singola chiamata) di sapere se hanno avuto +successo o meno, per verificarlo occorre eseguire una chiamata aggiuntiva +passando come argomento $-1$ (un valore impossibile per un identificativo), +così fallendo si può di ottenere il valore corrente e verificare se è +cambiato. \subsection{Le funzioni per la gestione dei gruppi associati a un processo} \label{sec:proc_setgroups} @@ -2200,8 +2256,8 @@ La funzione legge gli identificatori dei gruppi supplementari del processo sul vettore \param{list} che deve essere di dimensione pari a \param{size}. Non è specificato se la funzione inserisca o meno nella lista il \ids{GID} effettivo del processo. Se si specifica un valore di \param{size} uguale a $0$ allora -l'argomento \param{list} non viene modificato, ma si ottiene il numero di -gruppi supplementari. +l'argomento \param{list} non viene modificato, ma si ottiene dal valore di +ritorno il numero di gruppi supplementari. Una seconda funzione, \funcd{getgrouplist}, può invece essere usata per ottenere tutti i gruppi a cui appartiene utente identificato per nome; il suo @@ -2342,7 +2398,7 @@ 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 +processo; in Linux un processo può trovarsi in uno degli stati riportati in tab.~\ref{tab:proc_proc_states}; ma soltanto i processi che sono nello stato \textit{runnable} concorrono per l'esecuzione. Questo vuol dire che, qualunque sia la sua priorità, un processo non potrà mai essere messo in esecuzione @@ -2357,7 +2413,7 @@ fintanto che esso si trova in uno qualunque degli altri stati. \hline \hline \textit{runnable}& \texttt{R} & Il processo è in esecuzione o è pronto ad - essere eseguito (cioè è in attesa che gli + essere eseguito (in attesa che gli venga assegnata la CPU).\\ \textit{sleep} & \texttt{S} & Il processo è in attesa di un risposta dal sistema, ma può essere @@ -2441,9 +2497,9 @@ prevede solo priorità dinamiche. È di questo che, di norma, ci si dovrà preoccupare nella programmazione. Come accennato in Linux i processi ordinari hanno tutti una priorità assoluta nulla; quello che determina quale, fra tutti i processi in attesa di esecuzione, sarà eseguito per primo, è la cosiddetta -\textsl{priorità dinamica},\footnote{quella che viene mostrata nella colonna - \texttt{PR} del comando \texttt{top}.} che è chiamata così proprio perché -varia nel corso dell'esecuzione di un processo. +\textsl{priorità dinamica}, quella che viene mostrata nella colonna +\texttt{PR} del comando \texttt{top}, che è chiamata così proprio perché varia +nel corso dell'esecuzione di un processo. Il meccanismo usato da Linux è in realtà piuttosto complesso,\footnote{e dipende strettamente dalla versione di kernel; in particolare a partire @@ -2463,18 +2519,19 @@ in stato \textit{runnable} ma non viene posto in esecuzione.\footnote{in 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 - \textit{scheduler} si legga il quarto capitolo di \cite{LinKernDev}.} Lo -\textit{scheduler} infatti mette sempre in esecuzione, fra tutti i processi in -stato \textit{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. + rendere l'idea del funzionamento, per una trattazione più dettagliata dei + meccanismi di funzionamento dello \textit{scheduler}, anche se non + aggiornatissima, si legga il quarto capitolo di \cite{LinKernDev}.} + +Lo \textit{scheduler} infatti mette sempre in esecuzione, fra tutti i processi +in stato \textit{runnable}, quello che ha il valore di priorità dinamica più +basso; 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 @@ -2573,19 +2630,18 @@ caso \var{errno} assumerà uno dei valori: \end{errlist}} \end{funcproto} -La funzione permette, a seconda di quanto specificato -nell'argomento \param{which}, di leggere il valore di \textit{nice} di un -processo, di un gruppo di processi (vedi sez.~\ref{sec:sess_proc_group}) o di -un utente indicato dall'argomento \param{who}. Nelle vecchie versioni può -essere necessario includere anche \headfiled{sys/time.h}, questo non è più -necessario con versioni recenti delle librerie, ma è comunque utile per -portabilità. +La funzione permette, a seconda di quanto specificato nell'argomento +\param{which}, di leggere il valore di \textit{nice} o di un processo, o di un +gruppo di processi (vedi sez.~\ref{sec:sess_proc_group}) o di un utente, +indicati con l'argomento \param{who}. Nelle vecchie versioni può essere +necessario includere anche \headfiled{sys/time.h}, questo non è più necessario +con versioni recenti delle librerie, ma è comunque utile per portabilità. I valori possibili per \param{which}, ed il tipo di valore che occorre usare -in corrispondenza per \param{who} solo elencati nella legenda di +in corrispondenza per \param{who}, solo elencati nella legenda di tab.~\ref{tab:proc_getpriority} insieme ai relativi significati. Usare un valore nullo per \param{who} indica, a seconda della corrispondente -indicazione usata per \param{which} il processo, il gruppo di processi o +indicazione usata per \param{which}, il processo, il gruppo di processi o l'utente correnti. \begin{table}[htb] @@ -2610,7 +2666,7 @@ l'utente correnti. In caso di una indicazione che faccia riferimento a più processi, la funzione restituisce la priorità più alta (cioè il valore più basso) fra quelle dei -processi corrispondenti. Come per \func{nice} $-1$ è un valore possibile +processi corrispondenti. Come per \func{nice}, $-1$ è un possibile valore corretto, per cui di nuovo per poter rilevare una condizione di errore è necessario cancellare sempre \var{errno} prima della chiamata alla funzione e quando si ottiene un valore di ritorno uguale a $-1$ per verificare che essa @@ -2645,9 +2701,9 @@ i quali valgono le stesse considerazioni fatte per \func{getpriority} e lo specchietto di tab.~\ref{tab:proc_getpriority}. 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}, nell'intervallo fra \const{PRIO\_MIN} ($-20$) e -\const{PRIO\_MAX} ($19$). La funzione restituisce il valore di \textit{nice} +di \textit{nice} da assegnare nell'intervallo fra \const{PRIO\_MIN} ($-20$) e +\const{PRIO\_MAX} ($19$), 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 @@ -2679,33 +2735,33 @@ valore di \textit{nice} è cambiato parecchio nelle progressive riscritture dello \textit{scheduler} di Linux, ed in particolare a partire dal kernel 2.6.23 l'uso di diversi valori di \textit{nice} ha un impatto molto più forte nella distribuzione della CPU ai processi. Infatti se viene comunque calcolata -una priorità dinamica per i processi che non ricevono la CPU così che anche +una priorità dinamica per i processi che non ricevono la CPU, così che anche essi possano essere messi in esecuzione, un alto valore di \textit{nice} corrisponde comunque ad una \textit{time-slice} molto piccola che non cresce comunque, per cui un processo a bassa priorità avrà davvero scarse possibilità di essere eseguito in presenza di processi attivi a priorità più alta. - \subsection{Il meccanismo di \textit{scheduling real-time}} \label{sec:proc_real_time} 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 \textit{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 \textit{hard real-time}. In tal caso - infatti gli interrupt vengono intercettati dall'interfaccia - \textit{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 \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. +le priorità assolute per permettere la gestione di processi +\textit{real-time}. In realtà nel caso di Linux non si tratta di un vero +\textit{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 \textit{hard real-time}. In tal caso infatti gli interrupt + vengono intercettati dall'interfaccia \textit{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 +\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 @@ -2768,15 +2824,15 @@ ordinarie o viceversa, che di specificare, in caso di politiche caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EINVAL}] il valore di \param{policy} non esiste o il - relativo valore di \param{p} non è valido per la politica scelta. + valore di \param{p} non è valido per la politica scelta. \item[\errcode{EPERM}] il processo non ha i privilegi per attivare la politica richiesta. \item[\errcode{ESRCH}] il processo \param{pid} non esiste. - \end{errlist}} + \end{errlist}} \end{funcproto} La funzione esegue l'impostazione per il processo specificato dall'argomento -\param{pid}, un valore nullo di questo argomento esegue l'impostazione per il +\param{pid}; un valore nullo di questo argomento esegue l'impostazione per il processo corrente. La politica di \textit{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 @@ -2813,6 +2869,7 @@ corrente. % TODO Aggiungere SCHED_DEADLINE, sulla nuova politica di scheduling aggiunta % con il kernel 3.14, vedi anche Documentation/scheduler/sched-deadline.txt e % http://lwn.net/Articles/575497/ +% vedi anche man 7 sched, man sched_setattr Con le versioni più recenti del kernel sono state introdotte anche delle varianti sulla politica di \textit{scheduling} tradizionale per alcuni carichi @@ -2827,9 +2884,9 @@ di \textit{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}. +che in questo modo, pur non perdendo il loro valore di \textit{nice}, sono +leggermente sfavoriti rispetto ai processi interattivi che devono rispondere a +dei dati in ingresso. La politica \const{SCHED\_IDLE} invece è una politica dedicata ai processi che si desidera siano eseguiti con la più bassa priorità possibile, ancora più @@ -2848,7 +2905,7 @@ 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}[!htbp] +\begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{0.5\textwidth} \includestruct{listati/sched_param.c} @@ -2883,9 +2940,9 @@ prototipi sono: \end{errlist}} \end{funcproto} -Le funzioni ritornano rispettivamente i due valori della massima e minima -priorità statica possano essere ottenuti per una delle politiche di -\textit{scheduling} \textit{real-time} indicata dall'argomento \param{policy}. +Le funzioni ritornano rispettivamente il valore massimo e minimo usabile per +la priorità statica di una delle politiche di \textit{scheduling} +\textit{real-time} indicata dall'argomento \param{policy}. Si tenga presente che quando si imposta una politica di \textit{scheduling} real-time per un processo o se ne cambia la priorità statica questo viene @@ -2900,8 +2957,8 @@ 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} -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. +sarà 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 capacità \const{CAP\_SYS\_NICE}, vedi @@ -3055,7 +3112,7 @@ 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 +processi inattivi, con un tempo molto maggiore.\footnote{è comunque possibile ripristinare un comportamento analogo al precedente scrivendo il valore 1 nel file \sysctlfiled{kernel/sched\_compat\_yield}.} @@ -3175,13 +3232,13 @@ questa viene ereditata attraverso una \func{fork}, in questo modo diventa possibile legare automaticamente un gruppo di processi ad un singolo processore. -Nell'uso comune, almeno con i kernel successivi alla serie 2.6.x, l'uso di +Nell'uso comune, almeno con i kernel successivi alla serie 2.6.x, utilizzare questa funzione non è necessario, in quanto è lo \textit{scheduler} stesso che provvede a mantenere al meglio l'affinità di processore. Esistono però esigenze particolari, ad esempio quando un processo (o un gruppo di processi) è utilizzato per un compito importante (ad esempio per applicazioni \textit{real-time} o la cui risposta è critica) e si vuole la massima -velocità, e con questa interfaccia diventa possibile selezionare gruppi di +velocità; con questa interfaccia diventa possibile selezionare gruppi di processori utilizzabili in maniera esclusiva. Lo stesso dicasi quando l'accesso a certe risorse (memoria o periferiche) può avere un costo diverso a seconda del processore, come avviene nelle architetture NUMA @@ -3208,10 +3265,10 @@ cui ogni bit corrisponde ad un processore, ma oggi esistono architetture in cui questo numero può non essere sufficiente, e per questo è stato creato questo tipo opaco e una interfaccia di gestione che permette di usare a basso livello un tipo di dato qualunque rendendosi indipendenti dal numero di bit e -dalla loro disposizione. Per questo le funzioni richiedono anche che oltre -all'insieme di processori si indichi anche la dimensione dello stesso con -l'argomento \param{setsize}, per il quale, se non si usa l'allocazione -dinamica che vedremo a breve, ed è in genere sufficiente passare il valore +dalla loro disposizione. Per questo le funzioni di libreria richiedono che +oltre all'insieme di processori si indichi anche la dimensione dello stesso +con l'argomento \param{setsize}, per il quale, se non si usa l'allocazione +dinamica che vedremo a breve, è in genere sufficiente passare il valore \code{sizeof(cpu\_set\_t)}. L'interfaccia di gestione degli insiemi di processori, oltre alla definizione @@ -3250,7 +3307,7 @@ presente, diverso da zero se è presente). Si tenga presente che trattandosi di macro l'argomento \param{cpu} può essere valutato più volte. Questo significa ad esempio che non si può usare al suo posto una funzione o un'altra macro, altrimenti queste verrebbero eseguite più -volte, l'argomento cioè non deve avere \textsl{effetti collaterali} (in gergo +volte; l'argomento cioè non deve avere \textsl{effetti collaterali} (in gergo \textit{side effects}).\footnote{nel linguaggio C si parla appunto di \textit{side effects} quando si usano istruzioni la cui valutazione comporta effetti al di fuori dell'istruzione stessa, come il @@ -3261,13 +3318,13 @@ volte, l'argomento cioè non deve avere \textsl{effetti collaterali} (in gergo \itindend{side~effects} -Le CPU sono numerate da zero (che indica la prima disponibile) fino ad -un numero massimo che dipende dalla architettura hardware. La costante +Le CPU sono numerate da zero (che indica la prima disponibile) fino ad un +numero massimo che dipende dall'architettura hardware. La costante \constd{CPU\_SETSIZE} indica il numero massimo di processori che possono far parte di un insieme (al momento vale sempre 1024), e costituisce un limite -massimo al valore dell'argomento \param{cpu}. -Dalla versione 2.6 della \acr{glibc} alle precedenti macro è stata aggiunta, -per contare il numero di processori in un insieme, l'ulteriore: +massimo al valore dell'argomento \param{cpu}. Dalla versione 2.6 della +\acr{glibc} alle precedenti macro è stata aggiunta, per contare il numero di +processori in un insieme, l'ulteriore: {\centering \vspace{3pt} @@ -4234,6 +4291,10 @@ elenco, che illustra quelle attualmente disponibili:\footnote{si fa \end{basedescript} +\subsection{I \textit{namespace} ed i \textit{container}} +\label{sec:process_namespaces} + + %TODO sezione separata sui namespace %TODO trattare unshare, vedi anche http://lwn.net/Articles/532748/ @@ -4503,7 +4564,7 @@ varie funzioni di libreria, che sono identificate aggiungendo il suffisso % 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 threads -% LocalWords: void tempnam pathname sibling cap errno EAGAIN ENOMEM +% LocalWords: void tempnam pathname sibling cap errno EAGAIN ENOMEM context % LocalWords: stack read only copy write tab client spawn forktest sleep PATH % LocalWords: source LIBRARY scheduling race condition printf descriptor dup % LocalWords: close group session tms lock vfork execve BSD stream main abort @@ -4513,7 +4574,7 @@ varie funzioni di libreria, che sono identificate aggiungendo il suffisso % LocalWords: filesystem noexec EPERM suid sgid root nosuid ENOEXEC ENOENT ELF % LocalWords: ETXTBSY EINVAL ELIBBAD BIG EFAULT EIO ENAMETOOLONG ELOOP ENOTDIR % LocalWords: ENFILE EMFILE argc execl path execv execle execlp execvp vector -% LocalWords: list environ NULL umask utime cutime ustime fcntl linker +% LocalWords: list environ NULL umask utime cutime ustime fcntl linker Posix % LocalWords: opendir libc interpreter FreeBSD capabilities mandatory access % LocalWords: control MAC SELinux security modules LSM superuser uid gid saved % LocalWords: effective euid egid dell' fsuid fsgid getuid geteuid getgid SVr @@ -4527,7 +4588,7 @@ varie funzioni di libreria, che sono identificate aggiungendo il suffisso % LocalWords: shmctl ioperm iopl chroot ptrace accounting swap reboot hangup % LocalWords: vhangup mknod lease permitted inherited inheritable bounding AND % LocalWords: capability capget capset header ESRCH undef version obj clear PT -% LocalWords: pag ssize length proc capgetp preemptive cache runnable contest +% LocalWords: pag ssize length proc capgetp preemptive cache runnable % LocalWords: SIGSTOP soft slice nice niceness counter which SC switch side % LocalWords: getpriority who setpriority RTLinux RTAI Adeos fault FIFO COUNT % LocalWords: yield Robin setscheduler policy param OTHER priority setparam to @@ -4559,6 +4620,7 @@ varie funzioni di libreria, che sono identificate aggiungendo il suffisso % LocalWords: NEWUTS SETTLS SIGHAND SYSVSEM UNTRACED tls ctid CLEARTID panic % LocalWords: loader EISDIR SIGTRAP uninterrutible killable EQUAL sizeof XOR % LocalWords: destset srcset ALLOC num cpus setsize emacs pager getty TID +% LocalWords: reaper SUBREAPER Library futex %%% Local Variables: %%% mode: latex