\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
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
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}
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
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}).
\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}
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
\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
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
\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
\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
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
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
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
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
\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
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
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
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
\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
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
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}
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
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{<bits/waitstatus.h>} ma questo file non deve mai essere usato
- direttamente, esso viene incluso attraverso \file{<sys/wait.h>}.}
+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{<bits/waitstatus.h>} ma
+ questo file non deve mai essere usato direttamente, esso viene incluso
+ attraverso \file{<sys/wait.h>}.}
\begin{table}[!htb]
\centering
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
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.}
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}.
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},
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
\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
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
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
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} 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}),\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}.} 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
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:
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
\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}
\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
\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}.
}
\end{funcproto}
\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/
% 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
% 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
% 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
% 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