X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;f=prochand.tex;h=173c0bed7e7d585913bacce51ee78a8742d1f6ff;hb=8e2e77dff8f3cffb28ddf982280dff6fc015eb19;hp=f29677b4f28c887de920c0dee4197fa7e4a77a0d;hpb=58a735ea918f55e7eb99dee2052da1b4899e4954;p=gapil.git diff --git a/prochand.tex b/prochand.tex index f29677b..a87e848 100644 --- a/prochand.tex +++ b/prochand.tex @@ -1,244 +1,4360 @@ +%% prochand.tex +%% +%% Copyright (C) 2000-2012 Simone Piccardi. Permission is granted to +%% copy, distribute and/or modify this document under the terms of the GNU Free +%% Documentation License, Version 1.1 or any later version published by the +%% Free Software Foundation; with the Invariant Sections being "Un preambolo", +%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the +%% license is included in the section entitled "GNU Free Documentation +%% License". +%% + \chapter{La gestione dei processi} \label{cha:process_handling} -Come accennato nell'introduzione in un sistema unix ogni attività del sistema -viene svolta tramite i processi. In sostanza i processi costituiscono l'unità -base per l'allocazione e l'uso delle risorse del sistema. +Come accennato nell'introduzione in un sistema unix-like tutte le operazioni +vengono svolte tramite opportuni processi. In sostanza questi ultimi vengono +a costituire l'unità base per l'allocazione e l'uso delle risorse del sistema. + +Nel precedente capitolo abbiamo esaminato il funzionamento di un processo come +unità a se stante, in questo esamineremo il funzionamento dei processi +all'interno del sistema. Saranno cioè affrontati i dettagli della creazione e +della terminazione dei processi, della gestione dei loro attributi e +privilegi, e di tutte le funzioni a questo connesse. Infine nella sezione +finale introdurremo alcune problematiche generiche della programmazione in +ambiente multitasking. -Nel precedente capitolo abbiamo visto come funziona un singolo processo, in -questo capitolo affronteremo i dettagli della creazione e della distruzione -dei processi, della gestione dei loro attributi e privilegi, e di tutte le -funzioni a questo connesse. +\section{Le funzioni di base della gestione dei processi} +\label{sec:proc_handling} -\section{Introduzione} -\label{sec:proc_gen} +In questa sezione tratteremo le problematiche della gestione dei processi +all'interno del sistema, illustrandone tutti i dettagli. Inizieremo con una +panoramica dell'architettura dei processi, tratteremo poi le funzioni +elementari che permettono di leggerne gli identificatori, per poi passare alla +spiegazione delle funzioni base che si usano per la creazione e la +terminazione dei processi, e per la messa in esecuzione degli altri programmi. -Partiremo con una introduzione generale ai concetti che stanno alla base della -gestione dei processi in unix. Introdurremo in questa sezione l'architettura -della gestione dei processi e le sue principali caratteristiche, e daremo una -panoramica sull'uso delle principali funzioni per la gestione dei processi. -\subsection{La gerarchia dei processi} +\subsection{L'architettura della gestione dei processi} \label{sec:proc_hierarchy} -A differenza di quanto avviene in altri sistemi (ad esempio nel VMS la -generazione di nuovi processi è un'operazione privilegiata) una delle -caratteristiche di unix (che esamineremo in dettaglio più avanti) è che -qualunque processo può a sua volta generarne altri, detti processi figli -(\textit{child process}). Ogni processo è identificato presso il sistema da un -numero unico, il \acr{pid} (da \textit{process identifier}). - -Una seconda caratteristica è che la generazione di un processo è una -operazione separata rispetto al lancio di un programma. In genere la sequenza -è sempre quella di creare un nuovo processo, il quale si eseguirà, in un passo -successivo, il programma voluto: questo è ad esempio quello che fa la shell -quando mette in esecuzione il programma che gli indichiamo nella linea di -comando. - -Una terza caratteristica è che ogni processo viene sempre generato da un altro -che viene chiamato processo genitore (\textit{parent process}). Questo vale -per tutti i processi, con una eccezione (dato che ci deve essere un punto di -partenza), esiste sempre infatti un processo speciale, che normalmente è -\cmd{/sbin/init}, che viene lanciato dal kernel quando questo ha finito la -fase di avvio, esso essendo il primo processo lanciato ha sempre il \acr{pid} -uguale a 1 e non è figlio di nessuno. - -Questo è ovviamente un processo speciale, che in genere si occupa di far -partire tutti gli processi altri necessari al funzionamento del sistema, -inoltre \cmd{init} è essenziale per svolgere una serie di compiti -amministrativi nelle operazioni ordinarie del sistema (torneremo si alcuni di -essi in \secref{}) e non può mai essere terminato. La struttura del sistema -comunque consente di lanciare al posto di \cmd{init} qualunque altro programma -(e in casi di emergenza, ad esempio se il file di \cmd{init} si fosse -corrotto, è possibile farlo ad esempio passando la riga \cmd{init=/bin/sh} -all'avvio). - -Dato che tutti i processi successivi sono comunque generati da \cmd{init} o da -suoi figli tutto ciò comporta che, i processi sono organizzati gerarchicamente -dalla relazione fra genitori e figli, in maniera analoga a come i file sono -organizzati in un albero di directory con alla base \file{/} (si veda -\secref{sec:file_file_struct}); in questo caso alla base dell'albero c'è il -processo \cmd{init} che è progenitore di ogni altro processo\footnote{in - realtà questo non è del tutto vero, in Linux ci sono alcuni processi che pur - comparendo come figli di init (ad esempio in \cmd{pstree}) sono generati - direttamente dal kernel, come \cmd{keventd}, \cmd{kswapd}, etc.}. - - -\subsection{Una panoramica sulle funzioni di gestione} -\label{sec:proc_handling_intro} - -I processi vengono creati dalla funzione \func{fork}; in molti unix questa è -una system call, Linux però usa un'altra nomenclatura, e la funzione fork è -basata a sua volta sulla system call \func{clone}, che viene usata anche per -generare i \textit{thread}. Il processo figlio creato dalla \func{fork} è una -copia identica del processo processo padre, ma ha nuovo \acr{pid} e viene -eseguito in maniera indipendente (le differenze fra padre e figlio sono -affrontate in dettaglio in \secref{sec:proc_fork}). - -Se si vuole che il processo padre si fermi fino alla conclusione del processo -figlio questo deve essere specificato subito dopo la \func{fork} chiamando la -funzione \func{wait} o la funzione \func{waitpid}; queste funzioni -restituiscono anche una informazione abbastanza limitata (il codice di uscita) -sulle cause della terminazione del processo. - -Quando un processo ha concluso il suo compito o ha incontrato un errore non -risolvibile esso può essere terminato con la funzione \func{exit} (si veda -quanto discusso in \secref{sec:proc_termination}). La vita del processo però -termina solo quando la notifica della sua conclusione viene ricevuta dal -processo padre, a quel punto tutte le risorse allocate nel sistema ad esso -associate vengono rilasciate. - -Avere due processi che eseguono esattamente lo stesso codice non è molto -utile, normalmente si genera un secondo processo per affidargli l'esecuzione -di un compito specifico (ad esempio gestire una connessione dopo che questa è -stata stabilita), o fargli eseguire (come fa la shell) un altro programma. Per -quest'ultimo caso si usa la seconda funzione fondamentale per programmazione -coi processi che è la \func{exec}. - -Il programma che un processo sta eseguendo si chiama immagine del processo (o -\textit{process image}), le funzioni della famiglia \func{exec} permettono di -caricare un'altro programma da disco sostituendo quest'ultimo all'immagine -corrente; questo fa si che l'immagine precedente venga completamente -cancellata. Questo significa che quando il nuovo programma esce anche il -processo termina, e non si può tornare alla precedente immagine. - -Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto -particolari con caratteristiche uniche rispetto a tutte le altre, infatti la -prima ritorna due volte (nel processo padre e nel figlio) mentre la seconda -non ritorna mai (in quanto con essa viene eseguito un altro programma). - - - -\section{La gestione dei processi} -\label{sec:proc_handling} +A differenza di quanto avviene in altri sistemi, ad esempio nel VMS 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 +\itindex{Process~ID~(PID)} \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 +processo è un'operazione separata rispetto al lancio di un programma. In +genere la sequenza è sempre quella di creare un nuovo processo, il quale +eseguirà, in un passo successivo, il programma desiderato: questo è ad esempio +quello che fa la shell quando mette in esecuzione il programma che gli +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, +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 +struttura del sistema comunque consente di lanciare al posto di \cmd{init} +qualunque altro programma, e in casi di emergenza (ad esempio se il file di +\cmd{init} si fosse corrotto) è ad esempio possibile lanciare una shell al suo +posto.\footnote{la cosa si fa passando la riga \cmd{init=/bin/sh} come + parametro di avvio del kernel, l'argomento è di natura sistemistica e + trattato in sez.~5.3 di \cite{AGL}.} + +\begin{figure}[!htb] + \footnotesize +\begin{Command} +[piccardi@gont piccardi]$ pstree -n +\end{Command} +\begin{Terminal} +init-+-keventd + |-kapm-idled + |-kreiserfsd + |-portmap + |-syslogd + |-klogd + |-named + |-rpc.statd + |-gpm + |-inetd + |-junkbuster + |-master-+-qmgr + | `-pickup + |-sshd + |-xfs + |-cron + |-bash---startx---xinit-+-XFree86 + | `-WindowMaker-+-ssh-agent + | |-wmtime + | |-wmmon + | |-wmmount + | |-wmppp + | |-wmcube + | |-wmmixer + | |-wmgtemp + | |-wterm---bash---pstree + | `-wterm---bash-+-emacs + | `-man---pager + |-5*[getty] + |-snort + `-wwwoffled +\end{Terminal} +%$ + \caption{L'albero dei processi, così come riportato dal comando + \cmd{pstree}.} + \label{fig:proc_tree} +\end{figure} -In questa sezione tratteremo le funzioni per la gestione dei processi, a -partire dalle funzioni elementari che permettono di leggerne gli -identificatori, alle varie funzioni di manipolazione dei processi, che -riguardano la lore creazione, terminazione, e la messa in esecuzione di altri -programmi. +Dato che tutti i processi attivi nel sistema sono comunque generati da +\cmd{init} o da uno dei suoi figli si possono classificare i processi con la +relazione padre/figlio in un'organizzazione gerarchica ad albero. In +fig.~\ref{fig:proc_tree} si è mostrato il risultato del comando \cmd{pstree} +che permette di visualizzare questa struttura, alla cui base c'è \cmd{init} +che è progenitore di tutti gli altri processi.\footnote{in realtà questo non è + del tutto vero, in Linux, specialmente nelle versioni più recenti del + kernel, ci sono alcuni processi speciali (come \cmd{keventd}, \cmd{kswapd}, + ecc.) che pur comparendo nei comandi come figli di \cmd{init}, o con + \ids{PID} successivi ad uno, sono in realtà processi interni al kernel e che + non rientrano in questa classificazione.} + +Il kernel mantiene una tabella dei processi attivi, la cosiddetta +\itindex{process~table} \textit{process table}. Per ciascun processo viene +mantenuta una voce in 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 +\struct{task\_struct} (che in seguito incontreremo a più riprese), è mostrato +in fig.~\ref{fig:proc_task_struct}. + +\begin{figure}[!htb] + \centering \includegraphics[width=14cm]{img/task_struct} + \caption{Schema semplificato dell'architettura delle strutture usate dal + kernel nella gestione dei processi.} + \label{fig:proc_task_struct} +\end{figure} + +% TODO la task_struct è cambiata per qualche dettaglio vedi anche +% http://www.ibm.com/developerworks/linux/library/l-linux-process-management/ +% TODO completare la parte su quando viene chiamato lo scheduler. + +Come accennato in sez.~\ref{sec:intro_unix_struct} è lo \itindex{scheduler} +\textit{scheduler} che decide quale processo mettere in esecuzione; esso viene +eseguito ad ogni \textit{system call} ed ad ogni interrupt e in una serie di +altre occasioni, ma può essere anche attivato esplicitamente. Il timer di +sistema provvede comunque a che esso sia invocato periodicamente; generando un +interrupt periodico secondo la frequenza specificata dalla costante +\const{HZ},\footnote{fino al kernel 2.4 il valore di \const{HZ} era 100 su + tutte le architetture tranne l'alpha, per cui era 1000, nel 2.6 è stato + portato a 1000 su tutte; dal 2.6.13 lo si può impostare in fase di + compilazione del kernel, con un default di 250 e valori possibili di 100, + 250, 1000 e dal 2.6.20 anche 300 (che è divisibile per le frequenze di + refresh della televisione); occorre fare attenzione a non confondere questo + valore con quello dei \itindex{clock~tick} \textit{clock tick} (vedi + sez.~\ref{sec:sys_unix_time}).} definita in \file{asm/param.h}, ed il cui +valore è espresso in Hertz. + +A partire dal kernel 2.6.21 è stato introdotto anche un meccanismo +completamente diverso, detto \textit{tickless}, in cui non c'è più una +interruzione periodica con frequenza prefissata, ma ad ogni chiamata del timer +viene programmata l'interruzione successiva sulla base di una stima; in questo +modo si evita di dover eseguire un migliaio di interruzioni al secondo anche +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 +viene eseguito lo \itindex{scheduler} \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 invocazione. \subsection{Gli identificatori dei processi} \label{sec:proc_pid} -Come accennato nell'introduzione ogni processo viene identificato dal sistema -da un numero identificativo unico, il \textit{process id} o \acr{pid}; -quest'ultimo è un tipo di dato standard, il \type{pid\_t} che in genere è un -intero con segno (nel caso di Linux e delle glibc il tipo usato è \type{int}). - -Il \acr{pid} viene assegnato in forma progressiva ogni volta che un nuovo -processo viene creato, fino ad un limite massimo (in genere essendo detto -numero memorizzato in un intero a 16 bit si arriva a 32767) oltre il quale si -riparte dal numero più basso disponibile (FIXME: verificare, non sono sicuro). -Per questo motivo processo il processo di avvio (\cmd{init}) ha sempre il -\acr{pid} uguale a uno. - -Tutti i processi inoltre memorizzano anche il \acr{pid} del genitore da cui -sono stati creati, questo viene chiamato in genere \acr{ppid} (da -\textit{parent process id}) ed è normalmente utilizzato per il controllo di -sessione. Questi due identificativi possono essere ottenuti da programma -usando le funzioni: -\begin{functions} -\headdecl{sys/types.h} -\headdecl{unistd.h} -\funcdecl{pid\_t getpid(void)} restituisce il pid del processo corrente. -\funcdecl{pid\_t getppid(void)} restituisce il pid del padre del processo - corrente. - -Entrambe le funzioni non riportano condizioni di errore. -\end{functions} - -Il fatto che il \acr{pid} sia un numero univoco per il sistema lo rende il -candidato ideale per generare ultieriori indicatori associati al processo di -cui diventa possibile garantire l'unicità: ad esempio la funzione -\func{tmpname} (si veda \secref{sec:file_temp_file}) usa il \acr{pid} per -generare un pathname univoco, che non potrà essere replicato da un'altro -processo che usi la stessa funzione. - - -\subsection{La funzione \func{fork}} -\label{sec:proc_fork} +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 +intero con segno (nel caso di Linux e della \acr{glibc} il tipo usato è +\ctyp{int}). -La funzione \func{fork} è la funzione fondamentale della gestione dei processi -in unix; come si è detto l'unico modo di creare un nuovo processo è attraverso -l'uso di questa funzione, che è quindi la base per il multitasking; il protipo -della funzione è: +Il \ids{PID} viene assegnato in forma progressiva ogni volta che un nuovo +processo viene creato,\footnote{in genere viene assegnato il numero successivo + a quello usato per l'ultimo processo creato, a meno che questo numero non + sia già utilizzato per un altro \ids{PID}, \acr{pgid} o \acr{sid} (vedi + sez.~\ref{sec:sess_proc_group}).} fino ad un limite che, essendo il +tradizionalmente il \ids{PID} un numero positivo memorizzato in un intero a 16 +bit, arriva ad un massimo di 32768. Oltre questo valore l'assegnazione +riparte dal numero più basso disponibile a partire da un minimo di +300,\footnote{questi valori, fino al kernel 2.4.x, erano definiti dalla macro + \const{PID\_MAX} nei file \file{threads.h} e \file{fork.c} dei sorgenti del + kernel, con il 2.6.x e la nuova interfaccia per i \itindex{thread} + \textit{thread} anche il meccanismo di allocazione dei \ids{PID} è stato + modificato ed il valore massimo è impostabile attraverso il file + \sysctlfile{kernel/pid\_max} e di default vale 32768.} che serve a +riservare i \ids{PID} più bassi ai processi eseguiti direttamente dal kernel. +Per questo motivo, come visto in sez.~\ref{sec:proc_hierarchy}, il processo di +avvio (\cmd{init}) ha sempre il \ids{PID} uguale a uno. -\begin{functions} - \headdecl{sys/types.h} - \headdecl{unistd.h} - - \funcdecl{pid\_t fork(void)} +Tutti i processi inoltre memorizzano anche il \ids{PID} del genitore da cui +sono stati creati, questo viene chiamato in genere \ids{PPID} (da +\itindex{Parent~Process~ID~(PPID)} \textit{Parent Process ID}). Questi due +identificativi possono essere ottenuti usando le due funzioni di sistema +\funcd{getpid} e \funcd{getppid}, i cui prototipi sono: - Le funzioni restituiscono zero in caso di successo e -1 per un errore, in - caso di errore \texttt{errno} può assumere i valori: +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{unistd.h} +\fdecl{pid\_t getpid(void)} +\fdesc{Restituisce il \ids{PID} del processo corrente..} +\fdecl{pid\_t getppid(void)} +\fdesc{Restituisce il \ids{PID} del padre del processo corrente.} +} +{Entrambe le funzioni non riportano condizioni di errore.} +\end{funcproto} + +\noindent esempi dell'uso di queste funzioni sono riportati in +fig.~\ref{fig:proc_fork_code}, nel programma \file{fork\_test.c}. + +Il fatto che il \ids{PID} sia un numero univoco per il sistema lo rende un +candidato per generare ulteriori indicatori associati al processo di cui +diventa possibile garantire l'unicità: ad esempio in alcune implementazioni la +funzione \func{tempnam} (si veda sez.~\ref{sec:file_temp_file}) usa il +\ids{PID} per generare un \textit{pathname} univoco, che non potrà essere +replicato da un altro processo che usi la stessa funzione. Questo utilizzo +però può risultare pericoloso, un \ids{PID} infatti è univoco solo fintanto +che un processo è attivo, una volta terminato esso potrà essere riutilizzato +da un processo completamente diverso, e di questo bisogna essere ben +consapevoli. + +Tutti i processi figli dello stesso processo padre sono detti +\textit{sibling}, questa è una delle relazioni usate nel \textsl{controllo di + sessione}, in cui si raggruppano i processi creati su uno stesso terminale, +o relativi allo stesso login. Torneremo su questo argomento in dettaglio in +cap.~\ref{cha:session}, dove esamineremo gli altri identificativi associati ad +un processo e le varie relazioni fra processi utilizzate per definire una +sessione. + +Oltre al \ids{PID} e al \ids{PPID}, e a quelli che vedremo in +sez.~\ref{sec:sess_proc_group}, relativi al controllo di sessione, ad ogni +processo vengono associati degli ulteriori identificatori ed in particolare +quelli che vengono usati per il controllo di accesso. Questi servono per +determinare se un processo può eseguire o meno le operazioni richieste, a +seconda dei privilegi e dell'identità di chi lo ha posto in esecuzione; +l'argomento è complesso e sarà affrontato in dettaglio in +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 +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} + (vedi \ref{sec:process_clone}), anche perché a partire dalla \acr{glibc} + 2.3.3 non viene più usata la \textit{system call} originale, ma la stessa + \func{fork} viene implementata tramite \func{clone}, cosa che consente una + 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 è: + +\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: \begin{errlist} - \item \macro{EAGAIN} - \item \macro{ENOMEM} - \end{errlist} -\end{functions} + \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 + si è esaurito il numero di processi disponibili. + \item[\errcode{ENOMEM}] non è stato possibile allocare la memoria per le + strutture necessarie al kernel per creare il nuovo processo. + \end{errlist}} +\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 +dall'istruzione successiva alla \func{fork}. Il processo figlio è una copia del +padre, e riceve una copia dei \index{segmento!testo} segmenti di testo, +\index{segmento!dati} dati e dello \itindex{stack} \textit{stack} (vedi +sez.~\ref{sec:proc_mem_layout}), ed esegue esattamente lo stesso codice del +padre. Si tenga presente però che la memoria è copiata e non condivisa, +pertanto padre e figlio vedranno variabili diverse e le eventuali modifiche +saranno totalmente indipendenti. + +Per quanto riguarda la gestione della memoria, in generale il +\index{segmento!testo} segmento di testo, che è identico per i due processi, è +condiviso e tenuto in sola lettura per il padre e per i figli. Per gli altri +segmenti Linux utilizza la tecnica del \itindex{copy~on~write} \textit{copy on + write}. Questa tecnica comporta che una pagina di memoria viene +effettivamente copiata per il nuovo processo solo quando ci viene effettuata +sopra una scrittura, e si ha quindi una reale differenza fra padre e figlio. +In questo modo si rende molto più efficiente il meccanismo della creazione di +un nuovo processo, non essendo più necessaria la copia di tutto lo spazio +degli indirizzi virtuali del padre, ma solo delle pagine di memoria che sono +state modificate, e solo al momento della modifica stessa. + +La differenza che si ha nei due processi è che nel processo padre il valore di +ritorno della funzione \func{fork} è il \ids{PID} del processo figlio, mentre +nel figlio è zero; in questo modo il programma può identificare se viene +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. + +Normalmente la chiamata a \func{fork} può fallire solo per due ragioni: o ci +sono già troppi processi nel sistema, il che di solito è sintomo che +qualcos'altro non sta andando per il verso giusto, o si è ecceduto il limite +sul numero totale di processi permessi all'utente argomento su cui torneremo +in sez.~\ref{sec:sys_resource_limit}, (vedi in particolare +tab.~\ref{tab:sys_rlimit_values}). + +L'uso di \func{fork} avviene secondo due modalità principali; la prima è +quella in cui all'interno di un programma si creano processi figli cui viene +affidata l'esecuzione di una certa sezione di codice, mentre il processo padre +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. + +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 +crea un figlio la cui unica operazione è quella di fare una \func{exec} (di +cui parleremo in sez.~\ref{sec:proc_exec}) subito dopo la \func{fork}. + +Alcuni sistemi operativi (il VMS ad esempio) combinano le operazioni di questa +seconda modalità (una \func{fork} seguita da una \func{exec}) in un'unica +operazione che viene chiamata \textit{spawn}. Nei sistemi unix-like è stato +scelto di mantenere questa separazione, dato che, come per la prima modalità +d'uso, esistono numerosi scenari in cui si può usare una \func{fork} senza +aver bisogno di eseguire una \func{exec}. + +Inoltre, anche nel caso della seconda modalità d'uso, avere le due funzioni +separate permette al figlio di cambiare alcune caratteristiche del processo +(maschera dei segnali, redirezione dell'output, utente per conto del cui viene +eseguito, e molto altro su cui torneremo in seguito) prima della \func{exec}, +rendendo così relativamente facile intervenire sulle le modalità di esecuzione +del nuovo programma. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/fork_test.c} + \end{minipage} + \normalsize + \caption{Esempio di codice per la creazione di nuovi processi (da + \file{fork\_test.c}).} + \label{fig:proc_fork_code} +\end{figure} + +In fig.~\ref{fig:proc_fork_code} è riportato il corpo del codice del programma +di esempio \cmd{forktest}, che permette di illustrare molte caratteristiche +dell'uso della funzione \func{fork}. Il programma crea un numero di figli +specificato da linea di comando, e prende anche alcune opzioni per indicare +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}. + +Decifrato il numero di figli da creare, il ciclo principale del programma +(\texttt{\small 24--40}) esegue in successione la creazione dei processi figli +controllando il successo della chiamata a \func{fork} (\texttt{\small + 25--29}); ciascun figlio (\texttt{\small 31--34}) si limita a stampare il +suo numero di successione, eventualmente attendere il numero di secondi +specificato e scrivere un messaggio prima di uscire. Il processo padre invece +(\texttt{\small 36--38}) stampa un messaggio di creazione, eventualmente +attende il numero di secondi specificato, e procede nell'esecuzione del ciclo; +alla conclusione del ciclo, prima di uscire, può essere specificato un altro +periodo di attesa. + +Se eseguiamo il comando, che è preceduto dall'istruzione \code{export + LD\_LIBRARY\_PATH=./} per permettere l'uso delle librerie dinamiche, senza +specificare attese (come si può notare in (\texttt{\small 17--19}) i valori +predefiniti specificano di non attendere), otterremo come risultato sul +terminale: +\begin{Command} +[piccardi@selidor sources]$ export LD_LIBRARY_PATH=./; ./forktest 3 +\end{Command} +%$ +\begin{Terminal} +Process 1963: forking 3 child +Spawned 1 child, pid 1964 +Child 1 successfully executing +Child 1, parent 1963, exiting +Go to next child +Spawned 2 child, pid 1965 +Child 2 successfully executing +Child 2, parent 1963, exiting +Go to next child +Child 3 successfully executing +Child 3, parent 1963, exiting +Spawned 3 child, pid 1966 +Go to next child +\end{Terminal} + +Esaminiamo questo risultato: una prima conclusione che si può trarre è che non +si può dire quale processo fra il padre ed il figlio venga eseguito per primo +dopo la chiamata a \func{fork}; dall'esempio si può notare infatti come nei +primi due cicli sia stato eseguito per primo il padre (con la stampa del +\ids{PID} del nuovo processo) per poi passare all'esecuzione del figlio +(completata con i due avvisi di esecuzione ed uscita), e tornare +all'esecuzione del padre (con la stampa del passaggio al ciclo successivo), +mentre la terza volta è stato prima eseguito il figlio (fino alla conclusione) +e poi il padre. + +In generale l'ordine di esecuzione dipenderà, oltre che dall'algoritmo di +\itindex{scheduler} \textit{scheduling} usato dal kernel, dalla particolare +situazione in cui si trova la macchina al momento della chiamata, risultando +del tutto impredicibile. Eseguendo più volte il programma di prova e +producendo un numero diverso di figli, si sono ottenute situazioni +completamente diverse, compreso il caso in cui il processo padre ha eseguito +più di una \func{fork} prima che uno dei figli venisse messo in esecuzione. + +Pertanto non si può fare nessuna assunzione sulla sequenza di esecuzione delle +istruzioni del codice fra padre e figli, né sull'ordine in cui questi potranno +essere messi in esecuzione. Se è necessaria una qualche forma di precedenza +occorrerà provvedere ad espliciti meccanismi di sincronizzazione, pena il +rischio di incorrere nelle cosiddette \itindex{race~condition} \textit{race + condition} (vedi sez.~\ref{sec:proc_race_cond}). + +In realtà con l'introduzione dei kernel della serie 2.6 lo \itindex{scheduler} +\textit{scheduler} è stato modificato per eseguire sempre per primo il +figlio.\footnote{i 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 \itindex{copy~on~write} \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 \itindex{copy~on~write} \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 +vantaggio di poter utilizzare immediatamente tutti i dati che sono nella cache +della CPU e nella 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. + +Un secondo aspetto molto importante nella creazione dei processi figli è +quello dell'interazione dei vari processi con i file. Ne parleremo qui anche +se buona parte dei concetti relativi ai file verranno trattati più avanti +(principalmente in sez.~\ref{sec:file_unix_interface}). Per illustrare meglio +quello che avviene si può redirigere su un file l'output del programma di +test, quello che otterremo è: +\begin{Command} +[piccardi@selidor sources]$ ./forktest 3 > output +[piccardi@selidor sources]$ cat output +\end{Command} +\begin{Terminal} +Process 1967: forking 3 child +Child 1 successfully executing +Child 1, parent 1967, exiting +Test for forking 3 child +Spawned 1 child, pid 1968 +Go to next child +Child 2 successfully executing +Child 2, parent 1967, exiting +Test for forking 3 child +Spawned 1 child, pid 1968 +Go to next child +Spawned 2 child, pid 1969 +Go to next child +Child 3 successfully executing +Child 3, parent 1967, exiting +Test for forking 3 child +Spawned 1 child, pid 1968 +Go to next child +Spawned 2 child, pid 1969 +Go to next child +Spawned 3 child, pid 1970 +Go to next child +\end{Terminal} +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. 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. + +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 +le singole righe comparivano a video subito dopo l'esecuzione della +\func{printf}. Ma con la redirezione su file la scrittura non avviene più alla +fine di ogni riga e l'output resta nel buffer. Dato che ogni figlio riceve una +copia della memoria del padre, esso riceverà anche quanto c'è nel buffer delle +funzioni di I/O, comprese le linee scritte dal padre fino allora. Così quando +il buffer viene scritto su disco all'uscita del figlio, troveremo nel file +anche tutto quello che il processo padre aveva scritto prima della sua +creazione. E alla fine del file (dato che in questo caso il padre esce per +ultimo) troveremo anche l'output completo del padre. + +L'esempio ci mostra un altro aspetto fondamentale dell'interazione con i file, +valido anche per l'esempio precedente, ma meno evidente: il fatto cioè che non +solo processi diversi possono scrivere in contemporanea sullo stesso file +(l'argomento dell'accesso concorrente ai file è trattato in dettaglio in +sez.~\ref{sec:file_shared_access}), ma anche che, a differenza di quanto +avviene per le variabili in memoria, la posizione corrente sul file è +condivisa fra il padre e tutti i processi figli. + +Quello che succede è che quando lo \textit{standard output}\footnote{si chiama + così il file su cui un programma scrive i suoi dati in uscita, tratteremo + l'argomento in dettaglio in sez.~\ref{sec:file_fd}.} del padre 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 \itindex{file~table} \textit{file + table} (tratteremo in dettaglio questi termini in +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 \itindex{file~table} \textit{file table}, e tutti gli altri +processi, che vedono la stessa \itindex{file~table} \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. + +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 +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. + +In generale comunque non è buona norma far scrivere più processi sullo stesso +file senza una qualche forma di sincronizzazione in quanto, come visto anche +con il nostro esempio, le varie scritture risulteranno mescolate fra loro in +una sequenza impredicibile. Per questo le modalità con cui in genere si usano +i file dopo una \func{fork} sono sostanzialmente due: +\begin{enumerate*} +\item Il processo padre aspetta la conclusione del figlio. In questo caso non + è necessaria nessuna azione riguardo ai file, in quanto la sincronizzazione + della posizione corrente dopo eventuali operazioni di lettura e scrittura + effettuate dal figlio è automatica. +\item L'esecuzione di padre e figlio procede indipendentemente. In questo caso + ciascuno dei due processi deve chiudere i file che non gli servono una volta + che la \func{fork} è stata eseguita, per evitare ogni forma di interferenza. +\end{enumerate*} + +Oltre ai file aperti i processi figli ereditano dal padre una serie di altre +proprietà; la lista dettagliata delle proprietà che padre e figlio hanno in +comune dopo l'esecuzione di una \func{fork} è la seguente: +\begin{itemize*} +\item i file aperti e gli eventuali flag di \itindex{close-on-exec} + \textit{close-on-exec} impostati (vedi sez.~\ref{sec:proc_exec} e + sez.~\ref{sec:file_fcntl_ioctl}); +\item gli identificatori per il controllo di accesso: l'\textsl{user-ID + reale}, il \textsl{group-ID reale}, l'\textsl{user-ID effettivo}, il + \textsl{group-ID effettivo} ed i \textit{group-ID supplementari} (vedi + sez.~\ref{sec:proc_access_id}); +\item gli identificatori per il controllo di sessione: il + \itindex{process~group} \textit{process group-ID} e il \textit{session id} + ed il terminale di controllo (vedi sez.~\ref{sec:sess_proc_group}); +\item la \index{directory~di~lavoro} directory di lavoro e la directory radice + (vedi sez.~\ref{sec:file_work_dir} e sez.~\ref{sec:file_chroot}); +\item la maschera dei permessi di creazione dei file (vedi + sez.~\ref{sec:file_perm_management}); +\item la maschera dei segnali bloccati (vedi sez.~\ref{sec:sig_sigmask}) e le + azioni installate (vedi sez.~\ref{sec:sig_gen_beha}); +\item i segmenti di memoria condivisa agganciati al processo (vedi + sez.~\ref{sec:ipc_sysv_shm}); +\item i limiti sulle risorse (vedi sez.~\ref{sec:sys_resource_limit}); +\item il valori di \textit{nice}, le priorità real-time e le affinità di + processore (vedi sez.~\ref{sec:proc_sched_stand}, + sez.~\ref{sec:proc_real_time} e sez.~\ref{sec:proc_sched_multiprocess}); +\item le variabili di ambiente (vedi sez.~\ref{sec:proc_environ}). +\item l'insieme dei descrittori associati alle code di messaggi POSIX (vedi + sez.~\ref{sec:ipc_posix_mq}) che vengono copiate come i \textit{file + descriptor}, questo significa che entrambi condivideranno gli stessi flag. +\end{itemize*} + +Oltre a quelle relative ad un diverso spazio degli indirizzi (e una memoria +totalmente indipendente) le differenze fra padre e figlio dopo l'esecuzione di +una \func{fork} invece sono:\footnote{a parte le ultime quattro, relative a + funzionalità specifiche di Linux, le altre sono esplicitamente menzionate + dallo standard POSIX.1-2001.} +\begin{itemize*} +\item il valore di ritorno di \func{fork}; +\item il \ids{PID} (\textit{process id}), quello del figlio viene assegnato ad + un nuovo valore univoco; +\item il \ids{PPID} (\textit{parent process id}), quello del figlio viene + impostato al \ids{PID} del padre; +\item i valori dei tempi di esecuzione (vedi sez.~\ref{sec:sys_cpu_times}) e + delle risorse usate (vedi sez.~\ref{sec:sys_resource_use}), che nel figlio + sono posti a zero; +\item i \textit{lock} sui file (vedi sez.~\ref{sec:file_locking}) e sulla + memoria (vedi sez.~\ref{sec:proc_mem_lock}), che non vengono ereditati dal + figlio; +\item gli allarmi, i timer (vedi sez.~\ref{sec:sig_alarm_abort}) ed i segnali + pendenti (vedi sez.~\ref{sec:sig_gen_beha}), che per il figlio vengono + cancellati. +\item le operazioni di I/O asincrono in corso (vedi + sez.~\ref{sec:file_asyncronous_io}) che non vengono ereditate dal figlio; +\item gli aggiustamenti fatti dal padre ai semafori con \func{semop} (vedi + sez.~\ref{sec:ipc_sysv_sem}). +\item le notifiche sui cambiamenti delle directory con \textit{dnotify} (vedi + sez.~\ref{sec:sig_notification}), che non vengono ereditate dal figlio; +\item le mappature di memoria marcate come \const{MADV\_DONTFORK} (vedi + sez.~\ref{sec:file_memory_map}) che non vengono ereditate dal figlio; +\item l'impostazione con \func{prctl} (vedi sez.~\ref{sec:process_prctl}) che + notifica al figlio la terminazione del padre viene cancellata se presente + nel padre; +\item il segnale di terminazione del figlio è sempre \signal{SIGCHLD} anche + qualora nel padre fosse stato modificato (vedi sez.~\ref{sec:process_clone}). +\end{itemize*} + +Una seconda funzione storica usata per la creazione di un nuovo processo è +\funcm{vfork}, che è esattamente identica a \func{fork} ed ha la stessa +semantica e gli stessi errori; la sola differenza è che non viene creata la +tabella delle pagine né la struttura dei task per il nuovo processo. Il +processo padre è posto in attesa fintanto che il figlio non ha eseguito una +\func{execve} o non è uscito con una \func{\_exit}. Il figlio condivide la +memoria del padre (e modifiche possono avere effetti imprevedibili) e non deve +ritornare o uscire con \func{exit} ma usare esplicitamente \func{\_exit}. + +Questa funzione è un rimasuglio dei vecchi tempi in cui eseguire una +\func{fork} comportava anche la copia completa del segmento dati del processo +padre, che costituiva un inutile appesantimento in tutti quei casi in cui la +\func{fork} veniva fatta solo per poi eseguire una \func{exec}. La funzione +venne introdotta in BSD per migliorare le prestazioni. + +Dato che Linux supporta il \itindex{copy~on~write} \textit{copy on write} la +perdita di prestazioni è assolutamente trascurabile, e l'uso di questa +funzione, che resta un caso speciale della \textit{system call} \func{clone} +(che tratteremo in dettaglio in sez.~\ref{sec:process_clone}) è deprecato; per +questo eviteremo di trattarla ulteriormente. + + +\subsection{La conclusione di un processo} +\label{sec:proc_termination} + +In sez.~\ref{sec:proc_conclusion} abbiamo già affrontato le modalità con cui +chiudere un programma, ma dall'interno del programma stesso. Avendo a che fare +con un sistema \textit{multitasking} resta da affrontare l'argomento dal punto +di vista di come il sistema gestisce la conclusione dei processi. + +Abbiamo visto in sez.~\ref{sec:proc_conclusion} le tre modalità con cui un +programma viene terminato in maniera normale: la chiamata di \func{exit}, che +esegue le funzioni registrate per l'uscita e chiude gli \textit{stream} e poi +esegue \func{\_exit}, il ritorno dalla funzione \code{main} equivalente alla +chiamata di \func{exit}, e la chiamata diretta a \func{\_exit}, che passa +direttamente alle operazioni di terminazione del processo da parte del kernel. + +Ma abbiamo accennato che oltre alla conclusione normale esistono anche delle +modalità di conclusione anomala. Queste sono in sostanza due: il programma può +chiamare la funzione \func{abort} (vedi sez.~\ref{sec:sig_alarm_abort}) per +invocare una chiusura anomala, o essere terminato da un segnale (torneremo sui +segnali in cap.~\ref{cha:signals}). In realtà anche la prima modalità si +riconduce alla seconda, dato che \func{abort} si limita a generare il segnale +\signal{SIGABRT}. +Qualunque sia la modalità di conclusione di un processo, il kernel esegue +comunque una serie di operazioni di terminazione: chiude tutti i file aperti, +rilascia la memoria che stava usando, e così via; l'elenco completo delle +operazioni eseguite alla chiusura di un processo è il seguente: +\begin{itemize*} +\item tutti i \textit{file descriptor} (vedi sez.~\ref{sec:file_fd}) sono + chiusi; +\item viene memorizzato lo stato di terminazione del processo; +\item ad ogni processo figlio viene assegnato un nuovo padre (in genere + \cmd{init}); +\item viene inviato il segnale \signal{SIGCHLD} al processo padre (vedi + sez.~\ref{sec:sig_sigchld}); +\item se il processo è un leader di sessione ed il suo terminale di controllo + è quello della sessione viene mandato un segnale di \signal{SIGHUP} a tutti i + processi del gruppo di \textit{foreground} e il terminale di controllo viene + disconnesso (vedi sez.~\ref{sec:sess_ctrl_term}); +\item se la conclusione di un processo rende orfano un \textit{process + group} ciascun membro del gruppo viene bloccato, e poi gli vengono + inviati in successione i segnali \signal{SIGHUP} e \signal{SIGCONT} + (vedi ancora sez.~\ref{sec:sess_ctrl_term}). +\end{itemize*} -Dopo l'esecuzione di una fork sia il processo padre che il processo figlio -continuano ad essere eseguiti normalmente, ed il processo figlio esegue -esattamente lo stesso codice del padre. La sola differenza è che nel processo -padre il valore di ritorno della funzione fork è il pid del processo figlio, -mentre nel figlio è zero; in questo modo il programma può identificare se -viene eseguito dal padre o dal figlio. +\itindbeg{termination~status} +Oltre queste operazioni è però necessario poter disporre di un meccanismo +ulteriore che consenta di sapere come la terminazione è avvenuta: dato che in +un sistema unix-like tutto viene gestito attraverso i processi, il meccanismo +scelto consiste nel riportare lo \itindex{termination~status} \textsl{stato di + terminazione} (il cosiddetto \textit{termination status}) al processo padre. +Nel caso di conclusione normale, abbiamo visto in +sez.~\ref{sec:proc_conclusion} che lo stato di uscita del processo viene +caratterizzato tramite il valore del cosiddetto \textit{exit status}, cioè il +valore passato come argomento alle funzioni \func{exit} o \func{\_exit} o il +valore di ritorno per \code{main}. Ma se il processo viene concluso in +maniera anomala il programma non può specificare nessun \textit{exit status}, +ed è il kernel che deve generare autonomamente il \textit{termination status} +per indicare le ragioni della conclusione anomala. +Si noti la distinzione fra \textit{exit status} e \textit{termination status}: +quello che contraddistingue lo stato di chiusura del processo e viene +riportato attraverso le funzioni \func{wait} o \func{waitpid} (vedi +sez.~\ref{sec:proc_wait}) è sempre quest'ultimo; in caso di conclusione +normale il kernel usa il primo (nel codice eseguito da \func{\_exit}) per +produrre il secondo. -\subsection{Le funzioni \texttt{wait} e \texttt{waitpid}} +La scelta di riportare al padre lo stato di terminazione dei figli, pur +essendo l'unica possibile, comporta comunque alcune complicazioni: infatti se +alla sua creazione è scontato che ogni nuovo processo abbia un padre, non è +detto che sia così alla sua conclusione, dato che il padre potrebbe essere già +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 +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 + fatale.} + +Come già accennato quando un processo termina, il kernel controlla se è il +padre di altri processi in esecuzione: in caso positivo allora il \ids{PPID} +di tutti questi processi verrà sostituito dal kernel con il \ids{PID} di +\cmd{init}, cioè con 1. In questo modo ogni processo avrà sempre un padre (nel +caso possiamo parlare di un padre \textsl{adottivo}) cui riportare il suo +stato di terminazione. + +Come verifica di questo comportamento possiamo eseguire il nostro programma +\cmd{forktest} imponendo a ciascun processo figlio due secondi di attesa prima +di uscire, il risultato è: +\begin{Command} +[piccardi@selidor sources]$ ./forktest -c2 3 +\end{Command} +\begin{Terminal}[commandchars=\\\{\}] +Process 1972: forking 3 child +Spawned 1 child, pid 1973 +Child 1 successfully executing +Go to next child +Spawned 2 child, pid 1974 +Child 2 successfully executing +Go to next child +Child 3 successfully executing +Spawned 3 child, pid 1975 +Go to next child + +\textbf{[piccardi@selidor sources]$} Child 3, parent 1, exiting +Child 2, parent 1, exiting +Child 1, parent 1, exiting +\end{Terminal} +come si può notare in questo caso il processo padre si conclude prima dei +figli, tornando alla shell, che stampa il prompt sul terminale: circa due +secondi dopo viene stampato a video anche l'output dei tre figli che +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. + +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 +dal processo (vedi sez.~\ref{sec:sys_unix_time}) e lo stato di terminazione, +mentre la memoria in uso ed i file aperti vengono rilasciati immediatamente. + +I processi che sono terminati, ma il cui stato di terminazione non è stato +ancora ricevuto dal padre sono chiamati \itindex{zombie} \textit{zombie}, essi +restano presenti nella tabella dei processi ed in genere possono essere +identificati dall'output di \cmd{ps} per la presenza di una \texttt{Z} nella +colonna che ne indica lo stato (vedi tab.~\ref{tab:proc_proc_states}). Quando +il padre effettuerà la lettura dello stato di terminazione anche questa +informazione, non più necessaria, verrà scartata ed il processo potrà +considerarsi completamente concluso. + +Possiamo utilizzare il nostro programma di prova per analizzare anche questa +condizione: lanciamo il comando \cmd{forktest} in \textit{background} (vedi +sez.~\ref{sec:sess_job_control}), indicando al processo padre di aspettare 10 +secondi prima di uscire. In questo caso, usando \cmd{ps} sullo stesso +terminale (prima dello scadere dei 10 secondi) otterremo: +\begin{Command} +[piccardi@selidor sources]$ ps T +\end{Command} +%$ +\begin{Terminal} + PID TTY STAT TIME COMMAND + 419 pts/0 S 0:00 bash + 568 pts/0 S 0:00 ./forktest -e10 3 + 569 pts/0 Z 0:00 [forktest ] + 570 pts/0 Z 0:00 [forktest ] + 571 pts/0 Z 0:00 [forktest ] + 572 pts/0 R 0:00 ps T +\end{Terminal} +e come si vede, dato che non si è fatto nulla per riceverne lo stato di +terminazione, i tre processi figli sono ancora presenti pur essendosi +conclusi, con lo stato di \itindex{zombie} \textit{zombie} e l'indicazione che +sono terminati (la scritta \texttt{defunct}). + +La possibilità di avere degli \itindex{zombie} \textit{zombie} deve essere +tenuta sempre presente quando si scrive un programma che deve essere mantenuto +in esecuzione a lungo e creare molti figli. In questo caso si deve sempre +avere cura di far leggere l'eventuale stato di uscita di tutti i figli. In +genere questo si fa attraverso un apposito \textit{signal handler}, che chiama +la funzione \func{wait}, (vedi sez.~\ref{sec:sig_sigchld} e +sez.~\ref{sec:proc_wait}) di cui vedremo un esempio in +fig.~\ref{fig:sig_sigchld_handl}. + +Questa operazione è necessaria perché anche se gli \itindex{zombie} +\textit{zombie} non consumano risorse di memoria o processore, occupano +comunque una voce nella tabella dei processi e se li si lascia accumulare a +lungo quest'ultima potrebbe riempirsi, con l'impossibilità di lanciare nuovi +processi. + +Si noti tuttavia che quando un processo adottato da \cmd{init} termina, non +diviene mai uno \itindex{zombie} \textit{zombie}. Questo perché una delle +funzioni di \cmd{init} è appunto quella di chiamare la funzione \func{wait} +per i processi a cui fa da padre, completandone la terminazione. Questo è +quanto avviene anche quando, come nel caso del precedente esempio con +\cmd{forktest}, il padre termina con dei figli in stato di \itindex{zombie} +\textit{zombie}. Questi scompaiono quando, alla terminazione del padre dopo i +secondi programmati, tutti figli che avevamo generato, e che erano diventati +\itindex{zombie} \textit{zombie}, vengono adottati da \cmd{init}, il quale +provvede a completarne la terminazione. + +Si tenga presente infine che siccome gli \itindex{zombie} \textit{zombie} sono +processi già terminati, non c'è modo di eliminarli con il comando \cmd{kill} o +inviandogli un qualunque segnale di terminazione (l'argomento è trattato in +sez.~\ref{sec:sig_termination}). L'unica possibilità di cancellarli dalla +tabella dei processi è quella di terminare il processo che li ha generati, in +modo che \cmd{init} possa adottarli e concluderne la terminazione. + +\subsection{Le funzioni di attesa e ricezione degli stati di uscita} \label{sec:proc_wait} -\subsection{Le funzioni \texttt{exec}} +Uno degli usi più comuni delle capacità multitasking di un sistema unix-like +consiste nella creazione di programmi di tipo server, in cui un processo +principale attende le richieste che vengono poi soddisfatte da una serie di +processi figli. + +Si è già sottolineato al paragrafo precedente come in questo caso diventi +necessario gestire esplicitamente la conclusione dei figli onde evitare di +riempire di \itindex{zombie} \textit{zombie} la tabella dei +processi. Tratteremo in questa sezione le funzioni di sistema deputate a +questo compito; la prima è \funcd{wait} ed il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/wait.h} +\fdecl{pid\_t wait(int *status)} +\fdesc{Attende la terminazione di un processo.} +} +{La funzione ritorna il \ids{PID} del figlio in caso di successo e $-1$ per un + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{ECHILD}] il processo non ha nessun figlio di cui attendere + uno stato di terminazione. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \end{errlist}} +\end{funcproto} + +Questa funzione è presente fin dalle prime versioni di Unix ed è quella usata +tradizionalmente per attendere la terminazione dei figli. La funzione sospende +l'esecuzione del processo corrente e ritorna non appena un qualunque processo +figlio termina. Se un figlio è già terminato prima della sua chiamata la +funzione ritorna immediatamente, se più processi figli sono già terminati +occorrerà continuare a chiamare la funzione più volte fintanto che non si è +recuperato lo stato di terminazione di tutti quanti. + +Al ritorno della funzione lo stato di terminazione del figlio viene salvato +(come \itindex{value~result~argument} \textit{value result argument}) nella +variabile puntata da \param{status} e tutte le risorse del kernel relative al +processo (vedi sez.~\ref{sec:proc_termination}) vengono rilasciate. Nel caso +un processo abbia più figli il valore di ritorno della funzione sarà impostato +al \ids{PID} del processo di cui si è ricevuto lo stato di terminazione, cosa +che permette di identificare qual è il figlio che è terminato. + +\itindend{termination~status} + +Questa funzione ha il difetto di essere poco flessibile, in quanto ritorna +all'uscita di un qualunque processo figlio. Nelle occasioni in cui è +necessario attendere la conclusione di uno specifico processo fra tutti quelli +esistenti occorre predisporre un meccanismo che tenga conto di tutti processi +che sono terminati, e provveda a ripetere la chiamata alla funzione nel caso +il processo cercato non risulti fra questi. Se infatti il processo cercato è +già terminato e se è già ricevuto lo stato di uscita senza registrarlo, la +funzione non ha modo di accorgersene, e si continuerà a chiamarla senza +accorgersi che quanto interessava è già accaduto. + +Per questo motivo lo standard POSIX.1 ha introdotto una seconda funzione che +effettua lo stesso servizio, ma dispone di una serie di funzionalità più +ampie, legate anche al controllo di sessione (si veda +sez.~\ref{sec:sess_job_control}). Dato che è possibile ottenere lo stesso +comportamento di \func{wait}\footnote{in effetti il codice + \code{wait(\&status)} è del tutto equivalente a \code{waitpid(WAIT\_ANY, + \&status, 0)}.} si consiglia di utilizzare sempre questa nuova funzione di +sistema, \funcd{waitpid}, il cui prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/wait.h} +\fdecl{pid\_t waitpid(pid\_t pid, int *status, int options)} +\fdesc{Attende il cambiamento di stato di un processo figlio.} +} +{La funzione ritorna il \ids{PID} del processo che ha cambiato stato in caso + di successo, o 0 se è stata specificata l'opzione \const{WNOHANG} e il + processo non è uscito e $-1$ per un errore, nel qual caso \var{errno} + assumerà uno dei valori: + \begin{errlist} + \item[\errcode{ECHILD}] il processo specificato da \param{pid} non esiste o + non è figlio del processo chiamante. + \item[\errcode{EINTR}] non è stata specificata l'opzione \const{WNOHANG} e + la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] si è specificato un valore non valido per + l'argomento \param{options}. + \end{errlist}} +\end{funcproto} + +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, +secondo lo specchietto riportato in tab.~\ref{tab:proc_waidpid_pid}, dove si +sono riportate anche le costanti definite per indicare alcuni di essi. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|c|c|p{8cm}|} + \hline + \textbf{Valore} & \textbf{Costante} &\textbf{Significato}\\ + \hline + \hline + $<-1$& -- & Attende per un figlio il cui + \itindex{process~group} \textit{process group} + (vedi sez.~\ref{sec:sess_proc_group}) è uguale + al valore assoluto di \param{pid}.\\ + $-1$&\const{WAIT\_ANY} & Attende per un figlio qualsiasi, usata in + questa maniera senza specificare nessuna opzione + è equivalente a \func{wait}.\\ + $ 0$&\const{WAIT\_MYPGRP}&Attende per un figlio il cui + \itindex{process~group} \textit{process group} + (vedi sez.~\ref{sec:sess_proc_group}) è + uguale a quello del processo chiamante.\\ + $>0$& -- & Attende per un figlio il cui \ids{PID} è uguale + al valore di \param{pid}.\\ + \hline + \end{tabular} + \caption{Significato dei valori dell'argomento \param{pid} della funzione + \func{waitpid}.} + \label{tab:proc_waidpid_pid} +\end{table} + +Il comportamento di \func{waitpid} può inoltre essere modificato passando alla +funzione delle opportune opzioni tramite l'argomento \param{options}; questo +deve essere specificato come maschera binaria delle costanti riportati nella +prima parte in tab.~\ref{tab:proc_waitpid_options} che possono essere +combinate fra loro con un OR aritmetico. Nella seconda parte della stessa +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 \itindex{thread} \textit{thread} (vedi +sez.~\ref{sec:thread_xxx}). + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Costante} & \textbf{Descrizione}\\ + \hline + \hline + \const{WNOHANG} & La funzione ritorna immediatamente anche se non è + terminato nessun processo figlio.\\ + \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).\\ + \hline + \const{\_\_WCLONE}& Attende solo per i figli creati con \func{clone} + (vedi sez.~\ref{sec:process_clone}), vale a dire + processi che non emettono nessun segnale + o emettono un segnale diverso da \signal{SIGCHLD} alla + terminazione, il default è attendere soltanto i + processi figli ordinari ignorando quelli creati da + \func{clone}.\\ + \const{\_\_WALL} & Attende per qualunque figlio, sia ordinario che creato + con \func{clone}, se specificata insieme a + \const{\_\_WCLONE} quest'ultima viene ignorata. \\ + \const{\_\_WNOTHREAD}& Non attende per i figli di altri \textit{thread} + dello stesso \textit{thread group}, questo era il + comportamento di default del kernel 2.4 che non + supportava la possibilità, divenuta il default a + partire dal 2.6, di attendere un qualunque figlio + appartenente allo stesso \textit{thread group}. \\ + \hline + \end{tabular} + \caption{Costanti che identificano i bit dell'argomento \param{options} + della funzione \func{waitpid}.} + \label{tab:proc_waitpid_options} +\end{table} + + +L'uso dell'opzione \const{WNOHANG} consente di prevenire il blocco della +funzione qualora nessun figlio sia uscito o non si siano verificate le altre +condizioni per l'uscita della funzione. in tal caso. In tal caso la funzione, +invece di restituire il \ids{PID} del processo (che è sempre un intero +positivo) ritornerà un valore nullo. + +Le altre due opzioni, \const{WUNTRACED} e \const{WCONTINUED}, consentono +rispettivamente di tracciare non la terminazione di un processo, ma il fatto +che esso sia stato fermato, o fatto ripartire, e sono utilizzate per la +gestione del controllo di sessione (vedi sez.~\ref{sec:sess_job_control}). + +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}). + +La terminazione di un processo figlio (così come gli altri eventi osservabili +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. + +Il comportamento delle funzioni è però cambiato nel passaggio dal kernel 2.4 +al kernel 2.6, quest'ultimo infatti si è adeguato alle prescrizioni dello +standard POSIX.1-2001 e come da esso richiesto se \signal{SIGCHLD} viene +ignorato, o se si imposta il flag di \const{SA\_NOCLDSTOP} nella ricezione +dello stesso (si veda sez.~\ref{sec:sig_sigaction}) i processi figli che +terminano non diventano \textit{zombie} e sia \func{wait} che \func{waitpid} +si bloccano fintanto che tutti i processi figli non sono terminati, dopo di +che falliscono con un errore di \errcode{ENOCHLD}.\footnote{questo è anche il + motivo per cui le opzioni \const{WUNTRACED} e \const{WCONTINUED} sono + utilizzabili soltanto qualora non si sia impostato il flag di + \const{SA\_NOCLDSTOP} per il segnale \signal{SIGCHLD}.} + +Con i kernel della serie 2.4 e tutti i kernel delle serie precedenti entrambe +le funzioni di attesa ignorano questa prescrizione e si comportano sempre +nello stesso modo,\footnote{lo standard POSIX.1 originale infatti lascia + indefinito il comportamento di queste funzioni quando \signal{SIGCHLD} viene + ignorato.} indipendentemente dal fatto \signal{SIGCHLD} sia ignorato o meno: +attendono la terminazione di un processo figlio e ritornano il relativo +\ids{PID} e lo stato di terminazione nell'argomento \param{status}. + +In generale in un programma non si vuole essere forzati ad attendere la +conclusione di un processo figlio per proseguire l'esecuzione, specie se tutto +questo serve solo per leggerne lo stato di chiusura (ed evitare eventualmente +la presenza di \itindex{zombie} \textit{zombie}). Per questo la modalità più +comune di chiamare queste funzioni è quella di utilizzarle all'interno di un +\textit{signal handler} (vedremo un esempio di come gestire \signal{SIGCHLD} +con i segnali in sez.~\ref{sec:sig_example}). In questo caso infatti, dato che +il segnale è generato dalla terminazione di un figlio, avremo la certezza che +la chiamata a \func{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 \itindex{exit~status} 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 \itindex{core~dump} +\textit{core dump}, 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 + \footnotesize + \begin{tabular}[c]{|l|p{10cm}|} + \hline + \textbf{Macro} & \textbf{Descrizione}\\ + \hline + \hline + \macro{WIFEXITED}\texttt{(s)} & Condizione vera (valore non nullo) per + un processo figlio che sia terminato + normalmente. \\ + \macro{WEXITSTATUS}\texttt{(s)} & Restituisce gli otto bit meno + significativi dello stato di uscita del + processo (passato attraverso + \func{\_exit}, \func{exit} o come valore + di ritorno di \code{main}); può essere + valutata solo se \val{WIFEXITED} ha + restituito un valore non nullo.\\ + \macro{WIFSIGNALED}\texttt{(s)} & Condizione vera se il processo figlio è + terminato in maniera anomala a causa di + un segnale che non è stato catturato + (vedi sez.~\ref{sec:sig_notification}).\\ + \macro{WTERMSIG}\texttt{(s)} & Restituisce il numero del segnale che ha + causato la terminazione anomala del + processo; può essere valutata solo se + \val{WIFSIGNALED} ha restituito un + valore non nullo.\\ + \macro{WCOREDUMP}\texttt{(s)} & Vera se il processo terminato ha + generato un file di \itindex{core~dump} + \textit{core dump}; può essere valutata + solo se \val{WIFSIGNALED} ha restituito + un valore non nullo.\footnotemark \\ + \macro{WIFSTOPPED}\texttt{(s)} & Vera se il processo che ha causato il + ritorno di \func{waitpid} è bloccato; + l'uso è possibile solo con + \func{waitpid} avendo specificato + l'opzione \const{WUNTRACED}.\\ + \macro{WSTOPSIG}\texttt{(s)} & Restituisce il numero del segnale che ha + bloccato il processo; può essere + valutata solo se \val{WIFSTOPPED} ha + restituito un valore non nullo. \\ + \macro{WIFCONTINUED}\texttt{(s)}& Vera se il processo che ha causato il + ritorno è stato riavviato da un + \signal{SIGCONT} (disponibile solo a + partire dal kernel 2.6.10).\\ + \hline + \end{tabular} + \caption{Descrizione delle varie macro di preprocessore utilizzabili per + verificare lo stato di terminazione \var{s} di un processo.} + \label{tab:proc_status_macro} +\end{table} + +\footnotetext{questa macro non è definita dallo standard POSIX.1-2001, ma è + presente come estensione sia in Linux che in altri Unix, deve essere + pertanto utilizzata con attenzione (ad esempio è il caso di usarla in un + blocco \texttt{\#ifdef WCOREDUMP ... \#endif}.} + +Lo standard POSIX.1 definisce una serie di macro di preprocessore da usare per +analizzare lo stato di uscita. Esse sono definite sempre in +\file{} ed elencate in tab.~\ref{tab:proc_status_macro}. Si tenga +presente che queste macro prevedono che gli si passi come parametro la +variabile di tipo \ctyp{int} puntata dall'argomento \param{status} restituito +da \func{wait} o \func{waitpid}. + +Si tenga conto che nel caso di conclusione anomala il valore restituito da +\val{WTERMSIG} può essere confrontato con le costanti che identificano i +segnali definite in \headfile{signal.h} ed elencate in +tab.~\ref{tab:sig_signal_list}, e stampato usando le apposite funzioni +trattate in sez.~\ref{sec:sig_strsignal}. + +A partire dal kernel 2.6.9, sempre in conformità allo standard POSIX.1-2001, è +stata introdotta una nuova funzione di attesa che consente di avere un +controllo molto più preciso sui possibili cambiamenti di stato dei processi +figli e più dettagli sullo stato di uscita; la funzione di sistema è +\funcd{waitid} ed il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/wait.h} +\fdecl{int waitid(idtype\_t idtype, id\_t id, siginfo\_t *infop, int options)} +\fdesc{Attende il cambiamento di stato di un processo figlio.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{ECHILD}] il processo specificato da \param{pid} non esiste o + non è figlio del processo chiamante. + \item[\errcode{EINTR}] non è stata specificata l'opzione \const{WNOHANG} e + la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] si è specificato un valore non valido per + l'argomento \param{options}. + \end{errlist}} +\end{funcproto} + +La funzione prevede che si specifichi quali processi si intendono osservare +usando i due argomenti \param{idtype} ed \param{id}; il primo indica se ci si +vuole porre in attesa su un singolo processo, un gruppo di processi o un +processo qualsiasi, e deve essere specificato secondo uno dei valori di +tab.~\ref{tab:proc_waitid_idtype}; il secondo indica, a seconda del valore del +primo, quale processo o quale gruppo di processi selezionare. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Valore} & \textbf{Descrizione}\\ + \hline + \hline + \const{P\_PID} & Indica la richiesta di attendere per un processo figlio + il cui \ids{PID} corrisponda al valore dell'argomento + \param{id}.\\ + \const{P\_PGID}& Indica la richiesta di attendere per un processo figlio + appartenente al \textit{process group} (vedi + sez.~\ref{sec:sess_proc_group}) il cui \acr{pgid} + corrisponda al valore dell'argomento \param{id}.\\ + \const{P\_ALL} & Indica la richiesta di attendere per un processo figlio + generico, il valore dell'argomento \param{id} viene + ignorato.\\ + \hline + \end{tabular} + \caption{Costanti per i valori dell'argomento \param{idtype} della funzione + \func{waitid}.} + \label{tab:proc_waitid_idtype} +\end{table} + +Come per \func{waitpid} anche il comportamento di \func{waitid} viene +controllato dall'argomento \param{options}, da specificare come maschera +binaria dei valori riportati in tab.~\ref{tab:proc_waitid_options}. Benché +alcuni di questi siano identici come significato ed effetto ai precedenti di +tab.~\ref{tab:proc_waitpid_options}, ci sono delle differenze significative: +in questo caso si dovrà specificare esplicitamente l'attesa della terminazione +di un processo impostando l'opzione \const{WEXITED}, mentre il precedente +\const{WUNTRACED} è sostituito da \const{WSTOPPED}. Infine è stata aggiunta +l'opzione \const{WNOWAIT} che consente una lettura dello stato mantenendo il +processo in attesa di ricezione, così che una successiva chiamata possa di +nuovo riceverne lo stato. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Valore} & \textbf{Descrizione}\\ + \hline + \hline + \const{WEXITED} & Ritorna quando un processo figlio è terminato.\\ + \const{WNOHANG} & Ritorna immediatamente anche se non c'è niente da + notificare.\\ + \const{WSTOPPED} & Ritorna quando un processo figlio è stato fermato.\\ + \const{WCONTINUED}& Ritorna quando un processo figlio che era stato + fermato ha ripreso l'esecuzione.\\ + \const{WNOWAIT} & Lascia il processo ancora in attesa di ricezione, così + che una successiva chiamata possa di nuovo riceverne + lo stato.\\ + \hline + \end{tabular} + \caption{Costanti che identificano i bit dell'argomento \param{options} + della funzione \func{waitid}.} + \label{tab:proc_waitid_options} +\end{table} + +La funzione \func{waitid} restituisce un valore nullo in caso di successo, e +$-1$ in caso di errore; viene restituito un valore nullo anche se è stata +specificata l'opzione \const{WNOHANG} e la funzione è ritornata immediatamente +senza che nessun figlio sia terminato. Pertanto per verificare il motivo del +ritorno della funzione occorre analizzare le informazioni che essa +restituisce; queste, al contrario delle precedenti \func{wait} e +\func{waitpid} che usavano un semplice valore numerico, sono ritornate in una +struttura di tipo \struct{siginfo\_t} (vedi fig.~\ref{fig:sig_siginfo_t}) +all'indirizzo puntato dall'argomento \param{infop}. + +Tratteremo nei dettagli la struttura \struct{siginfo\_t} ed il significato dei +suoi vari campi in sez.~\ref{sec:sig_sigaction}, per quanto ci interessa qui +basta dire che al ritorno di \func{waitid} verranno avvalorati i seguenti +campi: +\begin{basedescript}{\desclabelwidth{2.0cm}} +\item[\var{si\_pid}] con il \ids{PID} del figlio. +\item[\var{si\_uid}] con l'\textsl{user-ID reale} (vedi + sez.~\ref{sec:proc_perms}) del figlio. +\item[\var{si\_signo}] con \signal{SIGCHLD}. +\item[\var{si\_status}] con lo stato di uscita del figlio o con il segnale che + lo ha terminato, fermato o riavviato. +\item[\var{si\_code}] con uno fra \const{CLD\_EXITED}, \const{CLD\_KILLED}, + \const{CLD\_STOPPED}, \const{CLD\_CONTINUED}, \const{CLD\_TRAPPED} e + \const{CLD\_DUMPED} a indicare la ragione del ritorno della funzione, il cui + significato è, nell'ordine: uscita normale, terminazione da segnale, + processo fermato, processo riavviato, processo terminato in \textit{core + dump}. +\end{basedescript} + +Infine Linux, seguendo un'estensione di BSD, supporta altre due funzioni per +la lettura dello stato di terminazione di un processo, analoghe alle +precedenti ma che prevedono un ulteriore argomento attraverso il quale il +kernel può restituire al padre informazioni sulle risorse (vedi +sez.~\ref{sec:sys_res_limits}) usate dal processo terminato e dai vari figli. +Le due funzioni di sistema sono \funcd{wait3} e \funcd{wait4}, che diventano +accessibili definendo la macro \macro{\_USE\_BSD}, i loro prototipi sono: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/times.h} +\fhead{sys/resource.h} +\fhead{sys/wait.h} +\fdecl{int wait3(int *status, int options, struct rusage *rusage)} +\fdecl{int wait4(pid\_t pid, int *status, int options, struct rusage *rusage)} +\fdesc{Attende il cambiamento di stato di un processo figlio, riportando l'uso + delle risorse.} +} +{La funzione ha gli stessi valori di ritorno e codici di errore di + \func{waitpid}. } +\end{funcproto} + +La funzione \func{wait4} è identica \func{waitpid} sia nel comportamento che +per i valori dei primi tre argomenti, ma in più restituisce nell'argomento +aggiuntivo \param{rusage} un sommario delle risorse usate dal processo. Questo +argomento è una struttura di tipo \struct{rusage} definita in +\headfile{sys/resource.h}, che viene utilizzata anche dalla funzione +\func{getrusage} per ottenere le risorse di sistema usate da un processo. La +sua definizione è riportata in fig.~\ref{fig:sys_rusage_struct} e ne +tratteremo in dettaglio il significato sez.~\ref{sec:sys_resource_use}. La +funzione \func{wait3} è semplicemente un caso particolare di (e con Linux +viene realizzata con la stessa \textit{system call}), ed è equivalente a +chiamare \code{wait4(-1, \&status, opt, rusage)}, per questo motivo è ormai +deprecata in favore di \func{wait4}. + + + +\subsection{La famiglia delle funzioni \func{exec} per l'esecuzione dei + programmi} \label{sec:proc_exec} +Abbiamo già detto che una delle modalità principali con cui si utilizzano i +processi in Unix è quella di usarli per lanciare nuovi programmi: questo viene +fatto attraverso una delle funzioni della famiglia \func{exec}. Quando un +processo chiama una di queste funzioni esso viene completamente sostituito dal +nuovo programma, il \ids{PID} del processo non cambia, dato che non viene +creato un nuovo processo, la funzione semplicemente rimpiazza lo +\itindex{stack} \textit{stack}, i \index{segmento!dati} dati ed il +\index{segmento!testo} testo del processo corrente con un nuovo programma +letto da disco, eseguendo il \itindex{link-loader} \textit{link-loader} con +gli effetti illustrati in sez.~\ref{sec:proc_main}. + +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 +tutte varianti che consentono di invocare in modi diversi, semplificando il +passaggio degli argomenti, la \textit{system call} \funcd{execve}, il cui +prototipo è: + +\begin{funcproto}{ +\fhead{unistd.h} +\fdecl{int execve(const char *filename, char *const argv[], char *const envp[])} +\fdesc{Esegue un programma.} +} +{La funzione ritorna solo in caso di errore, restituendo $-1$, nel qual + caso \var{errno} assumerà uno dei valori: +\begin{errlist} + \item[\errcode{EACCES}] il file o l'interprete non file ordinari, o non sono + 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{EINVAL}] l'eseguibile ELF ha più di un segmento + \const{PF\_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{EPERM}] il file ha i bit \itindex{suid~bit} \acr{suid} o + \itindex{sgid~bit} \acr{sgid} e l'utente non è root, ed il processo viene + tracciato, oppure il filesystem è montato con l'opzione \cmd{nosuid}. + \item[\errcode{ETXTBSY}] l'eseguibile è aperto in scrittura da uno o più + processi. + \item[\errcode{E2BIG}] la lista degli argomenti è troppo grande. + \end{errlist} + ed inoltre \errval{EFAULT}, \errval{EIO}, \errval{EISDIR}, \errval{ELOOP}, + \errval{EMFILE}, \errval{ENAMETOOLONG}, \errval{ENFILE}, \errval{ENOMEM}, + \errval{ENOTDIR} nel loro significato generico. } +\end{funcproto} + +La funzione \func{execve} esegue il programma o lo script indicato dal +\textit{pathname} \param{filename}, passandogli la lista di argomenti indicata +da \param{argv} e come ambiente la lista di stringhe indicata +da \param{envp}. Entrambe le liste devono essere terminate da un puntatore +nullo. I vettori degli argomenti e dell'ambiente possono essere acceduti dal +nuovo programma quando la sua funzione \code{main} è dichiarata nella forma +\code{main(int argc, char *argv[], char *envp[])}. Si tenga presente per il +passaggio degli argomenti e dell'ambiente esistono comunque dei limiti, su cui +torneremo in sez.~\ref{sec:sys_res_limits}). +% TODO aggiungere la parte sul numero massimo di argomenti, da man execve + +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 +\signal{SIGTRAP}. + +Le altre funzioni della famiglia (\funcd{execl}, \funcd{execv}, +\funcd{execle}, \funcd{execlp}, \funcd{execvp}) servono per fornire all'utente +una serie di possibili diverse interfacce nelle modalità di passaggio degli +argomenti all'esecuzione del nuovo programma. I loro prototipi sono: + +\begin{funcproto}{ +\fhead{unistd.h} +\fdecl{int execl(const char *path, const char *arg, ...)} +\fdecl{int execv(const char *path, char *const argv[])} +\fdecl{int execle(const char *path, const char *arg, ..., char * const envp[])} +\fdecl{int execlp(const char *file, const char *arg, ...)} +\fdecl{int execvp(const char *file, char *const argv[])} +\fdesc{Eseguono un programma.} +} +{Le funzioni ritornano solo in caso di errore, restituendo $-1$, i codici di + errore sono gli stessi di \func{execve}. +} +\end{funcproto} + +Tutte le funzioni mettono in esecuzione nel processo corrente il programma +indicati nel primo argomento. Gli argomenti successivi consentono di +specificare gli argomenti e l'ambiente che saranno ricevuti dal nuovo +processo. Per capire meglio le differenze fra le funzioni della famiglia si può +fare riferimento allo specchietto riportato in +tab.~\ref{tab:proc_exec_scheme}. La relazione fra le funzioni è invece +illustrata in fig.~\ref{fig:proc_exec_relat}. + +\begin{table}[!htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|c|c|c||c|c|c|} + \hline + \multicolumn{1}{|c|}{\textbf{Caratteristiche}} & + \multicolumn{6}{|c|}{\textbf{Funzioni}} \\ + \hline + &\func{execl}\texttt{ }&\func{execlp}&\func{execle} + &\func{execv}\texttt{ }& \func{execvp}& \func{execve} \\ + \hline + \hline + argomenti a lista &$\bullet$&$\bullet$&$\bullet$&&& \\ + argomenti a vettore &&&&$\bullet$&$\bullet$&$\bullet$\\ + \hline + filename completo &$\bullet$&&$\bullet$&$\bullet$&&$\bullet$\\ + ricerca su \var{PATH} &&$\bullet$&&&$\bullet$& \\ + \hline + ambiente a vettore &&&$\bullet$&&&$\bullet$ \\ + uso di \var{environ} &$\bullet$&$\bullet$&&$\bullet$&$\bullet$& \\ + \hline + \end{tabular} + \caption{Confronto delle caratteristiche delle varie funzioni della + famiglia \func{exec}.} + \label{tab:proc_exec_scheme} +\end{table} + +La prima differenza fra le funzioni riguarda le modalità di passaggio dei +valori che poi andranno a costituire gli argomenti a linea di comando (cioè i +valori di \param{argv} e \param{argc} visti dalla funzione \code{main} del +programma chiamato). Queste modalità sono due e sono riassunte dagli mnemonici +``\texttt{v}'' e ``\texttt{l}'' che stanno rispettivamente per \textit{vector} +e \textit{list}. + +Nel primo caso gli argomenti sono passati tramite il vettore di puntatori +\var{argv[]} a stringhe terminate con zero che costituiranno gli argomenti a +riga di comando, questo vettore \emph{deve} essere terminato da un puntatore +nullo. Nel secondo caso le stringhe degli argomenti sono passate alla funzione +come lista di puntatori, nella forma: +\includecodesnip{listati/char_list.c} +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=10cm]{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 +specificare il comando da eseguire; quando l'argomento \param{file} non +contiene una ``\texttt{/}'' esso viene considerato come un nome di programma, +e viene eseguita automaticamente una ricerca fra i file presenti nella lista +di directory specificate dalla variabile di ambiente \envvar{PATH}. Il file +che viene posto in esecuzione è il primo che viene trovato. Se si ha un errore +relativo a permessi di accesso insufficienti (cioè l'esecuzione della +sottostante \func{execve} ritorna un \errcode{EACCES}), la ricerca viene +proseguita nelle eventuali ulteriori directory indicate in \envvar{PATH}; solo +se non viene trovato nessun altro file viene finalmente restituito +\errcode{EACCES}. Le altre quattro funzioni si limitano invece a cercare di +eseguire il file indicato dall'argomento \param{path}, che viene interpretato +come il \textit{pathname} del programma. + +La terza differenza è come viene passata la lista delle variabili di ambiente. +Con lo mnemonico ``\texttt{e}'' vengono indicate quelle funzioni che +necessitano di un vettore di parametri \var{envp[]} analogo a quello usato per +gli argomenti a riga di comando (terminato quindi da un \val{NULL}), le altre +usano il valore della variabile \var{environ} (vedi +sez.~\ref{sec:proc_environ}) del processo di partenza per costruire +l'ambiente. + +Oltre a mantenere lo stesso \ids{PID}, il nuovo programma fatto partire da una +delle funzioni della famiglia \func{exec} mantiene la gran parte delle +proprietà del processo chiamante; una lista delle più significative è la +seguente: +\begin{itemize*} +\item il \textit{process id} (\ids{PID}) ed il \textit{parent process id} + (\ids{PPID}); +\item l'\textsl{user-ID reale}, il \textit{group-ID reale} ed i + \textsl{group-ID supplementari} (vedi sez.~\ref{sec:proc_access_id}); +\item la directory radice e la \index{directory~di~lavoro} directory di lavoro + corrente (vedi sez.~\ref{sec:file_work_dir}); +\item la maschera di creazione dei file \itindex{umask} (\textit{umask}, vedi + sez.~\ref{sec:file_perm_management}) ed i \textit{lock} sui file (vedi + sez.~\ref{sec:file_locking}); +\item il valori di \textit{nice}, le priorità real-time e le affinità di + processore (vedi sez.~\ref{sec:proc_sched_stand}; + sez.~\ref{sec:proc_real_time} e sez.~\ref{sec:proc_sched_multiprocess}); +\item il \textit{session ID} (\acr{sid}) ed il \itindex{process~group} + \textit{process group ID} (\acr{pgid}), vedi sez.~\ref{sec:sess_proc_group}; +\item il terminale di controllo (vedi sez.~\ref{sec:sess_ctrl_term}); +\item il tempo restante ad un allarme (vedi sez.~\ref{sec:sig_alarm_abort}); +\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}). +\end{itemize*} + +Una serie di proprietà del processo originale, che non avrebbe senso mantenere +in un programma che esegue un codice completamente diverso in uno spazio di +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 + vengono chiusi; +\item le mappature dei file in memoria (vedi sez.~\ref{sec:file_memory_map}); +\item i segmenti di memoria condivisa SysV (vedi sez.~\ref{sec:ipc_sysv_shm}) + e POSIX (vedi sez.~\ref{sec:ipc_posix_shm}); +\item i \textit{memory lock} (vedi sez.~\ref{sec:proc_mem_lock}); +\item le funzioni registrate all'uscita (vedi sez.~\ref{sec:proc_atexit}); +\item i semafori e le code di messaggi POSIX (vedi + sez.~\ref{sec:ipc_posix_sem} e sez.~\ref{sec:ipc_posix_mq}); +\item i timer POSIX (vedi sez.~\ref{sec:sig_timer_adv}). +\end{itemize*} + +Inoltre i segnali che sono stati impostati per essere ignorati nel processo +chiamante mantengono la stessa impostazione pure nel nuovo programma, ma tutti +gli altri segnali, ed in particolare quelli per i quali è stato installato un +gestore vengono impostati alla loro azione predefinita (vedi +sez.~\ref{sec:sig_gen_beha}). Un caso speciale è il segnale \signal{SIGCHLD} +che, quando impostato a \const{SIG\_IGN}, potrebbe anche essere reimpostato a +\const{SIG\_DFL}. Lo standard POSIX.1-2001 prevede che questo comportamento +sia deciso dalla singola implementazione, quella di Linux è di non modificare +l'impostazione precedente. + +Oltre alle precedenti, che sono completamente generali e disponibili anche su +altri sistemi unix-like, esistono altre proprietà dei processi, attinenti alle +caratteristiche specifiche di Linux, che non vengono preservate +nell'esecuzione della funzione \func{exec}, queste sono: +\begin{itemize*} +\item le operazioni di I/O asincrono (vedi sez.~\ref{sec:file_asyncronous_io}) + pendenti vengono cancellate; +\item le \itindex{capabilities} \textit{capabilities} vengono modificate come + illustrato in sez.~\ref{sec:proc_capabilities}; +\item tutti i \itindex{thread} \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 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 + \itindex{suid~bit} \acr{suid} o \itindex{sgid~bit} \acr{sgid} (vedi + sez.~\ref{sec:proc_access_id}); +\item il flag \const{PR\_SET\_KEEPCAPS} di \func{prctl} (vedi + sez.~\ref{sec:process_prctl}) viene cancellato; +\item il nome del processo viene impostato al nome del file contenente il + programma messo in esecuzione; +\item il segnale di terminazione viene reimpostato a \signal{SIGCHLD}; +\item l'ambiente viene reinizializzato impostando le variabili attinenti alla + localizzazione al valore di default POSIX. +\end{itemize*} + +La gestione dei file aperti nel passaggio al nuovo programma lanciato con +\func{exec} dipende dal valore che ha il flag di \itindex{close-on-exec} +\textit{close-on-exec} (vedi sez.~\ref{sec:file_fcntl_ioctl}) per ciascun +\textit{file descriptor}. I file per cui è impostato vengono chiusi, tutti gli +altri file restano aperti. Questo significa che il comportamento predefinito è +che i file restano aperti attraverso una \func{exec}, a meno di una chiamata +esplicita a \func{fcntl} che imposti il suddetto flag. Per le directory, lo +standard POSIX.1 richiede che esse vengano chiuse attraverso una \func{exec}, +in genere questo è fatto dalla funzione \func{opendir} (vedi +sez.~\ref{sec:file_dir_read}) che effettua da sola l'impostazione del flag di +\itindex{close-on-exec} \textit{close-on-exec} sulle directory che apre, in +maniera trasparente all'utente. + +Il comportamento della funzione in relazione agli identificatori relativi al +controllo di accesso verrà trattato in dettaglio in sez.~\ref{sec:proc_perms}, +qui è sufficiente anticipare (si faccia riferimento a +sez.~\ref{sec:proc_access_id} per la definizione di questi identificatori) +come l'\textsl{user-ID reale} ed il \textsl{group-ID reale} restano sempre gli +stessi, mentre l'\textsl{user-ID salvato} ed il \textsl{group-ID salvato} +vengono impostati rispettivamente all'\textsl{user-ID effettivo} ed il +\textsl{group-ID effettivo}. Questi ultimi normalmente non vengono modificati, +a meno che il file di cui viene chiesta l'esecuzione non abbia o il +\itindex{suid~bit} \acr{suid} bit o lo \itindex{sgid~bit} \acr{sgid} bit +impostato, in questo caso l'\textsl{user-ID effettivo} ed il \textsl{group-ID + effettivo} vengono impostati rispettivamente all'utente o al gruppo cui il +file appartiene. + +Se il file da eseguire è in formato \emph{a.out} e necessita di librerie +condivise, viene lanciato il \textit{linker} dinamico \cmd{/lib/ld.so} prima +del programma per caricare le librerie necessarie ed effettuare il link +dell'eseguibile.\footnote{il formato è ormai in completo disuso, per cui è + molto probabile che non il relativo supporto non sia disponibile.} Se il +programma è in formato ELF per caricare le librerie dinamiche viene usato +l'interprete indicato nel segmento \const{PT\_INTERP} previsto dal formato +stesso, in genere questo è \sysfile{/lib/ld-linux.so.1} per programmi +collegati con la \acr{libc5}, e \sysfile{/lib/ld-linux.so.2} per programmi +collegati con la \acr{glibc}. + +Infine nel caso il programma che si vuole eseguire sia uno script e non un +binario, questo deve essere un file di testo che deve iniziare con una linea +nella forma: +\begin{Example} +#!/path/to/interpreter [argomenti] +\end{Example} +dove l'interprete indicato deve essere un eseguibile binario e non un altro +script, che verrà chiamato come se si fosse eseguito il comando +\cmd{interpreter [argomenti] filename}.\footnote{si tenga presente che con + Linux quanto viene scritto come \texttt{argomenti} viene passato + all'interprete come un unico argomento con una unica stringa di lunghezza + massima di 127 caratteri e se questa dimensione viene ecceduta la stringa + viene troncata; altri Unix hanno dimensioni massime diverse, e diversi + comportamenti, ad esempio FreeBSD esegue la scansione della riga e la divide + nei vari argomenti e se è troppo lunga restituisce un errore di + \const{ENAMETOOLONG}, una comparazione dei vari comportamenti si trova su + \url{http://www.in-ulm.de/~mascheck/various/shebang/}.} + +Con la famiglia delle \func{exec} si chiude il novero delle funzioni su cui è +basata la gestione tradizionale dei processi in Unix: con \func{fork} si crea +un nuovo processo, con \func{exec} si lancia un nuovo programma, con +\func{exit} e \func{wait} si effettua e verifica la conclusione dei +processi. Tutte le altre funzioni sono ausiliarie e servono per la lettura e +l'impostazione dei vari parametri connessi ai processi. \section{Il controllo di accesso} \label{sec:proc_perms} +In questa sezione esamineremo le problematiche relative al controllo di +accesso dal punto di vista dei processi; vedremo quali sono gli identificatori +usati, come questi possono essere modificati nella creazione e nel lancio di +nuovi processi, le varie funzioni per la loro manipolazione diretta e tutte le +problematiche connesse ad una gestione accorta dei privilegi. +\subsection{Gli identificatori del controllo di accesso} +\label{sec:proc_access_id} +Come accennato in sez.~\ref{sec:intro_multiuser} il modello base\footnote{in + realtà già esistono estensioni di questo modello base, che lo rendono più + flessibile e controllabile, come le \itindex{capabilities} + \textit{capabilities} illustrate in sez.~\ref{sec:proc_capabilities}, le ACL + per i file (vedi sez.~\ref{sec:file_ACL}) o il + \itindex{Mandatory~Access~Control~(MAC)} \textit{Mandatory Access Control} + di \index{SELinux} SELinux; inoltre basandosi sul lavoro effettuato con + SELinux, a partire dal kernel 2.5.x, è iniziato lo sviluppo di una + infrastruttura di sicurezza, i \itindex{Linux~Security~Modules} + \textit{Linux Security Modules}, o LSM, in grado di fornire diversi agganci + a livello del kernel per modularizzare tutti i possibili controlli di + accesso, cosa che ha permesso di realizzare diverse alternative a + \index{SELinux} SELinux.} di sicurezza di un sistema unix-like è fondato sui +concetti di utente e gruppo, e sulla separazione fra l'amministratore +(\textsl{root}, detto spesso anche \textit{superuser}) che non è sottoposto a +restrizioni, ed il resto degli utenti, per i quali invece vengono effettuati i +vari controlli di accesso. -Come accennato in \secref{sec:file_perm_overview} ciascun processo porta con -se un gruppo di identificatori (riportati in \ntab) utilizzati per i controllo -degli accessi, +Abbiamo già accennato come il sistema associ ad ogni utente e gruppo due +identificatori univoci, lo \itindex{User~ID~(PID)} \textsl{User-ID} +(abbreviato in \ids{UID}) ed il \itindex{Group~ID~(PID)} \textsl{Group-ID} +(abbreviato in \ids{GID}). Questi servono al kernel per identificare uno +specifico utente o un gruppo di utenti, per poi poter controllare che essi +siano autorizzati a compiere le operazioni richieste. Ad esempio in +sez.~\ref{sec:file_access_control} vedremo come ad ogni file vengano associati +un utente ed un gruppo (i suoi \textsl{proprietari}, indicati appunto tramite +un \ids{UID} ed un \ids{GID}) che vengono controllati dal kernel nella +gestione dei permessi di accesso. +Dato che tutte le operazioni del sistema vengono compiute dai processi, è +evidente che per poter implementare un controllo sulle operazioni occorre +anche poter identificare chi è che ha lanciato un certo programma, e pertanto +anche a ciascun processo dovrà essere associato un utente e un gruppo. + +Un semplice controllo di una corrispondenza fra identificativi non garantisce +però sufficiente flessibilità per tutti quei casi in cui è necessario poter +disporre di privilegi diversi, o dover impersonare un altro utente per un +limitato insieme di operazioni. Per questo motivo in generale tutti i sistemi +unix-like prevedono che i processi abbiano almeno due gruppi di +identificatori, chiamati rispettivamente \textit{real} ed \textit{effective} +(cioè \textsl{reali} ed \textsl{effettivi}). Nel caso di Linux si aggiungono +poi altri due gruppi, il \textit{saved} (\textsl{salvati}) ed il +\textit{filesystem} (\textsl{di filesystem}), secondo la situazione illustrata +in tab.~\ref{tab:proc_uid_gid}. \begin{table}[htb] + \footnotesize \centering - \begin{tabular}[c]{|c|l|l|} + \begin{tabular}[c]{|c|c|l|p{7cm}|} + \hline + \textbf{Suffisso} & \textbf{Gruppo} & \textbf{Denominazione} + & \textbf{Significato} \\ + \hline + \hline + \texttt{uid} & \textit{real} & \textsl{user-ID reale} + & Indica l'utente che ha lanciato il programma.\\ + \texttt{gid} & '' &\textsl{group-ID reale} + & Indica il gruppo principale dell'utente che ha lanciato + il programma.\\ \hline - Sigla & Significato & Utilizzo \\ + \texttt{euid}& \textit{effective} &\textsl{user-ID effettivo} + & Indica l'utente usato nel controllo di accesso.\\ + \texttt{egid}& '' & \textsl{group-ID effettivo} + & Indica il gruppo usato nel controllo di accesso.\\ + -- & -- & \textsl{group-ID supplementari} + & Indicano gli ulteriori gruppi cui l'utente appartiene.\\ \hline + -- & \textit{saved} & \textsl{user-ID salvato} + & Mantiene una copia dell'\acr{euid} iniziale.\\ + -- & '' & \textsl{group-ID salvato} + & Mantiene una copia dell'\acr{egid} iniziale.\\ \hline - \acr{ruid} & \textit{real user id} & indica l'utente reale \\ - \acr{rgid} & \textit{real group id} & indica il gruppo reale \\ - \acr{euid} & \textit{effective user id} & indica l'utente reale \\ - \acr{egid} & \textit{effective group id} & indica il gruppo reale \\ - & \textit{supplementaru group id} & indica il gruppo \\ - \acr{suid} & \textit{saved user id} & indica l'utente reale \\ - \acr{sgid} & \textit{daved group id} & indica il gruppo reale \\ - \acr{fsuid} & \textit{real user id} & indica l'utente reale \\ - \acr{fsgid} & \textit{real group id} & indica il gruppo reale \\ + \texttt{fsuid}& \textit{filesystem} &\textsl{user-ID di filesystem} + & Indica l'utente effettivo per l'accesso al filesystem. \\ + \texttt{fsgid}& '' & \textsl{group-ID di filesystem} + & Indica il gruppo effettivo per l'accesso al filesystem.\\ \hline \end{tabular} - \caption{Identificatori di utente e gruppo associati a ciascun processo.} + \caption{Identificatori di utente e gruppo associati a ciascun processo con + indicazione dei suffissi usati dalle varie funzioni di manipolazione.} \label{tab:proc_uid_gid} \end{table} +Al primo gruppo appartengono l'\ids{UID} \textsl{reale} ed il \ids{GID} +\textsl{reale}: questi vengono impostati al login ai valori corrispondenti +all'utente con cui si accede al sistema (e relativo gruppo principale). +Servono per l'identificazione dell'utente e normalmente non vengono mai +cambiati. In realtà vedremo (in sez.~\ref{sec:proc_setuid}) che è possibile +modificarli, ma solo ad un processo che abbia i privilegi di amministratore; +questa possibilità è usata proprio dal programma \cmd{login} che, una volta +completata la procedura di autenticazione, lancia una shell per la quale +imposta questi identificatori ai valori corrispondenti all'utente che entra +nel sistema. -\subsection{Le funzioni \texttt{setuid} e \texttt{setgid}} +Al secondo gruppo appartengono l'\ids{UID} \textsl{effettivo} e il \ids{GID} +\textsl{effettivo}, a cui si aggiungono gli eventuali \ids{GID} +\textsl{supplementari} dei gruppi dei quali l'utente fa parte. Questi sono +invece gli identificatori usati nelle verifiche dei permessi del processo e +per il controllo di accesso ai file (argomento affrontato in dettaglio in +sez.~\ref{sec:file_perm_overview}). + +Questi identificatori normalmente sono identici ai corrispondenti del gruppo +\textit{real} tranne nel caso in cui, come accennato in +sez.~\ref{sec:proc_exec}, il programma che si è posto in esecuzione abbia i +bit \itindex{suid~bit} \acr{suid} o \itindex{sgid~bit} \acr{sgid} impostati +(il significato di questi bit è affrontato in dettaglio in +sez.~\ref{sec:file_special_perm}). In questo caso essi saranno impostati +all'utente e al gruppo proprietari del file. Questo consente, per programmi in +cui ci sia questa necessità, di dare a qualunque utente i privilegi o i +permessi di un altro, compreso l'amministratore. + +Come nel caso del \ids{PID} e del \ids{PPID}, anche tutti questi +identificatori possono essere ottenuti da un programma attraverso altrettante +funzioni di sistema dedicate alla loro lettura, queste sono \funcd{getuid}, +\funcd{geteuid}, \funcd{getgid} e \funcd{getegid}, ed i loro prototipi sono: + +\begin{funcproto}{ +\fhead{unistd.h} +\fhead{sys/types.h} +\fdecl{uid\_t getuid(void)} +\fdesc{Legge l'\ids{UID} reale del processo corrente.} +\fdecl{uid\_t geteuid(void)} +\fdesc{Legge l'\ids{UID} effettivo del processo corrente.} +\fdecl{gid\_t getgid(void)} +\fdesc{Legge il \ids{GID} reale del processo corrente.} +\fdecl{gid\_t getegid(void)} +\fdesc{Legge il \ids{GID} effettivo del processo corrente.} +} +{Le funzioni ritornano i rispettivi identificativi del processo corrente, e + non sono previste condizioni di errore.} +\end{funcproto} + +In generale l'uso di privilegi superiori, ottenibile con un \ids{UID} +\textsl{effettivo} diverso da quello reale, deve essere limitato il più +possibile, per evitare abusi e problemi di sicurezza, per questo occorre anche +un meccanismo che consenta ad un programma di rilasciare gli eventuali +maggiori privilegi necessari, una volta che si siano effettuate le operazioni +per i quali erano richiesti, e a poterli eventualmente recuperare in caso +servano di nuovo. + +Questo in Linux viene fatto usando altri due gruppi di identificatori, il +\textit{saved} ed il \textit{filesystem}. Il primo gruppo è lo stesso usato in +SVr4, e previsto dallo standard POSIX quando è definita la costante +\macro{\_POSIX\_SAVED\_IDS},\footnote{in caso si abbia a cuore la portabilità + del programma su altri Unix è buona norma controllare sempre la + disponibilità di queste funzioni controllando se questa costante è + definita.} il secondo gruppo è specifico di Linux e viene usato per +migliorare la sicurezza con NFS (il \textit{Network File System}, protocollo +che consente di accedere ai file via rete). + +L'\ids{UID} \textsl{salvato} ed il \ids{GID} \textsl{salvato} sono copie +dell'\ids{UID} \textsl{effettivo} e del \ids{GID} \textsl{effettivo} del +processo padre, e vengono impostati dalla funzione \func{exec} all'avvio del +processo, come copie dell'\ids{UID} \textsl{effettivo} e del \ids{GID} +\textsl{effettivo} dopo che questi sono stati impostati tenendo conto di +eventuali \itindex{suid~bit} \acr{suid} o \itindex{sgid~bit} \acr{sgid}. Essi +quindi consentono di tenere traccia di quale fossero utente e gruppo effettivi +all'inizio dell'esecuzione di un nuovo programma. + +L'\ids{UID} \textsl{di filesystem} e il \ids{GID} \textsl{di filesystem} sono +un'estensione introdotta in Linux per rendere più sicuro l'uso di NFS +(torneremo sull'argomento in sez.~\ref{sec:proc_setuid}). Essi sono una +replica dei corrispondenti identificatori del gruppo \textit{effective}, ai +quali si sostituiscono per tutte le operazioni di verifica dei permessi +relativi ai file (trattate in sez.~\ref{sec:file_perm_overview}). Ogni +cambiamento effettuato sugli identificatori effettivi viene automaticamente +riportato su di essi, per cui in condizioni normali si può tranquillamente +ignorarne l'esistenza, in quanto saranno del tutto equivalenti ai precedenti. + + +\subsection{Le funzioni di gestione degli identificatori dei processi} \label{sec:proc_setuid} +Le funzioni di sistema più comuni che vengono usate per cambiare identità +(cioè utente e gruppo di appartenenza) ad un processo, e che come accennato in +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}{ +\fhead{unistd.h} +\fhead{sys/types.h} +\fdecl{int setuid(uid\_t uid)} +\fdesc{Imposta l'\ids{UID} del processo corrente.} +\fdecl{int setgid(gid\_t gid)} +\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}. +} +\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} +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}. + +Come accennato l'uso principale di queste funzioni è quello di poter +consentire ad un programma con i bit \itindex{suid~bit} \acr{suid} o +\itindex{sgid~bit} \acr{sgid} impostati (vedi +sez.~\ref{sec:file_special_perm}) di riportare l'\ids{UID} effettivo a quello +dell'utente che ha lanciato il programma, effettuare il lavoro che non +necessita di privilegi aggiuntivi, ed eventualmente tornare indietro. + +Come esempio per chiarire l'uso di queste funzioni prendiamo quello con cui +viene gestito l'accesso al file \sysfile{/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 +\sysfile{/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: +\begin{eqnarray*} + \label{eq:1} + \textsl{group-ID reale} &=& \textrm{\ids{GID} (del chiamante)} \\ + \textsl{group-ID effettivo} &=& \textrm{\acr{utmp}} \\ + \textsl{group-ID salvato} &=& \textrm{\acr{utmp}} +\end{eqnarray*} +in questo modo, dato che il \textsl{group-ID effettivo} è quello giusto, il +programma può accedere a \sysfile{/var/run/utmp} in scrittura ed aggiornarlo. +A questo punto il programma può eseguire una \code{setgid(getgid())} per +impostare il \textsl{group-ID effettivo} a quello dell'utente (e dato che il +\textsl{group-ID reale} corrisponde la funzione avrà successo), in questo modo +non sarà possibile lanciare dal terminale programmi che modificano detto file, +in tal caso infatti la situazione degli identificatori sarebbe: +\begin{eqnarray*} + \label{eq:2} + \textsl{group-ID reale} &=& \textrm{\ids{GID} (invariato)} \\ + \textsl{group-ID effettivo} &=& \textrm{\ids{GID}} \\ + \textsl{group-ID salvato} &=& \textrm{\acr{utmp} (invariato)} +\end{eqnarray*} +e ogni processo lanciato dal terminale avrebbe comunque \ids{GID} come +\textsl{group-ID effettivo}. All'uscita dal terminale, per poter di nuovo +aggiornare lo stato di \sysfile{/var/run/utmp} il programma eseguirà una +\code{setgid(utmp)} (dove \var{utmp} è il valore numerico associato al gruppo +\acr{utmp}, ottenuto ad esempio con una precedente \func{getegid}), dato che +in questo caso il valore richiesto corrisponde al \textsl{group-ID salvato} la +funzione avrà successo e riporterà la situazione a: +\begin{eqnarray*} + \label{eq:3} + \textsl{group-ID reale} &=& \textrm{\ids{GID} (invariato)} \\ + \textsl{group-ID effettivo} &=& \textrm{\acr{utmp}} \\ + \textsl{group-ID salvato} &=& \textrm{\acr{utmp} (invariato)} +\end{eqnarray*} +consentendo l'accesso a \sysfile{/var/run/utmp}. + +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. + +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 +del gruppo \textit{saved}, le usa per poter scambiare fra di loro +\textit{effective} e \textit{real}; i rispettivi prototipi sono: + +\begin{funcproto}{ +\fhead{unistd.h} +\fhead{sys/types.h} +\fdecl{int setreuid(uid\_t ruid, uid\_t euid)} +\fdesc{Imposta \ids{UID} reale e \ids{UID} effettivo del processo corrente.} +\fdecl{int setregid(gid\_t rgid, gid\_t egid)} +\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}. +} +\end{funcproto} + +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. + +Con queste funzioni si possono scambiare fra loro gli \ids{UID} reale ed +effettivo, e pertanto è possibile implementare un comportamento simile a +quello visto in precedenza per \func{setgid}, cedendo i privilegi con un primo +scambio, e recuperandoli, una volta eseguito il lavoro non privilegiato, con +un secondo scambio. + +In questo caso però occorre porre molta attenzione quando si creano nuovi +processi nella fase intermedia in cui si sono scambiati gli identificatori, in +questo caso infatti essi avranno un \ids{UID} reale privilegiato, che dovrà +essere esplicitamente eliminato prima di porre in esecuzione un nuovo +programma, occorrerà cioè eseguire un'altra chiamata dopo la \func{fork} e +prima della \func{exec} per uniformare l'\ids{UID} reale a quello effettivo, +perché in caso contrario il nuovo programma potrebbe a sua volta effettuare +uno scambio e riottenere dei privilegi non previsti. + +Lo stesso problema di propagazione dei privilegi ad eventuali processi figli +si pone anche per l'\ids{UID} salvato. Ma la funzione \func{setreuid} deriva +da un'implementazione di sistema che non ne prevede la presenza, e quindi non +è possibile usarla per correggere la situazione come nel caso precedente. Per +questo motivo in Linux tutte le volte che si imposta un qualunque valore +diverso da quello dall'\ids{UID} reale corrente, l'\ids{UID} salvato viene +automaticamente uniformato al valore dell'\ids{UID} effettivo. + +Altre due funzioni di sistema, \funcd{seteuid} e \funcd{setegid}, sono +un'estensione dello standard POSIX.1, ma sono comunque supportate dalla +maggior parte degli Unix, esse vengono usate per cambiare gli identificatori +del gruppo \textit{effective} ed i loro prototipi sono: + +\begin{funcproto}{ +\fhead{unistd.h} +\fhead{sys/types.h} +\fdecl{int seteuid(uid\_t uid)} +\fdesc{Imposta l'\ids{UID} effettivo del processo corrente.} +\fdecl{int setegid(gid\_t gid)} +\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}. +} +\end{funcproto} + +Ancora una volta le due funzioni sono identiche, e quanto diremo per la prima +riguardo gli \ids{UID} si applica allo stesso modo alla seconda per i +\ids{GID}. Con \func{seteuid} gli utenti normali possono impostare l'\ids{UID} +effettivo solo al valore dell'\ids{UID} reale o dell'\ids{UID} salvato, +l'amministratore può specificare qualunque valore. Queste funzioni sono usate +per permettere all'amministratore di impostare solo l'\ids{UID} effettivo, +dato che l'uso normale di \func{setuid} comporta l'impostazione di tutti gli +identificatori. + +Le due funzioni di sistema \funcd{setresuid} e \funcd{setresgid} sono invece +un'estensione introdotta in Linux (a partire dal kernel 2.1.44) e permettono +un completo controllo su tutti e tre i gruppi di identificatori +(\textit{real}, \textit{effective} e \textit{saved}), i loro prototipi sono: + +\begin{funcproto}{ +\fhead{unistd.h} +\fhead{sys/types.h} +\fdecl{int setresuid(uid\_t ruid, uid\_t euid, uid\_t suid)} +\fdesc{Imposta l'\ids{UID} reale, effettivo e salvato del processo corrente.} +\fdecl{int setresgid(gid\_t rgid, gid\_t egid, gid\_t sgid)} +\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}. +} +\end{funcproto} + +Di nuovo le due funzioni sono identiche e quanto detto per la prima riguardo +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 +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 +corrispondente. + +Per queste funzioni di sistema esistono anche due controparti, +\funcd{getresuid} e \funcd{getresgid},\footnote{le funzioni non sono standard, + anche se appaiono in altri kernel, su Linux sono presenti dal kernel 2.1.44 + e con le versioni della \acr{glibc} a partire dalla 2.3.2, definendo la + macro \macro{\_GNU\_SOURCE}.} che permettono di leggere in blocco i vari +identificatori; i loro prototipi sono: + +\begin{funcproto}{ +\fhead{unistd.h} +\fhead{sys/types.h} +\fdecl{int getresuid(uid\_t *ruid, uid\_t *euid, uid\_t *suid)} +\fdesc{Legge l'\ids{UID} reale, effettivo e salvato del processo corrente.} +\fdecl{int getresgid(gid\_t *rgid, gid\_t *egid, gid\_t *sgid)} +\fdesc{Legge 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{EFAULT} se gli + indirizzi delle variabili di ritorno non sono validi. } +\end{funcproto} + +Anche queste funzioni sono un'estensione specifica di Linux, e non richiedono +nessun privilegio. I valori sono restituiti negli argomenti, che vanno +specificati come puntatori (è un altro esempio di +\itindex{value~result~argument} \textit{value result argument}). Si noti che +queste funzioni sono le uniche in grado di leggere gli identificatori del +gruppo \textit{saved}. + +Infine le funzioni \func{setfsuid} e \func{setfsgid} servono per impostare gli +identificatori del gruppo \textit{filesystem} che sono usati da Linux per il +controllo dell'accesso ai file. Come già accennato in +sez.~\ref{sec:proc_access_id} Linux definisce questo ulteriore gruppo di +identificatori, che in circostanze normali sono assolutamente equivalenti a +quelli del gruppo \textit{effective}, dato che ogni cambiamento di questi +ultimi viene immediatamente riportato su di essi. + +C'è un solo caso in cui si ha necessità di introdurre una differenza fra gli +identificatori dei gruppi \textit{effective} e \textit{filesystem}, ed è per +ovviare ad un problema di sicurezza che si presenta quando si deve +implementare un server NFS. + +Il server NFS infatti deve poter cambiare l'identificatore con cui accede ai +file per assumere l'identità del singolo utente remoto, ma se questo viene +fatto cambiando l'\ids{UID} effettivo o l'\ids{UID} reale il server si espone +alla ricezione di eventuali segnali ostili da parte dell'utente di cui ha +temporaneamente assunto l'identità. Cambiando solo l'\ids{UID} di filesystem +si ottengono i privilegi necessari per accedere ai file, mantenendo quelli +originari per quanto riguarda tutti gli altri controlli di accesso, così che +l'utente non possa inviare segnali al server NFS. + +Le due funzioni di sistema usate per cambiare questi identificatori sono +\funcd{setfsuid} e \funcd{setfsgid}, ed ovviamente sono specifiche di Linux e +non devono essere usate se si intendono scrivere programmi portabili; i loro +prototipi sono: + +\begin{funcproto}{ +\fhead{sys/fsuid.h} +\fdecl{int setfsuid(uid\_t fsuid)} +\fdesc{Imposta l'\ids{UID} di filesystem del processo corrente.} +\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}.} +\end{funcproto} + +Le due funzioni sono analoghe ed usano il valore passato come argomento per +effettuare l'impostazione dell'identificativo. Le funzioni hanno successo +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}. + + +\subsection{Le funzioni per la gestione dei gruppi associati a un processo} +\label{sec:proc_setgroups} + +Le ultime funzioni che esamineremo sono quelle che permettono di operare sui +gruppi supplementari cui un utente può appartenere. Ogni processo può avere +almeno \const{NGROUPS\_MAX} gruppi supplementari\footnote{il numero massimo di + gruppi secondari può essere ottenuto con \func{sysconf} (vedi + sez.~\ref{sec:sys_limits}), leggendo il parametro + \texttt{\_SC\_NGROUPS\_MAX}.} in aggiunta al gruppo primario; questi vengono +ereditati dal processo padre e possono essere cambiati con queste funzioni. + +La funzione di sistema che permette di leggere i gruppi supplementari +associati ad un processo è \funcd{getgroups}; questa funzione è definita nello +standard POSIX.1, ed il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{unistd.h} +\fdecl{int getgroups(int size, gid\_t list[])} +\fdesc{Legge gli identificatori dei gruppi supplementari.} +} +{La funzione ritorna il numero di gruppi letti in caso di successo e $-1$ per + un errore, nel qual caso \var{errno} assumerà uno dei valori: +\begin{errlist} +\item[\errcode{EFAULT}] \param{list} non ha un indirizzo valido. +\item[\errcode{EINVAL}] il valore di \param{size} è diverso da zero ma + minore del numero di gruppi supplementari del processo. +\end{errlist}} +\end{funcproto} + +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. + +Una seconda funzione, \funcd{getgrouplist}, può invece essere usata per +ottenere tutti i gruppi a cui appartiene utente identificato per nome; il suo +prototipo è: + +\begin{funcproto}{ +\fhead{grp.h} +\fdecl{int getgrouplist(const char *user, gid\_t group, gid\_t *groups, int + *ngroups)} +\fdesc{Legge i gruppi cui appartiene un utente.} +} +{La funzione ritorna il numero di gruppi ottenuto in caso di successo e $-1$ + per un errore, che avviene solo quando il numero di gruppi è maggiore di + quelli specificati con \param{ngroups}.} +\end{funcproto} + +La funzione esegue una scansione del database dei gruppi (si veda +sez.~\ref{sec:sys_user_group}) per leggere i gruppi supplementari dell'utente +specificato per nome (e non con un \ids{UID}) nella stringa passata con +l'argomento \param{user}. Ritorna poi nel vettore \param{groups} la lista dei +\ids{GID} dei gruppi a cui l'utente appartiene. Si noti che \param{ngroups}, +che in ingresso deve indicare la dimensione di \param{group}, è passato come +\itindex{value~result~argument} \textit{value result argument} perché, qualora +il valore specificato sia troppo piccolo, la funzione ritorna $-1$, passando +comunque indietro il numero dei gruppi trovati, in modo da poter ripetere la +chiamata con un vettore di dimensioni adeguate. + +Infine per impostare i gruppi supplementari di un processo ci sono due +funzioni, che possono essere usate solo se si hanno i privilegi di +amministratore.\footnote{e più precisamente se si ha la \itindex{capabilities} + \textit{capability} \macro{CAP\_SETGID}.} La prima delle due è la funzione +di sistema \funcd{setgroups},\footnote{la funzione è definita in BSD e SRv4, + ma a differenza di \func{getgroups} non è stata inclusa in POSIX.1-2001, per + poterla utilizzare deve essere definita la macro \macro{\_BSD\_SOURCE}.} ed +il suo prototipo è: + +\begin{funcproto}{ +\fhead{grp.h} +\fdecl{int setgroups(size\_t size, gid\_t *list)} +\fdesc{Imposta i gruppi supplementari del processo.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual +caso \var{errno} assumerà uno dei valori: +\begin{errlist} +\item[\errcode{EFAULT}] \param{list} non ha un indirizzo valido. +\item[\errcode{EINVAL}] il valore di \param{size} è maggiore del valore + massimo consentito di gruppi supplementari. +\item[\errcode{EPERM}] il processo non ha i privilegi di amministratore. +\end{errlist}} +\end{funcproto} + +La funzione imposta i gruppi supplementari del processo corrente ai valori +specificati nel vettore passato con l'argomento \param{list}, di dimensioni +date dall'argomento \param{size}. Il numero massimo di gruppi supplementari +che si possono impostare è un parametro di sistema, che può essere ricavato +con le modalità spiegate in sez.~\ref{sec:sys_characteristics}. + +Se invece si vogliono impostare i gruppi supplementari del processo a quelli +di un utente specifico, si può usare la funzione \funcd{initgroups} il cui +prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{grp.h} +\fdecl{int initgroups(const char *user, gid\_t group)} +\fdesc{Inizializza la lista dei gruppi supplementari.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual +caso \var{errno} assumerà uno dei valori: +\begin{errlist} +\item[\errcode{ENOMEM}] non c'è memoria sufficiente per allocare lo spazio per + informazioni dei gruppi. +\item[\errcode{EPERM}] il processo non ha i privilegi di amministratore. +\end{errlist}} +\end{funcproto} + +La funzione esegue la scansione del database dei gruppi (usualmente +\conffile{/etc/group}) cercando i gruppi di cui è membro l'utente \param{user} +(di nuovo specificato per nome e non per \ids{UID}) con cui costruisce una +lista di gruppi supplementari, a cui aggiunge anche +\param{group}, infine imposta questa lista per il processo corrente usando +\func{setgroups}. Si tenga presente che sia \func{setgroups} che +\func{initgroups} non sono definite nello standard POSIX.1 e che pertanto non +è possibile utilizzarle quando si definisce \macro{\_POSIX\_SOURCE} o si +compila con il flag \cmd{-ansi}, è pertanto meglio evitarle se si vuole +scrivere codice portabile. + + +\section{La gestione della priorità dei processi} +\label{sec:proc_priority} + +In questa sezione tratteremo più approfonditamente i meccanismi con il quale +lo \itindex{scheduler} \textit{scheduler} assegna la CPU ai vari processi +attivi. In particolare prenderemo in esame i vari meccanismi con cui viene +gestita l'assegnazione del tempo di CPU, ed illustreremo le varie funzioni di +gestione. Tratteremo infine anche le altre priorità dei processi (come quelle +per l'accesso a disco) divenute disponibili con i kernel più recenti. + + +\subsection{I meccanismi di \textit{scheduling}} +\label{sec:proc_sched} + +\itindbeg{scheduler} + +La scelta di un meccanismo che sia in grado di distribuire in maniera efficace +il tempo di CPU per l'esecuzione dei processi è sempre una questione delicata, +ed oggetto di numerose ricerche; in generale essa dipende in maniera +essenziale anche dal tipo di utilizzo che deve essere fatto del sistema, per +cui non esiste un meccanismo che sia valido per tutti gli usi. + +La caratteristica specifica di un sistema multitasking come Linux è quella del +cosiddetto \itindex{preemptive~multitasking} \textit{preemptive + multitasking}: questo significa che al contrario di altri sistemi (che usano +invece il cosiddetto \itindex{cooperative~multitasking} \textit{cooperative + multitasking}) non sono i singoli processi, ma il kernel stesso a decidere +quando la CPU deve essere passata ad un altro processo. Come accennato in +sez.~\ref{sec:proc_hierarchy} questa scelta viene eseguita da una sezione +apposita del kernel, lo \textit{scheduler}, il cui scopo è quello di +distribuire al meglio il tempo di CPU fra i vari processi. + +La cosa è resa ancora più complicata dal fatto che con le architetture +multi-processore si deve anche scegliere quale sia la CPU più opportuna da +utilizzare.\footnote{nei processori moderni la presenza di ampie cache può + rendere poco efficiente trasferire l'esecuzione di un processo da una CPU ad + un'altra, per cui effettuare la migliore scelta fra le diverse CPU non è + banale.} Tutto questo comunque appartiene alle sottigliezze +dell'implementazione del kernel; dal punto di vista dei programmi che girano +in \textit{user space}, anche quando si hanno più processori (e dei processi +che sono eseguiti davvero in contemporanea), le politiche di +\textit{scheduling} riguardano semplicemente l'allocazione della risorsa +\textsl{tempo di esecuzione}, la cui assegnazione sarà governata dai +meccanismi di scelta delle priorità che restano gli stessi indipendentemente +dal numero di processori. + +Si tenga conto poi che i processi non devono solo eseguire del codice: ad +esempio molto spesso saranno impegnati in operazioni di I/O, o potranno +venire bloccati da un comando dal terminale, o sospesi per un certo periodo di +tempo. In tutti questi casi la CPU diventa disponibile ed è compito dello +kernel provvedere a mettere in esecuzione un altro processo. + +Tutte queste possibilità sono caratterizzate da un diverso \textsl{stato} del +processo, in Linux un processo può trovarsi in uno degli stati riportati in +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 +fintanto che esso si trova in uno qualunque degli altri stati. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|p{2.4cm}|c|p{9cm}|} + \hline + \textbf{Stato} & \texttt{STAT} & \textbf{Descrizione} \\ + \hline + \hline + \textit{runnable}& \texttt{R} & Il processo è in esecuzione o è pronto ad + essere eseguito (cioè è in attesa che gli + venga assegnata la CPU).\\ + \textit{sleep} & \texttt{S} & Il processo è in attesa di un + risposta dal sistema, ma può essere + interrotto da un segnale.\\ + \textit{uninterrutible sleep}& \texttt{D} & Il processo è in + attesa di un risposta dal sistema (in + genere per I/O), e non può essere + interrotto in nessuna circostanza.\\ + \textit{stopped} & \texttt{T} & Il processo è stato fermato con un + \signal{SIGSTOP}, o è tracciato.\\ + \textit{zombie}\itindex{zombie}& \texttt{Z} & Il processo è terminato ma il + suo stato di terminazione non è ancora + stato letto dal padre.\\ + \textit{killable}& \texttt{D} & Un nuovo stato introdotto con il kernel + 2.6.25, sostanzialmente identico + all'\textit{uninterrutible sleep} con la + sola differenza che il processo può + terminato con \signal{SIGKILL} (usato per + lo più per NFS).\\ + \hline + \end{tabular} + \caption{Elenco dei possibili stati di un processo in Linux, nella colonna + \texttt{STAT} si è riportata la corrispondente lettera usata dal comando + \cmd{ps} nell'omonimo campo.} + \label{tab:proc_proc_states} +\end{table} + +Si deve quindi tenere presente che l'utilizzo della CPU è soltanto una delle +risorse che sono necessarie per l'esecuzione di un programma, e a seconda +dello scopo del programma non è detto neanche che sia la più importante, dato +che molti programmi dipendono in maniera molto più critica dall'I/O. Per +questo motivo non è affatto detto che dare ad un programma la massima priorità +di esecuzione abbia risultati significativi in termini di prestazioni. + +Il meccanismo tradizionale di \textit{scheduling} di Unix (che tratteremo in +sez.~\ref{sec:proc_sched_stand}) è sempre stato basato su delle +\textsl{priorità dinamiche}, in modo da assicurare che tutti i processi, anche +i meno importanti, potessero ricevere un po' di tempo di CPU. In sostanza +quando un processo ottiene la CPU la sua priorità viene diminuita. In questo +modo alla fine, anche un processo con priorità iniziale molto bassa, finisce +per avere una priorità sufficiente per essere eseguito. + +Lo standard POSIX.1b però ha introdotto il concetto di \textsl{priorità + assoluta}, (chiamata anche \textsl{priorità statica}, in contrapposizione +alla normale priorità dinamica), per tenere conto dei sistemi +\textit{real-time},\footnote{per sistema \textit{real-time} si intende un + sistema in grado di eseguire operazioni in un tempo ben determinato; in + genere si tende a distinguere fra l'\textit{hard real-time} in cui è + necessario che i tempi di esecuzione di un programma siano determinabili con + certezza assoluta (come nel caso di meccanismi di controllo di macchine, + dove uno sforamento dei tempi avrebbe conseguenze disastrose), e + \textit{soft-real-time} in cui un occasionale sforamento è ritenuto + accettabile.} in cui è vitale che i processi che devono essere eseguiti in +un determinato momento non debbano aspettare la conclusione di altri che non +hanno questa necessità. + +Il concetto di priorità assoluta dice che quando due processi si contendono +l'esecuzione, vince sempre quello con la priorità assoluta più alta. +Ovviamente questo avviene solo per i processi che sono pronti per essere +eseguiti (cioè nello stato \textit{runnable}). La priorità assoluta viene in +genere indicata con un numero intero, ed un valore più alto comporta una +priorità maggiore. Su questa politica di \textit{scheduling} torneremo in +sez.~\ref{sec:proc_real_time}. + +In generale quello che succede in tutti gli Unix moderni è che ai processi +normali viene sempre data una priorità assoluta pari a zero, e la decisione di +assegnazione della CPU è fatta solo con il meccanismo tradizionale della +priorità dinamica. In Linux tuttavia è possibile assegnare anche una priorità +assoluta, nel qual caso un processo avrà la precedenza su tutti gli altri di +priorità inferiore, che saranno eseguiti solo quando quest'ultimo non avrà +bisogno della CPU. + + +\subsection{Il meccanismo di \textit{scheduling} standard} +\label{sec:proc_sched_stand} + +A meno che non si abbiano esigenze specifiche,\footnote{per alcune delle quali + sono state introdotte delle varianti specifiche.} l'unico meccanismo di +\textit{scheduling} con il quale si avrà a che fare è quello tradizionale, che +prevede solo priorità dinamiche. È di questo che, di norma, ci si dovrà +preoccupare nella programmazione. Come accennato in Linux 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. + +Il meccanismo usato da Linux è in realtà piuttosto complesso,\footnote{e + dipende strettamente dalla versione di kernel; in particolare a partire + dalla serie 2.6.x lo scheduler è stato riscritto completamente, con molte + modifiche susseguitesi per migliorarne le prestazioni, per un certo periodo + ed è stata anche introdotta la possibilità di usare diversi algoritmi, + selezionabili sia in fase di compilazione, che, nelle versioni più recenti, + all'avvio (addirittura è stato ideato un sistema modulare che permette di + cambiare lo scheduler a sistema attivo).} ma a grandi linee si può dire che +ad ogni processo è assegnata una \textit{time-slice}, cioè un intervallo di +tempo (letteralmente una fetta) per il quale, a meno di eventi esterni, esso +viene eseguito senza essere interrotto. Inoltre la priorità dinamica viene +calcolata dallo scheduler a partire da un valore iniziale che viene +\textsl{diminuito} tutte le volte che un processo è in stato \textit{runnable} +ma non viene posto in esecuzione.\footnote{in realtà il calcolo della priorità + dinamica e la conseguente scelta di quale 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 scheduler si legga il quarto capitolo di + \cite{LinKernDev}.} Lo 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. + +Sia la dimensione della \textit{time-slice} che il valore di partenza della +priorità dinamica sono determinate dalla cosiddetta \textit{nice} (o +\textit{niceness}) del processo.\footnote{questa è una delle tante proprietà + che ciascun processo si porta dietro, essa viene ereditata dai processi + figli e mantenuta attraverso una \func{exec}; fino alla serie 2.4 essa era + mantenuta nell'omonimo campo \texttt{nice} della \texttt{task\_struct}, con + la riscrittura dello scheduler eseguita nel 2.6 viene mantenuta nel campo + \texttt{static\_prio} come per le priorità statiche.} L'origine del nome di +questo parametro sta nel fatto che generalmente questo viene usato per +\textsl{diminuire} la priorità di un processo, come misura di cortesia nei +confronti degli altri. I processi infatti vengono creati dal sistema con un +valore nullo e nessuno è privilegiato rispetto agli altri. Specificando un +valore di \textit{nice} positivo si avrà una \textit{time-slice} più breve ed +un valore di priorità dinamica iniziale più alto, mentre un valore negativo +darà una \textit{time-slice} più lunga ed un valore di priorità dinamica +iniziale più basso. + +Esistono diverse funzioni che consentono di indicare un valore di +\textit{nice} di un processo; la più semplice è \funcd{nice}, che opera sul +processo corrente, il suo prototipo è: + +\begin{funcproto}{ +\fhead{unistd.h} +\fdecl{int nice(int inc)} +\fdesc{Aumenta il valore di \textit{nice} del processo corrente.} +} +{La funzione ritorna il nuovo valore di \textit{nice} in caso di successo e + $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori: +\begin{errlist} + \item[\errcode{EPERM}] non si ha il permesso di specificare un valore + di \param{inc} negativo. +\end{errlist}} +\end{funcproto} + +L'argomento \param{inc} indica l'incremento da effettuare rispetto al valore +di \textit{nice} corrente, che può assumere valori compresi fra +\const{PRIO\_MIN} e \const{PRIO\_MAX}; nel caso di Linux sono fra $-20$ e +$19$,\footnote{in realtà l'intervallo varia a seconda delle versioni di + kernel, ed è questo a partire dal kernel 1.3.43, anche se oggi si può avere + anche l'intervallo fra $-20$ e $20$.} ma per \param{inc} si può specificare +un valore qualunque, positivo o negativo, ed il sistema provvederà a troncare +il risultato nell'intervallo consentito. Valori positivi comportano maggiore +\textit{cortesia} e cioè una diminuzione della priorità, valori negativi +comportano invece un aumento della priorità. Con i kernel precedenti il 2.6.12 +solo l'amministratore\footnote{o un processo con la \itindex{capabilities} + \textit{capability} \const{CAP\_SYS\_NICE}, vedi + sez.~\ref{sec:proc_capabilities}.} può specificare valori negativi +di \param{inc} che permettono di aumentare la priorità di un processo, a +partire da questa versione è consentito anche agli utenti normali alzare +(entro certi limiti, che vedremo in sez.~\ref{sec:sys_resource_limit}) la +priorità dei propri processi. + +Gli standard SUSv2 e POSIX.1 prevedono che la funzione ritorni il nuovo valore +di \textit{nice} del processo; tuttavia la \textit{system call} di Linux non +segue questa convenzione e restituisce sempre $0$ in caso di successo e $-1$ +in caso di errore; questo perché $-1$ è anche un valore di \textit{nice} +legittimo e questo comporta una confusione con una eventuale condizione di +errore. La \textit{system call} originaria inoltre non consente, se non dotati +di adeguati privilegi, di diminuire un valore di \textit{nice} precedentemente +innalzato. + +Fino alla \acr{glibc} 2.2.4 la funzione di libreria riportava direttamente il +risultato dalla \textit{system call}, violando lo standard, per cui per +ottenere il nuovo valore occorreva una successiva chiamata alla funzione +\func{getpriority}. A partire dalla \acr{glibc} 2.2.4 \func{nice} è stata +reimplementata e non viene più chiamata la omonima \textit{system call}, con +questa versione viene restituito come valore di ritorno il valore di +\textit{nice}, come richiesto dallo standard.\footnote{questo viene fatto + chiamando al suo interno \func{setpriority}, che tratteremo a breve.} In +questo caso l'unico modo per rilevare in maniera affidabile una condizione di +errore è quello di azzerare \var{errno} prima della chiamata della funzione e +verificarne il valore quando \func{nice} restituisce $-1$. + +Per leggere il valore di \textit{nice} di un processo occorre usare la +funzione di sistema \funcd{getpriority}, derivata da BSD; il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/time.h} +\fhead{sys/resource.h} +\fdecl{int getpriority(int which, int who)} +\fdesc{Legge un valore di \textit{nice}.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual +caso \var{errno} assumerà uno dei valori: +\begin{errlist} +\item[\errcode{EINVAL}] il valore di \param{which} non è uno di quelli + elencati in tab.~\ref{tab:proc_getpriority}. +\item[\errcode{ESRCH}] non c'è nessun processo che corrisponda ai valori di + \param{which} e \param{who}. +\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 \headfile{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 +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 +l'utente correnti. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|c|c|l|} + \hline + \param{which} & \param{who} & \textbf{Significato} \\ + \hline + \hline + \const{PRIO\_PROCESS} & \type{pid\_t} & processo \\ + \const{PRIO\_PRGR} & \type{pid\_t} & \itindex{process~group} + \textit{process group} \\ + \const{PRIO\_USER} & \type{uid\_t} & utente \\ + \hline + \end{tabular} + \caption{Legenda del valore dell'argomento \param{which} e del tipo + dell'argomento \param{who} delle funzioni \func{getpriority} e + \func{setpriority} per le tre possibili scelte.} + \label{tab:proc_getpriority} +\end{table} + +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 +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 +resti uguale a zero. + +Analoga a \func{getpriority} è la funzione di sistema \funcd{setpriority} che +permette di impostare la priorità di uno o più processi; il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/time.h} +\fhead{sys/resource.h} +\fdecl{int setpriority(int which, int who, int prio)} +\fdesc{Imposta un valore di \textit{nice}.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual +caso \var{errno} assumerà uno dei valori: +\begin{errlist} +\item[\errcode{EACCES}] si è richiesto un aumento di priorità senza avere + sufficienti privilegi. +\item[\errcode{EINVAL}] il valore di \param{which} non è uno di quelli + elencati in tab.~\ref{tab:proc_getpriority}. +\item[\errcode{EPERM}] un processo senza i privilegi di amministratore ha + cercato di modificare la priorità di un processo di un altro utente. +\item[\errcode{ESRCH}] non c'è nessun processo che corrisponda ai valori di + \param{which} e \param{who}. +\end{errlist}} +\end{funcproto} + +La funzione imposta la priorità dinamica al valore specificato da \param{prio} +per tutti i processi indicati dagli argomenti \param{which} e \param{who}, per +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} +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 +\textit{nice} valido. + +Si tenga presente che solo l'amministratore\footnote{o più precisamente un + processo con la \itindex{capabilities} \textit{capability} + \const{CAP\_SYS\_NICE}, vedi sez.~\ref{sec:proc_capabilities}.} ha la +possibilità di modificare arbitrariamente le priorità di qualunque +processo. Un utente normale infatti può modificare solo la priorità dei suoi +processi ed in genere soltanto diminuirla. Fino alla versione di kernel +2.6.12 Linux ha seguito le specifiche dello standard SUSv3, e come per tutti i +sistemi derivati da SysV veniva richiesto che l'\ids{UID} reale o quello +effettivo del processo chiamante corrispondessero all'\ids{UID} reale (e solo +a quello) del processo di cui si intendeva cambiare la priorità. A partire +dalla versione 2.6.12 è stata adottata la semantica in uso presso i sistemi +derivati da BSD (SunOS, Ultrix, *BSD), in cui la corrispondenza può essere +anche con l'\ids{UID} effettivo. + +Sempre a partire dal kernel 2.6.12 è divenuto possibile anche per gli utenti +ordinari poter aumentare la priorità dei propri processi specificando un +valore di \param{prio} negativo. Questa operazione non è possibile però in +maniera indiscriminata, ed in particolare può essere effettuata solo +nell'intervallo consentito dal valore del limite \const{RLIMIT\_NICE} +(torneremo su questo in sez.~\ref{sec:sys_resource_limit}). + +Infine nonostante i valori siano sempre rimasti gli stessi, il significato del +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 +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 \itindex{page~fault} \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 + dalla versione 2.6.18 sono stati inserite nel kernel una serie di modifiche + che consentono di avvicinarsi sempre di più ad un vero e proprio sistema + \textit{real-time} estendendo il concetto di \textit{preemption} alle + operazioni dello stesso kernel; esistono vari livelli a cui questo può + essere fatto, ottenibili attivando in fase di compilazione una fra le + opzioni \texttt{CONFIG\_PREEMPT\_NONE}, \texttt{CONFIG\_PREEMPT\_VOLUNTARY} + e \texttt{CONFIG\_PREEMPT\_DESKTOP}.} si può arrivare ad una ottima +approssimazione di sistema \textit{real-time} usando le priorità assolute. +Occorre farlo però con molta attenzione: se si dà ad un processo una priorità +assoluta e questo finisce in un loop infinito, nessun altro processo potrà +essere eseguito, ed esso sarà mantenuto in esecuzione permanentemente +assorbendo tutta la CPU e senza nessuna possibilità di riottenere l'accesso al +sistema. Per questo motivo è sempre opportuno, quando si lavora con processi +che usano priorità assolute, tenere attiva una shell cui si sia assegnata la +massima priorità assoluta, in modo da poter essere comunque in grado di +rientrare nel sistema. + +Quando c'è un processo con priorità assoluta lo \textit{scheduler} lo metterà +in esecuzione prima di ogni processo normale. In caso di più processi sarà +eseguito per primo quello con priorità assoluta più alta. Quando ci sono più +processi con la stessa priorità assoluta questi vengono tenuti in una coda e +tocca al kernel decidere quale deve essere eseguito. Il meccanismo con cui +vengono gestiti questi processi dipende dalla politica di \textit{scheduling} +che si è scelta; lo standard ne prevede due: +\begin{basedescript}{\desclabelwidth{1.2cm}\desclabelstyle{\nextlinelabel}} +\item[\textit{First In First Out} (FIFO)] Il processo viene eseguito + fintanto che non cede volontariamente la CPU (con la funzione + \func{sched\_yield}), si blocca, finisce o viene interrotto da un processo a + priorità più alta. Se il processo viene interrotto da uno a priorità più + alta esso resterà in cima alla lista e sarà il primo ad essere eseguito + quando i processi a priorità più alta diverranno inattivi. Se invece lo si + blocca volontariamente sarà posto in coda alla lista (ed altri processi con + la stessa priorità potranno essere eseguiti). +\item[\textit{Round Robin} (RR)] Il comportamento è del tutto analogo a quello + precedente, con la sola differenza che ciascun processo viene eseguito al + massimo per un certo periodo di tempo (la cosiddetta \textit{time-slice}) + dopo di che viene automaticamente posto in fondo alla coda dei processi con + la stessa priorità. In questo modo si ha comunque una esecuzione a turno di + tutti i processi, da cui il nome della politica. Solo i processi con la + stessa priorità ed in stato \textit{runnable} entrano nel + \textsl{girotondo}. +\end{basedescript} + +Lo standard POSIX.1-2001 prevede una funzione che consenta sia di modificare +le politiche di \textit{scheduling}, passando da \textit{real-time} a +ordinarie o viceversa, che di specificare, in caso di politiche +\textit{real-time}, la eventuale priorità statica; la funzione di sistema è +\funcd{sched\_setscheduler} ed il suo prototipo è: + +\begin{funcproto}{ +\fhead{sched.h} +\fdecl{int sched\_setscheduler(pid\_t pid, int policy, const struct + sched\_param *p)} +\fdesc{Imposta priorità e politica di \textit{scheduling}.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual +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. + \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{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 +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 +politiche \textit{real-time}, quella bassa le politiche ordinarie. Un valore +negativo per \param{policy} mantiene la politica di \textit{scheduling} +corrente. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{6cm}|} + \hline + \textbf{Politica} & \textbf{Significato} \\ + \hline + \hline + \const{SCHED\_FIFO} & \textit{Scheduling real-time} con politica + \textit{FIFO}. \\ + \const{SCHED\_RR} & \textit{Scheduling real-time} con politica + \textit{Round Robin}. \\ + \hline + \const{SCHED\_OTHER}& \textit{Scheduling} ordinario.\\ + \const{SCHED\_BATCH}& \textit{Scheduling} ordinario con l'assunzione + ulteriore di lavoro \textit{CPU + intensive} (dal kernel 2.6.16)\\ + \const{SCHED\_IDLE} & \textit{Scheduling} di priorità estremamente + bassa (dal kernel 2.6.23)\\ + \hline + \end{tabular} + \caption{Valori dell'argomento \param{policy} per la funzione + \func{sched\_setscheduler}.} + \label{tab:proc_sched_policy} +\end{table} + +Con le versioni più recenti del kernel sono state introdotte anche delle +varianti sulla politica di \textit{scheduling} tradizionale per alcuni carichi +di lavoro specifici, queste due nuove politiche sono specifiche di Linux e non +devono essere usate se si vogliono scrivere programmi portabili. + +La politica \const{SCHED\_BATCH} è una variante della politica ordinaria con +la sola differenza che i processi ad essa soggetti non ottengono, nel calcolo +delle priorità dinamiche fatto dallo scheduler, il cosiddetto bonus di +interattività che mira a favorire i processi che si svegliano dallo stato 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}. + +La politica \const{SCHED\_IDLE} invece è una politica dedicata ai processi che +si desidera siano eseguiti con la più bassa priorità possibile, ancora più +bassa di un processo con il minimo valore di \textit{nice}. In sostanza la si +può utilizzare per processi che devono essere eseguiti se non c'è niente altro +da fare. Va comunque sottolineato che anche un processo \const{SCHED\_IDLE} +avrà comunque una sua possibilità di utilizzo della CPU, sia pure in +percentuale molto bassa. + +Qualora si sia richiesta una politica \textit{real-time} il valore della +priorità statica viene impostato attraverso la struttura +\struct{sched\_param}, riportata in fig.~\ref{fig:sig_sched_param}, il cui +solo campo attualmente definito è \var{sched\_priority}. Il campo deve +contenere il valore della priorità statica da assegnare al processo; lo +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] + \footnotesize \centering + \begin{minipage}[c]{0.5\textwidth} + \includestruct{listati/sched_param.c} + \end{minipage} + \normalsize + \caption{La struttura \structd{sched\_param}.} + \label{fig:sig_sched_param} +\end{figure} + +I processi con politica di \textit{scheduling} ordinaria devono sempre +specificare un valore nullo di \var{sched\_priority} altrimenti si avrà un +errore \errcode{EINVAL}, questo valore infatti non ha niente a che vedere con +la priorità dinamica determinata dal valore di \textit{nice}, che deve essere +impostato con le funzioni viste in precedenza. + +Lo standard POSIX.1b prevede comunque che l'intervallo dei valori delle +priorità statiche possa essere ottenuto con le funzioni di sistema +\funcd{sched\_get\_priority\_max} e \funcd{sched\_get\_priority\_min}, i cui +prototipi sono: + +\begin{funcproto}{ +\fhead{sched.h} +\fdecl{int sched\_get\_priority\_max(int policy)} +\fdesc{Legge il valore massimo di una priorità statica.} +\fdecl{int sched\_get\_priority\_min(int policy)} +\fdesc{Legge il valore minimo di una priorità statica.} +} +{Le funzioni ritornano il valore della priorità in caso di successo e $-1$ per + un errore, nel qual caso \var{errno} assumerà il valore: +\begin{errlist} +\item[\errcode{EINVAL}] il valore di \param{policy} non è valido. +\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}. + +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 +messo in cima alla lista dei processi con la stessa priorità; questo comporta +che verrà eseguito subito, interrompendo eventuali altri processi con la +stessa priorità in quel momento in esecuzione. + +Il kernel mantiene i processi con la stessa priorità assoluta in una lista, ed +esegue sempre il primo della lista, mentre un nuovo processo che torna in +stato \textit{runnable} viene sempre inserito in coda alla lista. Se la +politica scelta è \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. + +Solo un processo con i privilegi di amministratore\footnote{più precisamente + con la \itindex{capabilities} capacità \const{CAP\_SYS\_NICE}, vedi + sez.~\ref{sec:proc_capabilities}.} può impostare senza restrizioni priorità +assolute diverse da zero o politiche \const{SCHED\_FIFO} e +\const{SCHED\_RR}. Un utente normale può modificare solo le priorità di +processi che gli appartengono; è cioè richiesto che l'\ids{UID} effettivo del +processo chiamante corrisponda all'\ids{UID} reale o effettivo del processo +indicato con \param{pid}. + +Fino al kernel 2.6.12 gli utenti normali non potevano impostare politiche +\textit{real-time} o modificare la eventuale priorità statica di un loro +processo. A partire da questa versione è divenuto possibile anche per gli +utenti normali usare politiche \textit{real-time} fintanto che la priorità +assoluta che si vuole impostare è inferiore al limite \const{RLIMIT\_RTPRIO} +(vedi sez.~\ref{sec:sys_resource_limit}) ad essi assegnato. + +Unica eccezione a questa possibilità sono i processi \const{SCHED\_IDLE}, che +non possono cambiare politica di \textit{scheduling} indipendentemente dal +valore di \const{RLIMIT\_RTPRIO}. Inoltre, in caso di processo già sottoposto +ad una politica \textit{real-time}, un utente può sempre, indipendentemente +dal valore di \const{RLIMIT\_RTPRIO}, diminuirne la priorità o portarlo ad una +politica ordinaria. + +Se si intende operare solo sulla priorità statica di un processo si possono +usare le due funzioni di sistema \funcd{sched\_setparam} e +\funcd{sched\_getparam} che consentono rispettivamente di impostarne e +leggerne il valore, i loro prototipi sono: + +\begin{funcproto}{ +\fhead{sched.h} +\fdecl{int sched\_setparam(pid\_t pid, const struct sched\_param *param)} +\fdesc{Imposta la priorità statica di un processo.} +\fdecl{int sched\_getparam(pid\_t pid, struct sched\_param *param)} +\fdesc{Legge la priorità statica di un processo.} +} +{Le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, nel qual +caso \var{errno} assumerà uno dei valori: +\begin{errlist} +\item[\errcode{EINVAL}] il valore di \param{param} non ha senso per la + politica usata dal processo. +\item[\errcode{EPERM}] non si hanno privilegi sufficienti per eseguire + l'operazione. +\item[\errcode{ESRCH}] il processo \param{pid} non esiste. +\end{errlist}} +\end{funcproto} + +Le funzioni richiedono di indicare nell'argomento \param{pid} il processo su +cui operare e usano l'argomento \param{param} per mantenere il valore della +priorità dinamica. Questo è ancora una struttura \struct{sched\_param} ed +assume gli stessi valori già visti per \func{sched\_setscheduler}. + +L'uso di \func{sched\_setparam}, compresi i controlli di accesso che vi si +applicano, è del tutto equivalente a quello di \func{sched\_setscheduler} con +argomento \param{policy} uguale a $-1$. Come per \func{sched\_setscheduler} +specificando $0$ come valore dell'argomento \param{pid} si opera sul processo +corrente. Benché la funzione sia utilizzabile anche con processi sottoposti a +politica ordinaria essa ha senso soltanto per quelli \textit{real-time}, dato +che per i primi la priorità statica può essere soltanto nulla. La +disponibilità di entrambe le funzioni può essere verificata controllando la +macro \macro{\_POSIX\_PRIORITY\_SCHEDULING} che è definita nell'\textit{header + file} \headfile{sched.h}. + +Se invece si vuole sapere quale è politica di \textit{scheduling} di un +processo si può usare la funzione di sistema \funcd{sched\_getscheduler}, il +cui prototipo è: + +\begin{funcproto}{ +\fhead{sched.h} +\fdecl{int sched\_getscheduler(pid\_t pid)} +\fdesc{Legge la politica di \textit{scheduling}.} +} +{La funzione ritorna la politica di \textit{scheduling} in caso di successo e + $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori: +\begin{errlist} + \item[\errcode{EPERM}] non si hanno privilegi sufficienti per eseguire + l'operazione. + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. +\end{errlist}} +\end{funcproto} + +La funzione restituisce il valore, secondo quanto elencato in +tab.~\ref{tab:proc_sched_policy}, della politica di \textit{scheduling} per il +processo specificato dall'argomento \param{pid}, se questo è nullo viene +restituito il valore relativo al processo chiamante. + +L'ultima funzione di sistema che permette di leggere le informazioni relative +ai processi real-time è \funcd{sched\_rr\_get\_interval}, che permette di +ottenere la lunghezza della \textit{time-slice} usata dalla politica +\textit{round robin}; il suo prototipo è: + +\begin{funcproto}{ +\fhead{sched.h} +\fdecl{int sched\_rr\_get\_interval(pid\_t pid, struct timespec *tp)} +\fdesc{Legge la durata della \textit{time-slice} per lo \textit{scheduling} + \textit{round robin}.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual +caso \var{errno} assumerà uno dei valori: +\begin{errlist} +\item[\errcode{EINVAL}] l'argomento \param{pid} non è valido. +\item[\errcode{ENOSYS}] la \textit{system call} non è presente (solo per + kernel arcaici). +\item[\errcode{ESRCH}] il processo \param{pid} non esiste. +\end{errlist} +ed inoltre anche \errval{EFAULT} nel suo significato generico.} +\end{funcproto} + +La funzione restituisce nell'argomento \param{tp} come una struttura +\struct{timespec}, (la cui definizione si può trovare in +fig.~\ref{fig:sys_timeval_struct}) il valore dell'intervallo di tempo usato +per la politica \textit{round robin} dal processo indicato da \ids{PID}. Il +valore dipende dalla versione del kernel, a lungo infatti questo intervallo di +tempo era prefissato e non modificabile ad un valore di 150 millisecondi, +restituito indipendentemente dal \ids{PID} indicato. + +Con kernel recenti però è possibile ottenere una variazione della +\textit{time-slice}, modificando il valore di \textit{nice} del processo +(anche se questo non incide assolutamente sulla priorità statica) che come +accennato in precedenza modifica il valore assegnato alla \textit{time-slice} +di un processo ordinario, che però viene usato anche dai processi +\textit{real-time}. + +Come accennato ogni processo può rilasciare volontariamente la CPU in modo da +consentire agli altri processi di essere eseguiti; la funzione di sistema che +consente di fare tutto questo è \funcd{sched\_yield}, il cui prototipo è: + +\begin{funcproto}{ +\fhead{sched.h} +\fdecl{int sched\_yield(void)} +\fdesc{Rilascia volontariamente l'esecuzione.} +} +{La funzione ritorna $0$ in caso di successo e teoricamente $-1$ per un + errore, ma su Linux ha sempre successo.} +\end{funcproto} + + +Questa funzione ha un utilizzo effettivo soltanto quando si usa lo +\textit{scheduling} \textit{real-time}, e serve a far sì che il processo +corrente rilasci la CPU, in modo da essere rimesso in coda alla lista dei +processi con la stessa priorità per permettere ad un altro di essere eseguito; +se però il processo è l'unico ad essere presente sulla coda l'esecuzione non +sarà interrotta. In genere usano questa funzione i processi con politica +\const{SCHED\_FIFO}, per permettere l'esecuzione degli altri processi con pari +priorità quando la sezione più urgente è finita. + +La funzione può essere utilizzata anche con processi che usano lo +\textit{scheduling} ordinario, ma in questo caso il comportamento non è ben +definito, e dipende dall'implementazione. Fino al kernel 2.6.23 questo +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 + ripristinare un comportamento analogo al precedente scrivendo il valore 1 + nel file \sysctlfile{kernel/sched\_compat\_yield}.} + +L'uso delle funzione nella programmazione ordinaria può essere utile e +migliorare le prestazioni generali del sistema quando si è appena rilasciata +una risorsa contesa con altri processi, e si vuole dare agli altri una +possibilità di approfittarne mettendoli in esecuzione, ma chiamarla senza +necessità, specie se questo avviene ripetutamente all'interno di un qualche +ciclo, può avere invece un forte impatto negativo per la generazione di +\itindex{contest~switch} \textit{contest switch} inutili. + + +\subsection{Il controllo dello \textit{scheduler} per i sistemi + multiprocessore} +\label{sec:proc_sched_multiprocess} + +Con il supporto dei sistemi multiprocessore sono state introdotte delle +funzioni che permettono di controllare in maniera più dettagliata la scelta di +quale processore utilizzare per eseguire un certo programma. Uno dei problemi +che si pongono nei sistemi multiprocessore è infatti quello del cosiddetto +\index{effetto~ping-pong} \textsl{effetto ping-pong}. Può accadere cioè che lo +\textit{scheduler}, quando riavvia un processo precedentemente interrotto +scegliendo il primo processore disponibile, lo faccia eseguire da un +processore diverso rispetto a quello su cui era stato eseguito in +precedenza. Se il processo passa da un processore all'altro in questo modo, +cosa che avveniva abbastanza di frequente con i kernel della seria 2.4.x, si +ha l'\textsl{effetto ping-pong}. + +Questo tipo di comportamento può generare dei seri problemi di prestazioni; +infatti tutti i processori moderni utilizzano una memoria interna (la +\textit{cache}) contenente i dati più usati, che permette di evitare di +eseguire un accesso (molto più lento) alla memoria principale sulla scheda +madre. Chiaramente un processo sarà favorito se i suoi dati sono nella cache +del processore, ma è ovvio che questo può essere vero solo per un processore +alla volta, perché in presenza di più copie degli stessi dati su più +processori, non si potrebbe determinare quale di questi ha la versione dei +dati aggiornata rispetto alla memoria principale. + +Questo comporta che quando un processore inserisce un dato nella sua cache, +tutti gli altri processori che hanno lo stesso dato devono invalidarlo, e +questa operazione è molto costosa in termini di prestazioni. Il problema +diventa serio quando si verifica l'\textsl{effetto ping-pong}, in tal caso +infatti un processo \textsl{rimbalza} continuamente da un processore all'altro +e si ha una continua invalidazione della cache, che non diventa mai +disponibile. + +\itindbeg{CPU~affinity} + +Per ovviare a questo tipo di problemi è nato il concetto di \textsl{affinità + di processore} (o \textit{CPU affinity}); la possibilità cioè di far sì che +un processo possa essere assegnato per l'esecuzione sempre allo stesso +processore. Lo \textit{scheduler} dei kernel della serie 2.4.x aveva una +scarsa \textit{CPU affinity}, e \index{effetto~ping-pong} l'effetto ping-pong +era comune; con il nuovo \textit{scheduler} dei kernel della 2.6.x questo +problema è stato risolto ed esso cerca di mantenere il più possibile ciascun +processo sullo stesso processore. + +In certi casi però resta l'esigenza di poter essere sicuri che un processo sia +sempre eseguito dallo stesso processore,\footnote{quella che viene detta + \textit{hard CPU affinity}, in contrasto con quella fornita dallo + \textit{scheduler}, detta \textit{soft CPU affinity}, che di norma indica + solo una preferenza, non un requisito assoluto.} e per poter risolvere +questo tipo di problematiche nei nuovi kernel\footnote{le due \textit{system + call} per la gestione della \textit{CPU affinity} sono state introdotte + nel kernel 2.5.8, e le corrispondenti funzioni di sistema nella + \textsl{glibc} 2.3.} è stata introdotta l'opportuna infrastruttura ed una +nuova \textit{system call} che permette di impostare su quali processori far +eseguire un determinato processo attraverso una \textsl{maschera di + affinità}. La corrispondente funzione di sistema è +\funcd{sched\_setaffinity} ed il suo prototipo è: + +\index{insieme~di~processori|(} + +\begin{funcproto}{ +\fhead{sched.h} +\fdecl{int sched\_setaffinity(pid\_t pid, size\_t setsize, + cpu\_set\_t *mask)} +\fdesc{Imposta la maschera di affinità di un processo.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual +caso \var{errno} assumerà uno dei valori: +\begin{errlist} +\item[\errcode{EINVAL}] il valore di \param{mask} contiene riferimenti a + processori non esistenti nel sistema o a cui non è consentito l'accesso. +\item[\errcode{EPERM}] il processo non ha i privilegi sufficienti per + eseguire l'operazione. +\item[\errcode{ESRCH}] il processo \param{pid} non esiste. +\end{errlist} +ed inoltre anche \errval{EFAULT} nel suo significato generico.} +\end{funcproto} + +Questa funzione e la corrispondente \func{sched\_getaffinity} hanno una storia +abbastanza complessa, la sottostante \textit{system call} infatti prevede +l'uso di due soli argomenti (per il pid e l'indicazione della maschera dei +processori), che corrispondono al fatto che l'implementazione effettiva usa +una semplice maschera binaria. Quando le funzioni vennero incluse nella +\acr{glibc} assunsero invece un prototipo simile a quello mostrato però con il +secondo argomento di tipo \ctyp{unsigned int}. A complicare la cosa si +aggiunge il fatto che nella versione 2.3.3 della \acr{glibc} detto argomento +venne stato eliminato, per poi essere ripristinato nella versione 2.3.4 nella +forma attuale.\footnote{pertanto se la vostra pagina di manuale non è + aggiornata, o usate quella particolare versione della \acr{glibc}, potrete + trovare indicazioni diverse, il prototipo illustrato è quello riportato + nella versione corrente (maggio 2008) delle pagine di manuale e + corrispondente alla definizione presente in \headfile{sched.h}.} + +La funzione imposta, con l'uso del valore contenuto all'indirizzo +\param{mask}, l'insieme dei processori sui quali deve essere eseguito il +processo identificato tramite il valore passato in \param{pid}. Come in +precedenza il valore nullo di \param{pid} indica il processo corrente. Per +poter utilizzare questa funzione sono richiesti i privilegi di amministratore +(è necessaria la capacità \const{CAP\_SYS\_NICE}) altrimenti essa fallirà con +un errore di \errcode{EPERM}. Una volta impostata una maschera di affinità, +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 +questa funzione non è necessario, in quanto è lo 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 +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 +(\textit{Non-Uniform Memory Access}). + +Infine se un gruppo di processi accede alle stesse risorse condivise (ad +esempio una applicazione con più \itindex{thread} \textit{thread}) può avere +senso usare lo stesso processore in modo da sfruttare meglio l'uso della sua +cache; questo ovviamente riduce i benefici di un sistema multiprocessore +nell'esecuzione contemporanea dei \itindex{thread} \textit{thread}, ma in +certi casi (quando i \itindex{thread} \textit{thread} sono inerentemente +serializzati nell'accesso ad una risorsa) possono esserci sufficienti vantaggi +nell'evitare la perdita della cache da rendere conveniente l'uso dell'affinità +di processore. + +Dato che il numero di processori può variare a seconda delle architetture, per +semplificare l'uso dell'argomento \param{mask} la \acr{glibc} ha introdotto un +apposito dato di tipo, \type{cpu\_set\_t},\footnote{questa è una estensione + specifica della \acr{glibc}, da attivare definendo la macro + \macro{\_GNU\_SOURCE}, non esiste infatti una standardizzazione per questo + tipo di interfaccia e POSIX al momento non prevede nulla al riguardo.} che +permette di identificare un insieme di processori. Il dato è normalmente una +maschera binaria: nei casi più comuni potrebbe bastare un intero a 32 bit, in +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 \index{tipo!opaco} 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 \code{sizeof(cpu\_set\_t)}. + +L'interfaccia di gestione degli insiemi di processori, oltre alla definizione +del tipo \type{cpu\_set\_t}, prevede una serie di macro di preprocessore per +la manipolazione degli stessi. Quelle di base, che consentono rispettivamente +di svuotare un insieme, di aggiungere o togliere un processore o di verificare +se esso è già presente in un insieme, sono le seguenti: + +{\centering +\vspace{3pt} +\begin{funcbox}{ +\fhead{sched.h} +\fdecl{void \macro{CPU\_ZERO}(cpu\_set\_t *set)} +\fdesc{Inizializza un insieme di processori vuoto \param{set}.} +\fdecl{void \macro{CPU\_SET}(int cpu, cpu\_set\_t *set)} +\fdesc{Inserisce il processore \param{cpu} nell'insieme di processori \param{set}.} +\fdecl{void \macro{CPU\_CLR}(int cpu, cpu\_set\_t *set)} +\fdesc{Rimuove il processore \param{cpu} nell'insieme di processori \param{set}.} +\fdecl{int \macro{CPU\_ISSET}(int cpu, cpu\_set\_t *set)} +\fdesc{Controlla se il processore \param{cpu} è nell'insieme di processori \param{set}.} +} +\end{funcbox}} + +Queste macro che sono ispirate dalle analoghe usate per gli insiemi di +\textit{file descriptor} (vedi sez.~\ref{sec:file_select}) e sono state +introdotte con la versione 2.3.3 della \acr{glibc}. Tutte richiedono che si +specifichi il numero di una CPU nell'argomento \param{cpu}, ed un insieme su +cui operare. L'unica che ritorna un risultato è \macro{CPU\_ISSET}, che +restituisce un intero da usare come valore logico (zero se la CPU non è +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 +\itindex{side~effects} \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 + caso indicato in cui si passa una funzione ad una macro che usa l'argomento + al suo interno più volte, o si scrivono espressioni come \code{a=a++} in cui + non è chiaro se prima avvenga l'incremento e poi l'assegnazione, ed il cui + risultato dipende dall'implementazione del compilatore.} + +Le CPU sono numerate da zero (che indica la prima disponibile) fino ad +un numero massimo che dipende dalla architettura hardware. La costante +\const{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: + +{\centering +\vspace{3pt} +\begin{funcbox}{ +\fhead{sched.h} +\fdecl{int \macro{CPU\_COUNT}(cpu\_set\_t *set)} +\fdesc{Conta il numero di processori presenti nell'insieme \param{set}.} +} +\end{funcbox}} + +A partire dalla versione 2.7 della \acr{glibc} sono state introdotte altre +macro che consentono ulteriori manipolazioni, in particolare si possono +compiere delle operazioni logiche sugli insiemi di processori con: + +{\centering +\vspace{3pt} +\begin{funcbox}{ +\fhead{sched.h} +\fdecl{void \macro{CPU\_AND}(cpu\_set\_t *destset, cpu\_set\_t *srcset1, cpu\_set\_t *srcset2)} +\fdesc{Esegue l'AND logico di due insiemi di processori.} +\fdecl{void \macro{CPU\_OR}(cpu\_set\_t *destset, cpu\_set\_t *srcset1, cpu\_set\_t *srcset2)} +\fdesc{Esegue l'OR logico di due insiemi di processori.} +\fdecl{void \macro{CPU\_XOR}(cpu\_set\_t *destset, cpu\_set\_t *srcset1, cpu\_set\_t *srcset2)} +\fdesc{Esegue lo XOR logico di due insiemi di processori.} +\fdecl{int \macro{CPU\_EQUAL}(cpu\_set\_t *set1, cpu\_set\_t *set2)} +\fdesc{Verifica se due insiemi di processori sono uguali.} +} +\end{funcbox}} + +Le prime tre macro richiedono due insiemi di partenza, \param{srcset1} +e \param{srcset2} e forniscono in un terzo insieme \param{destset} (che può +essere anche lo stesso di uno dei precedenti) il risultato della rispettiva +operazione logica sui contenuti degli stessi. In sostanza con \macro{CPU\_AND} +si otterrà come risultato l'insieme che contiene le CPU presenti in entrambi +gli insiemi di partenza, con \macro{CPU\_OR} l'insieme che contiene le CPU +presenti in uno qualunque dei due insiemi di partenza, e con \macro{CPU\_XOR} +l'insieme che contiene le CPU presenti presenti in uno solo dei due insiemi di +partenza. Infine \macro{CPU\_EQUAL} confronta due insiemi ed è l'unica che +restituisce un intero, da usare come valore logico che indica se sono +identici o meno. + +Inoltre, sempre a partire dalla versione 2.7 della \acr{glibc}, è stata +introdotta la possibilità di una allocazione dinamica degli insiemi di +processori, per poterli avere di dimensioni corrispondenti al numero di CPU +effettivamente in gioco, senza dover fare riferimento necessariamente alla +precedente dimensione preimpostata di 1024. Per questo motivo sono state +definite tre ulteriori macro, che consentono rispettivamente di allocare, +disallocare ed ottenere la dimensione in byte di un insieme di processori: + +{\centering +\vspace{3pt} +\begin{funcbox}{ +\fhead{sched.h} +\fdecl{cpu\_set\_t * \macro{CPU\_ALLOC}(num\_cpus)} +\fdesc{Alloca dinamicamente un insieme di processori di dimensione voluta.} +\fdecl{void \macro{CPU\_FREE}(cpu\_set\_t *set)} +\fdesc{Disalloca un insieme di processori allocato dinamicamente.} +\fdecl{size\_t \macro{CPU\_ALLOC\_SIZE}(num\_cpus)} +\fdesc{Ritorna la dimensione di un insieme di processori allocato dinamicamente.} +} +\end{funcbox}} + +La prima macro, \macro{CPU\_ALLOC}, restituisce il puntatore ad un insieme di +processori in grado di contenere almeno \param{num\_cpus} che viene allocato +dinamicamente. Ogni insieme così allocato dovrà essere disallocato con +\macro{CPU\_FREE} passandogli un puntatore ottenuto da una precedente +\macro{CPU\_ALLOC}. La terza macro, \macro{CPU\_ALLOC\_SIZE}, consente di +ottenere la dimensione in byte di un insieme allocato dinamicamente che +contenga \param{num\_cpus} processori. + +Dato che le dimensioni effettive possono essere diverse le macro di gestione e +manipolazione che abbiamo trattato in precedenza non si applicano agli insiemi +allocati dinamicamente, per i quali dovranno sono state definite altrettante +macro equivalenti contraddistinte dal suffisso \texttt{\_S}, che effettuano le +stesse operazioni, ma richiedono in più un argomento +aggiuntivo \param{setsize} che deve essere assegnato al valore ottenuto con +\macro{CPU\_ALLOC\_SIZE}. Questo stesso valore deve essere usato per l'omonimo +argomento delle funzioni \func{sched\_setaffinity} o \func{sched\_getaffinity} +quando si vuole usare per l'argomento che indica la maschera di affinità un +insieme di processori allocato dinamicamente. + +\index{insieme~di~processori|)} + +A meno di non aver utilizzato \func{sched\_setaffinity}, in condizioni +ordinarie la maschera di affinità di un processo è preimpostata dal sistema in +modo che esso possa essere eseguito su qualunque processore. Se ne può +comunque ottenere il valore corrente usando la funzione di sistema +\funcd{sched\_getaffinity}, il cui prototipo è: + +\begin{funcproto}{ +\fhead{sched.h} +\fdecl{int sched\_getaffinity (pid\_t pid, size\_t setsize, + cpu\_set\_t *mask)} +\fdesc{Legge la maschera di affinità di un processo.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual +caso \var{errno} assumerà uno dei valori: +\begin{errlist} +\item[\errcode{EINVAL}] \param{setsize} è più piccolo delle dimensioni + della maschera di affinità usata dal kernel. +\item[\errcode{ESRCH}] il processo \param{pid} non esiste. +\end{errlist} +ed inoltre anche \errval{EFAULT} nel suo significato generico.} +\end{funcproto} + +La funzione restituirà all'indirizzo specificato da \param{mask} il valore +della maschera di affinità del processo indicato dall'argomento \param{pid} +(al solito un valore nullo indica il processo corrente) così da poterla +riutilizzare per una successiva reimpostazione. + +È chiaro che queste funzioni per la gestione dell'affinità hanno significato +soltanto su un sistema multiprocessore, esse possono comunque essere +utilizzate anche in un sistema con un processore singolo, nel qual caso però +non avranno alcun risultato effettivo. + + +\itindend{scheduler} +\itindend{CPU~affinity} + + +\subsection{Le priorità per le operazioni di I/O} +\label{sec:io_priority} + +A lungo l'unica priorità usata per i processi è stata quella relativa +all'assegnazione dell'uso del processore. Ma il processore non è l'unica +risorsa che i processi devono contendersi, un'altra, altrettanto importante +per le prestazioni, è quella dell'accesso a disco. Per questo motivo nello +sviluppo del kernel sono stati introdotti diversi \textit{I/O scheduler} in +grado di distribuire in maniera opportuna questa risorsa ai vari processi. + +Fino al kernel 2.6.17 era possibile soltanto differenziare le politiche +generali di gestione, scegliendo di usare un diverso \textit{I/O scheduler}. A +partire da questa versione, con l'introduzione dello \textit{scheduler} CFQ +(\textit{Completely Fair Queuing}) è divenuto possibile, qualora si usi questo +\textit{scheduler}, impostare anche delle diverse priorità di accesso per i +singoli processi.\footnote{al momento (kernel 2.6.31), le priorità di I/O sono + disponibili soltanto per questo \textit{scheduler}.} + +La scelta di uno \textit{scheduler} di I/O si può fare in maniera generica per +tutto il sistema all'avvio del kernel con il parametro di avvio +\texttt{elevator},\footnote{per la trattazione dei parametri di avvio del + kernel si rimanda al solito alla sez.~5.3 di \cite{AGL}.} cui assegnare il +nome dello \textit{scheduler}, ma se ne può anche indicare uno specifico per +l'accesso al singolo disco scrivendo nel file +\texttt{/sys/block/\textit{}/queue/scheduler} (dove +\texttt{\textit{}} è il nome del dispositivo associato al disco). + +Gli \textit{scheduler} disponibili sono mostrati dal contenuto dello stesso +file che riporta fra parentesi quadre quello attivo, il default in tutti i +kernel recenti è proprio il \texttt{cfq},\footnote{nome con cui si indica + appunto lo \textit{scheduler} CFQ.} che supporta le priorità. Per i dettagli +sulle caratteristiche specifiche degli altri \textit{scheduler}, la cui +discussione attiene a problematiche di ambito sistemistico, si consulti la +documentazione nella directory \texttt{Documentation/block/} dei sorgenti del +kernel. + +Una volta che si sia impostato lo \textit{scheduler} CFQ ci sono due +specifiche \textit{system call}, specifiche di Linux, che consentono di +leggere ed impostare le priorità di I/O.\footnote{se usate in corrispondenza + ad uno \textit{scheduler} diverso il loro utilizzo non avrà alcun effetto.} +Dato che non esiste una interfaccia diretta nella \acr{glibc} per queste due +funzioni\footnote{almeno al momento della scrittura di questa sezione, con la + versione 2.11 della \acr{glibc}.} occorrerà invocarle tramite la funzione +\func{syscall} (come illustrato in sez.~\ref{sec:proc_syscall}). Le due +\textit{system call} sono \funcd{ioprio\_get} ed \funcd{ioprio\_set}; i +rispettivi prototipi sono: + +\begin{funcproto}{ +\fhead{linux/ioprio.h} +\fdecl{int ioprio\_get(int which, int who)} +\fdesc{Legge la priorità di I/O di un processo.} +\fdecl{int ioprio\_set(int which, int who, int ioprio)} +\fdesc{Imposta la priorità di I/O di un processo.} +} +{Le funzioni ritornano rispettivamente un intero positivo o 0 in caso di + successo e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei + valori: +\begin{errlist} +\item[\errcode{EINVAL}] i valori di \param{which} o di \param{ioprio} non + sono validi. +\item[\errcode{EPERM}] non si hanno i privilegi per eseguire + l'impostazione (solo per \func{ioprio\_set}). +\item[\errcode{ESRCH}] non esiste un processo corrispondente alle indicazioni. +\end{errlist}} +\end{funcproto} + +Le funzioni leggono o impostano la priorità di I/O sulla base dell'indicazione +dei due argomenti \param{which} e \param{who} che hanno lo stesso significato +già visto per gli omonimi argomenti di \func{getpriority} e +\func{setpriority}. Anche in questo caso si deve specificare il valore +di \param{which} tramite le opportune costanti riportate in +tab.~\ref{tab:ioprio_args} che consentono di indicare un singolo processo, i +processi di un \textit{process group} (tratteremo questo argomento in +sez.~\ref{sec:sess_proc_group}) o tutti i processi di un utente. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|c|c|l|} + \hline + \param{which} & \param{who} & \textbf{Significato} \\ + \hline + \hline + \const{IPRIO\_WHO\_PROCESS} & \type{pid\_t} & processo\\ + \const{IPRIO\_WHO\_PRGR} & \type{pid\_t} & \itindex{process~group} + \textit{process group}\\ + \const{IPRIO\_WHO\_USER} & \type{uid\_t} & utente\\ + \hline + \end{tabular} + \caption{Legenda del valore dell'argomento \param{which} e del tipo + dell'argomento \param{who} delle funzioni \func{ioprio\_get} e + \func{ioprio\_set} per le tre possibili scelte.} + \label{tab:ioprio_args} +\end{table} + +In caso di successo \func{ioprio\_get} restituisce un intero positivo che +esprime il valore della priorità di I/O, questo valore è una maschera binaria +composta da due parti, una che esprime la \textsl{classe} di +\textit{scheduling} di I/O del processo, l'altra che esprime, quando la classe +di \textit{scheduling} lo prevede, la priorità del processo all'interno della +classe stessa. Questo stesso formato viene utilizzato per indicare il valore +della priorità da impostare con l'argomento \param{ioprio} di +\func{ioprio\_set}. +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Macro} & \textbf{Significato}\\ + \hline + \hline + \macro{IOPRIO\_PRIO\_CLASS}\texttt{(\textit{value})} + & Dato il valore di una priorità come + restituito da \func{ioprio\_get} estrae il + valore della classe.\\ + \macro{IOPRIO\_PRIO\_DATA}\texttt{(\textit{value})} + & Dato il valore di una priorità come + restituito da \func{ioprio\_get} estrae il + valore della priorità.\\ + \macro{IOPRIO\_PRIO\_VALUE}\texttt{(\textit{class},\textit{prio})} + & Dato un valore di priorità ed una classe + ottiene il valore numerico da passare a + \func{ioprio\_set}.\\ + \hline + \end{tabular} + \caption{Le macro per la gestione dei valori numerici .} + \label{tab:IOsched_class_macro} +\end{table} + + +Per la gestione dei valori che esprimono le priorità di I/O sono state +definite delle opportune macro di preprocessore, riportate in +tab.~\ref{tab:IOsched_class_macro}. I valori delle priorità si ottengono o si +impostano usando queste macro. Le prime due si usano con il valore restituito +da \func{ioprio\_get} e per ottenere rispettivamente la classe di +\textit{scheduling}\footnote{restituita dalla macro con i valori di + tab.~\ref{tab:IOsched_class}.} e l'eventuale valore della priorità. La terza +macro viene invece usata per creare un valore di priorità da usare come +argomento di \func{ioprio\_set} per eseguire una impostazione. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Classe} & \textbf{Significato} \\ + \hline + \hline + \const{IOPRIO\_CLASS\_RT} & \textit{Scheduling} di I/O \textit{real-time}.\\ + \const{IOPRIO\_CLASS\_BE} & \textit{Scheduling} di I/O ordinario.\\ + \const{IOPRIO\_CLASS\_IDLE}& \textit{Scheduling} di I/O di priorità minima.\\ + \hline + \end{tabular} + \caption{Costanti che identificano le classi di \textit{scheduling} di I/O.} + \label{tab:IOsched_class} +\end{table} + +Le classi di \textit{scheduling} previste dallo \textit{scheduler} CFQ sono +tre, e ricalcano tre diverse modalità di distribuzione delle risorse analoghe +a quelle già adottate anche nel funzionamento dello \textit{scheduler} del +processore. Ciascuna di esse è identificata tramite una opportuna costante, +secondo quanto riportato in tab.~\ref{tab:IOsched_class}. + +La classe di priorità più bassa è \const{IOPRIO\_CLASS\_IDLE}; i processi in +questa classe riescono ad accedere a disco soltanto quando nessun altro +processo richiede l'accesso. Occorre pertanto usarla con molta attenzione, +perché un processo in questa classe può venire completamente bloccato quando +ci sono altri processi in una qualunque delle altre due classi che stanno +accedendo al disco. Quando si usa questa classe non ha senso indicare un +valore di priorità, dato che in questo caso non esiste nessuna gerarchia e la +priorità è identica, la minima possibile, per tutti i processi. + +La seconda classe di priorità di I/O è \const{IOPRIO\_CLASS\_BE} (il nome sta +per \textit{best-effort}) che è quella usata ordinariamente da tutti +processi. In questo caso esistono priorità diverse che consentono di +assegnazione di una maggiore banda passante nell'accesso a disco ad un +processo rispetto agli altri, con meccanismo simile a quello dei valori di +\textit{nice} in cui si evita che un processo a priorità più alta possa +bloccare indefinitamente quelli a priorità più bassa. In questo caso però le +diverse priorità sono soltanto otto, indicate da un valore numerico fra 0 e 7 +e come per \textit{nice} anche in questo caso un valore più basso indica una +priorità maggiore. + + +Infine la classe di priorità di I/O \textit{real-time} +\const{IOPRIO\_CLASS\_RT} ricalca le omonime priorità di processore: un +processo in questa classe ha sempre la precedenza nell'accesso a disco +rispetto a tutti i processi delle altre classi e di un processo nella stessa +classe ma con priorità inferiore, ed è pertanto in grado di bloccare +completamente tutti gli altri. Anche in questo caso ci sono 8 priorità diverse +con un valore numerico fra 0 e 7, con una priorità più elevata per valori più +bassi. + +In generale nel funzionamento ordinario la priorità di I/O di un processo +viene impostata in maniera automatica nella classe \const{IOPRIO\_CLASS\_BE} +con un valore ottenuto a partire dal corrispondente valore di \textit{nice} +tramite la formula: $\mathtt{\mathit{prio}}=(\mathtt{\mathit{nice}}+20)/5$. Un +utente ordinario può modificare con \func{ioprio\_set} soltanto le priorità +dei processi che gli appartengono,\footnote{per la modifica delle priorità di + altri processi occorrono privilegi amministrativi, ed in particolare la + capacità \const{CAP\_SYS\_NICE} (vedi sez.~\ref{sec:proc_capabilities}).} +cioè quelli il cui \ids{UID} reale corrisponde all'\ids{UID} reale o effettivo +del chiamante. Data la possibilità di ottenere un blocco totale del sistema, +solo l'amministratore\footnote{o un processo con la capacità + \const{CAP\_SYS\_ADMIN} (vedi sez.~\ref{sec:proc_capabilities}).} può +impostare un processo ad una priorità di I/O nella classe +\const{IOPRIO\_CLASS\_RT}, lo stesso privilegio era richiesto anche per la +classe \const{IOPRIO\_CLASS\_IDLE} fino al kernel 2.6.24, ma dato che in +questo caso non ci sono effetti sugli altri processi questo limite è stato +rimosso a partire dal kernel 2.6.25. + +%TODO verificare http://lwn.net/Articles/355987/ +\section{Funzioni di gestione avanzata} +\label{sec:proc_advanced_control} + +Nelle precedenti sezioni si sono trattate la gran parte delle funzioni che +attengono alla gestione ordinaria dei processi e delle loro proprietà più +comuni. Tratteremo qui alcune \textit{system call} dedicate alla gestione di +funzionalità dei processi molto specifiche ed avanzate, il cui uso è in genere +piuttosto ridotto. Trattandosi di problematiche abbastanza complesse, che +spesso presuppongono la conoscenza di altri argomenti trattati nel seguito +della guida, si può saltare questa sezione in una prima lettura, tornando su +di essa in un secondo tempo. + + +\subsection{La funzione \func{prctl}} +\label{sec:process_prctl} + +Benché la gestione ordinaria possa essere effettuata attraverso le funzioni +che abbiamo già esaminato nelle sezioni precedenti, esistono una serie di +proprietà e caratteristiche particolari dei processi non coperte da esse, per +la cui gestione è stata predisposta una apposita \textit{system call} che +fornisce una interfaccia generica per tutte le operazioni specialistiche. La +funzione di sistema è \funcd{prctl} ed il suo prototipo è:\footnote{la + funzione non è standardizzata ed è specifica di Linux, anche se ne esiste + una analoga in IRIX; è stata introdotta con il kernel 2.1.57.} + +\begin{funcproto}{ +\fhead{sys/prctl.h} +\fdecl{int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned + long arg4, \\ +\phantom{int prctl(}unsigned long arg5)} +\fdesc{Esegue una operazione speciale sul processo corrente.} +} +{La funzione ritorna $0$ o un valore positivo dipendente dall'operazione in + caso di successo e $-1$ per un errore, nel qual caso \var{errno} assumerà + valori diversi a seconda del tipo di operazione richiesta (in genere + \errval{EINVAL} o \errval{EPERM}).} +\end{funcproto} + +La funzione ritorna un valore nullo o positivo in caso di successo e $-1$ in +caso di errore; il significato degli argomenti della funzione successivi al +primo, il valore di ritorno in caso di successo, il tipo di errore restituito +in \var{errno} dipendono dall'operazione eseguita, indicata tramite il primo +argomento, \param{option}. Questo è un valore intero che identifica +l'operazione, e deve essere specificato con l'uso di una delle costanti +predefinite del seguente elenco, che illustra quelle disponibili al +momento:\footnote{alla stesura di questa sezione, cioè con il kernel 3.2.} + +\begin{basedescript}{\desclabelwidth{2.cm}\desclabelstyle{\nextlinelabel}} +\item[\const{PR\_CAPBSET\_READ}] Controlla la disponibilità di una delle + \itindex{capabilities} \textit{capabilities} (vedi + sez.~\ref{sec:proc_capabilities}). La funzione ritorna 1 se la capacità + specificata nell'argomento \param{arg2} (con una delle costanti di + tab.~\ref{tab:proc_capabilities}) è presente nel \textit{capabilities + bounding set} del processo e zero altrimenti, se \param{arg2} non è un + valore valido si avrà un errore di \errval{EINVAL}. Introdotta a partire + dal kernel 2.6.25. + +\item[\const{PR\_CAPBSET\_DROP}] Rimuove permanentemente una delle + \itindex{capabilities} \textit{capabilities} (vedi + sez.~\ref{sec:proc_capabilities}) dal processo e da tutti i suoi + discendenti. La funzione cancella la capacità specificata + nell'argomento \param{arg2} con una delle costanti di + tab.~\ref{tab:proc_capabilities} dal \textit{capabilities bounding set} + \itindex{capabilities~bounding~set} del processo. L'operazione richiede i + privilegi di amministratore (la capacità \const{CAP\_SETPCAP}), altrimenti + la chiamata fallirà con un errore di \errcode{EPERM}; se il valore + di \param{arg2} non è valido o se il supporto per le \textit{file + capabilities} non è stato compilato nel kernel la chiamata fallirà con un + errore di \errval{EINVAL}. Introdotta a partire dal kernel 2.6.25. + +\item[\const{PR\_SET\_DUMPABLE}] Imposta il flag che determina se la + terminazione di un processo a causa di un segnale per il quale è prevista la + generazione di un file di \itindex{core~dump} \textit{core dump} (vedi + sez.~\ref{sec:sig_standard}) lo genera effettivamente. In genere questo flag + viene attivato automaticamente, ma per evitare problemi di sicurezza (la + generazione di un file da parte di processi privilegiati può essere usata + per sovrascriverne altri) viene cancellato quando si mette in esecuzione un + programma con i bit \acr{suid} e \acr{sgid} attivi (vedi + sez.~\ref{sec:file_special_perm}) o con l'uso delle funzioni per la modifica + degli \ids{UID} dei processi (vedi sez.~\ref{sec:proc_setuid}). + + L'operazione è stata introdotta a partire dal kernel 2.3.20, fino al kernel + 2.6.12 e per i kernel successivi al 2.6.17 era possibile usare solo un + valore 0 di \param{arg2} per disattivare il flag ed un valore 1 per + attivarlo. Nei kernel dal 2.6.13 al 2.6.17 è stato supportato anche il + valore 2, che causava la generazione di un \itindex{core~dump} \textit{core + dump} leggibile solo dall'amministratore, ma questa funzionalità è stata + rimossa per motivi di sicurezza, in quanto consentiva ad un utente normale + di creare un file di \textit{core dump} appartenente all'amministratore in + directory dove l'utente avrebbe avuto permessi di accesso. + +\item[\const{PR\_GET\_DUMPABLE}] Ottiene come valore di ritorno della funzione + lo stato corrente del flag che controlla la effettiva generazione dei + \itindex{core~dump} \textit{core dump}. Introdotta a partire dal kernel + 2.3.20. + +\item[\const{PR\_SET\_ENDIAN}] Imposta la \itindex{endianness} + \textit{endianness} del processo chiamante secondo il valore fornito + in \param{arg2}. I valori possibili sono sono: \const{PR\_ENDIAN\_BIG} + (\textit{big endian}), \const{PR\_ENDIAN\_LITTLE} (\textit{little endian}), + e \const{PR\_ENDIAN\_PPC\_LITTLE} (lo pseudo \textit{little endian} del + PowerPC). Introdotta a partire dal kernel 2.6.18, solo per architettura + PowerPC. + +\item[\const{PR\_GET\_ENDIAN}] Ottiene il valore della \itindex{endianness} + \textit{endianness} del processo chiamante, salvato sulla variabile puntata + da \param{arg2} che deve essere passata come di tipo ``\ctyp{int + *}''. Introdotta a partire dal kernel 2.6.18, solo su PowerPC. + +\item[\const{PR\_SET\_FPEMU}] Imposta i bit di controllo per l'emulazione + della virgola mobile su architettura ia64, secondo il valore + di \param{arg2}, si deve passare \const{PR\_FPEMU\_NOPRINT} per emulare in + maniera trasparente l'accesso alle operazioni in virgola mobile, o + \const{PR\_FPEMU\_SIGFPE} per non emularle ed inviare il segnale + \signal{SIGFPE} (vedi sez.~\ref{sec:sig_prog_error}). Introdotta a partire + dal kernel 2.4.18, solo su architettura ia64. + +\item[\const{PR\_GET\_FPEMU}] Ottiene il valore dei flag di controllo + dell'emulazione della virgola mobile, salvato all'indirizzo puntato + da \param{arg2}, che deve essere di tipo ``\ctyp{int *}''. Introdotta a + partire dal kernel 2.4.18, solo su architettura ia64. + +\item[\const{PR\_SET\_FPEXC}] Imposta la modalità delle eccezioni in virgola + mobile (\textit{floating-point exception mode}) al valore di \param{arg2}. + I valori possibili sono: + \begin{itemize*} + \item \const{PR\_FP\_EXC\_SW\_ENABLE} per usare FPEXC per le eccezioni, + \item \const{PR\_FP\_EXC\_DIV} per la divisione per zero in virgola mobile, + \item \const{PR\_FP\_EXC\_OVF} per gli overflow, + \item \const{PR\_FP\_EXC\_UND} per gli underflow, + \item \const{PR\_FP\_EXC\_RES} per risultati non esatti, + \item \const{PR\_FP\_EXC\_INV} per operazioni invalide, + \item \const{PR\_FP\_EXC\_DISABLED} per disabilitare le eccezioni, + \item \const{PR\_FP\_EXC\_NONRECOV} per usare la modalità di eccezione + asincrona non recuperabile, + \item \const{PR\_FP\_EXC\_ASYNC} per usare la modalità di eccezione + asincrona recuperabile, + \item \const{PR\_FP\_EXC\_PRECISE} per la modalità precisa di + eccezione.\footnote{trattasi di gestione specialistica della gestione + delle eccezioni dei calcoli in virgola mobile che, i cui dettagli al + momento vanno al di là dello scopo di questo testo.} + \end{itemize*} +Introdotta a partire dal kernel 2.4.21, solo su PowerPC. + +\item[\const{PR\_GET\_FPEXC}] Ottiene il valore della modalità delle eccezioni + delle operazioni in virgola mobile, salvata all'indirizzo + puntato \param{arg2}, che deve essere di tipo ``\ctyp{int *}''. Introdotta + a partire dal kernel 2.4.21, solo su PowerPC. + +\item[\const{PR\_SET\_KEEPCAPS}] Consente di controllare quali + \itindex{capabilities} \textit{capabilities} vengono cancellate quando si + esegue un cambiamento di \ids{UID} del processo (per i dettagli si veda + sez.~\ref{sec:proc_capabilities}, in particolare quanto illustrato a + pag.~\pageref{sec:capability-uid-transition}). Un valore nullo (il default) + per \param{arg2} comporta che vengano cancellate, il valore 1 che vengano + mantenute, questo valore viene sempre cancellato attraverso una \func{exec}. + L'uso di questo flag è stato sostituito, a partire dal kernel 2.6.26, dal + flag \const{SECURE\_KEEP\_CAPS} dei \itindex{securebits} \textit{securebits} + (vedi l'uso di \const{PR\_SET\_SECUREBITS} più avanti). Introdotta a partire + dal kernel 2.2.18. + +\item[\const{PR\_GET\_KEEPCAPS}] Ottiene come valore di ritorno della funzione + il valore del flag di controllo delle \itindex{capabilities} + \textit{capabilities} impostato con \const{PR\_SET\_KEEPCAPS}. Introdotta a + partire dal kernel 2.2.18. + +\item[\const{PR\_SET\_NAME}] Imposta il nome del processo chiamante alla + stringa puntata da \param{arg2}, che deve essere di tipo ``\ctyp{char *}''. Il + nome può essere lungo al massimo 16 caratteri, e la stringa deve essere + terminata da NUL se più corta. Introdotta a partire dal kernel 2.6.9. + +\item[\const{PR\_GET\_NAME}] Ottiene il nome del processo chiamante nella + stringa puntata da \param{arg2}, che deve essere di tipo ``\ctyp{char *}''; + si devono allocare per questo almeno 16 byte, e il nome sarà terminato da + NUL se più corto. Introdotta a partire dal kernel 2.6.9. + +\item[\const{PR\_SET\_PDEATHSIG}] Consente di richiedere l'emissione di un + segnale, che sarà ricevuto dal processo chiamante, in occorrenza della + terminazione del proprio processo padre; in sostanza consente di invertire + il ruolo di \signal{SIGCHLD}. Il valore di \param{arg2} deve indicare il + numero del segnale, o 0 per disabilitare l'emissione. Il valore viene + automaticamente cancellato per un processo figlio creato con \func{fork}. + Introdotta a partire dal kernel 2.1.57. + +\item[\const{PR\_GET\_PDEATHSIG}] Ottiene il valore dell'eventuale segnale + emesso alla terminazione del padre, salvato all'indirizzo + puntato \param{arg2}, che deve essere di tipo ``\ctyp{int *}''. Introdotta a + partire dal kernel 2.3.15. + +\item[\const{PR\_SET\_SECCOMP}] Imposta il cosiddetto + \itindex{secure~computing~mode} \textit{secure computing mode} per il + processo corrente. Prevede come unica possibilità che \param{arg2} sia + impostato ad 1. Una volta abilitato il \itindex{secure~computing~mode} + \textit{secure computing mode} il processo potrà utilizzare soltanto un + insieme estremamente limitato di \textit{system call}: \func{read}, + \func{write}, \func{\_exit} e \funcm{sigreturn}. Ogni altra \textit{system + call} porterà all'emissione di un \signal{SIGKILL} (vedi + sez.~\ref{sec:sig_termination}). Il \textit{secure computing mode} è stato + ideato per fornire un supporto per l'esecuzione di codice esterno non fidato + e non verificabile a scopo di calcolo;\footnote{lo scopo è quello di poter + vendere la capacità di calcolo della proprio macchina ad un qualche + servizio di calcolo distribuito senza comprometterne la sicurezza + eseguendo codice non sotto il proprio controllo.} in genere i dati vengono + letti o scritti grazie ad un socket o una pipe, e per evitare problemi di + sicurezza non sono possibili altre operazioni se non quelle citate. + Introdotta a partire dal kernel 2.6.23, disponibile solo se si è abilitato + il supporto nel kernel con \texttt{CONFIG\_SECCOMP}. + +\item[\const{PR\_GET\_SECCOMP}] Ottiene come valore di ritorno della funzione + lo stato corrente del \textit{secure computing mode}, al momento attuale la + funzione è totalmente inutile in quanto l'unico valore ottenibile è 0, dato + che la chiamata di questa funzione in \itindex{secure~computing~mode} + \textit{secure computing mode} comporterebbe l'emissione di + \signal{SIGKILL}, è stata comunque definita per eventuali estensioni future. + Introdotta a partire dal kernel 2.6.23. + +\item[\const{PR\_SET\_SECUREBITS}] Imposta i \itindex{securebits} + \textit{securebits} per il processo chiamante al valore indicato + da \param{arg2}; per i dettagli sul significato dei \textit{securebits} si + veda sez.~\ref{sec:proc_capabilities}, ed in particolare i valori di + tab.~\ref{tab:securebits_values} e la relativa trattazione. L'operazione + richiede i privilegi di amministratore (la \itindex{capabilities} capacità + \const{CAP\_SETPCAP}), altrimenti la chiamata fallirà con un errore di + \errval{EPERM}. Introdotta a partire dal kernel 2.6.26. + +\item[\const{PR\_GET\_SECUREBITS}] Ottiene come valore di ritorno della + funzione l'impostazione corrente per i \itindex{securebits} + \textit{securebits}. Introdotta a partire dal kernel 2.6.26. + +\item[\const{PR\_SET\_TIMING}] Imposta il metodo di temporizzazione del + processo da indicare con il valore di \param{arg2}, attualmente i valori + possibili sono due, con \const{PR\_TIMING\_STATISTICAL} si usa il metodo + statistico tradizionale, con \const{PR\_TIMING\_TIMESTAMP} il più accurato + basato su dei \textit{timestamp}, quest'ultimo però non è ancora + implementato ed il suo uso comporta la restituzione di un errore di + \errval{EINVAL}. Introdotta a partire dal kernel 2.6.0-test4. + +\item[\const{PR\_GET\_TIMING}] Ottiene come valore di ritorno della funzione + il metodo di temporizzazione del processo attualmente in uso (uno dei due + valori citati per \const{PR\_SET\_TIMING}). Introdotta a partire dal kernel + 2.6.0-test4. + +\item[\const{PR\_SET\_TSC}] Imposta il flag che indica se il processo + chiamante può leggere il registro di processore contenente il contatore dei + \textit{timestamp} (TSC, o \textit{Time Stamp Counter}) da indicare con il + valore di \param{arg2}. Si deve specificare \const{PR\_TSC\_ENABLE} per + abilitare la lettura o \const{PR\_TSC\_SIGSEGV} per disabilitarla con la + generazione di un segnale di \signal{SIGSEGV} (vedi + sez.~\ref{sec:sig_prog_error}). La lettura viene automaticamente + disabilitata se si attiva il \itindex{secure~computing~mode} \textit{secure + computing mode}. Introdotta a partire dal kernel 2.6.26, solo su x86. + +\item[\const{PR\_GET\_TSC}] Ottiene il valore del flag che controlla la + lettura del contattore dei \textit{timestamp}, salvato all'indirizzo + puntato \param{arg2}, che deve essere di tipo ``\ctyp{int *}''. Introdotta a + partire dal kernel 2.6.26, solo su x86. +% articoli sul TSC e relativi problemi: http://lwn.net/Articles/209101/, +% http://blog.cr0.org/2009/05/time-stamp-counter-disabling-oddities.html, +% http://en.wikipedia.org/wiki/Time_Stamp_Counter + +\item[\const{PR\_SET\_UNALIGN}] Imposta la modalità di controllo per l'accesso + a indirizzi di memoria non allineati, che in varie architetture risultano + illegali, da indicare con il valore di \param{arg2}. Si deve specificare il + valore \const{PR\_UNALIGN\_NOPRINT} per ignorare gli accessi non allineati, + ed il valore \const{PR\_UNALIGN\_SIGBUS} per generare un segnale di + \signal{SIGBUS} (vedi sez.~\ref{sec:sig_prog_error}) in caso di accesso non + allineato. Introdotta con diverse versioni su diverse architetture. + +\item[\const{PR\_GET\_UNALIGN}] Ottiene il valore della modalità di controllo + per l'accesso a indirizzi di memoria non allineati, salvato all'indirizzo + puntato \param{arg2}, che deve essere di tipo \code{(int *)}. Introdotta con + diverse versioni su diverse architetture. +\item[\const{PR\_MCE\_KILL}] Imposta la politica di gestione degli errori + dovuti a corruzione della memoria per problemi hardware. Questo tipo di + errori vengono riportati dall'hardware di controllo della RAM e vengono + gestiti dal kernel,\footnote{la funzionalità è disponibile solo sulle + piattaforme più avanzate che hanno il supporto hardware per questo tipo di + controlli.} ma devono essere opportunamente riportati ai processi che + usano quella parte di RAM che presenta errori; nel caso specifico questo + avviene attraverso l'emissione di un segnale di \signal{SIGBUS} (vedi + sez.~\ref{sec:sig_prog_error}).\footnote{in particolare viene anche + impostato il valore di \var{si\_code} in \struct{siginfo\_t} a + \const{BUS\_MCEERR\_AO}; per il significato di tutto questo si faccia + riferimento alla trattazione di sez.~\ref{sec:sig_sigaction}.} + + Il comportamento di default prevede che per tutti i processi si applichi la + politica generale di sistema definita nel file + \sysctlfile{vm/memory\_failure\_early\_kill}, ma specificando + per \param{arg2} il valore \const{PR\_MCE\_KILL\_SET} è possibile impostare + con il contenuto di \param{arg3} una politica specifica del processo + chiamante. Si può tornare alla politica di default del sistema utilizzando + invece per \param{arg2} il valore \const{PR\_MCE\_KILL\_CLEAR}. In tutti i + casi, per compatibilità con eventuali estensioni future, tutti i valori + degli argomenti non utilizzati devono essere esplicitamente posti a zero, + pena il fallimento della chiamata con un errore di \errval{EINVAL}. + + In caso di impostazione di una politica specifica del processo con + \const{PR\_MCE\_KILL\_SET} i valori di \param{arg3} possono essere soltanto + due, che corrispondono anche al valore che si trova nell'impostazione + generale di sistema di \texttt{memory\_failure\_early\_kill}, con + \const{PR\_MCE\_KILL\_EARLY} si richiede l'emissione immediata di + \signal{SIGBUS} non appena viene rilevato un errore, mentre con + \const{PR\_MCE\_KILL\_LATE} il segnale verrà inviato solo quando il processo + tenterà un accesso alla memoria corrotta. Questi due valori corrispondono + rispettivamente ai valori 1 e 0 di + \texttt{memory\_failure\_early\_kill}.\footnote{in sostanza nel primo caso + viene immediatamente inviato il segnale a tutti i processi che hanno la + memoria corrotta mappata all'interno del loro spazio degli indirizzi, nel + secondo caso prima la pagina di memoria viene tolta dallo spazio degli + indirizzi di ciascun processo, mentre il segnale viene inviato solo quei + processi che tentano di accedervi.} Si può usare per \param{arg3} anche un + terzo valore, \const{PR\_MCE\_KILL\_DEFAULT}, che corrisponde a impostare + per il processo la politica di default.\footnote{si presume la politica di + default corrente, in modo da non essere influenzati da un eventuale + successivo cambiamento della stessa.} Introdotta a partire dal kernel + 2.6.32. +\item[\const{PR\_MCE\_KILL\_GET}] Ottiene come valore di ritorno della + funzione la politica di gestione degli errori dovuti a corruzione della + memoria. Tutti gli argomenti non utilizzati (al momento tutti) devono essere + nulli pena la ricezione di un errore di \errval{EINVAL}. Introdotta a + partire dal kernel 2.6.32. +\label{sec:prctl_operation} +\end{basedescript} + + + +\subsection{La \textit{system call} \func{clone}} +\label{sec:process_clone} + +La funzione tradizionale con cui creare un nuovo processo in un sistema +Unix-like, come illustrato in sez.~\ref{sec:proc_fork}, è \func{fork}, ma con +l'introduzione del supporto del kernel per i \textit{thread} (vedi +cap.~\ref{cha:threads}), si è avuta la necessità di una interfaccia che +consentisse un maggiore controllo sulla modalità con cui vengono creati nuovi +processi, che poi è stata utilizzata anche per fornire supporto per le +tecnologie di virtualizzazione dei processi (i cosiddetti \textit{container}). + +Per questo l'interfaccia per la creazione di un nuovo processo è stata +delegata ad una nuova \textit{system call}, \funcm{sys\_clone}, che consente +di reimplementare anche la tradizionale \func{fork}. In realtà in questo caso +più che di nuovi processi si può parlare della creazioni di nuovi +``\textit{task}'' del kernel che possono assumere la veste sia di un processo +classico isolato dagli altri come quelli trattati finora, che di un +\textit{thread} in cui la memoria viene condivisa fra il processo chiamante ed +il nuovo processo creato, come quelli che vedremo in +sez.~\ref{sec:linux_thread}. Per evitare confusione fra \textit{thread} e +processi ordinari, abbiamo deciso di usare la nomenclatura \textit{task} per +indicare la unità di esecuzione generica messa a disposizione del kernel che +\texttt{sys\_clone} permette di creare. + +Oltre a questo la funzione consente, ad uso delle nuove funzionalità di +virtualizzazione dei processi, di creare nuovi \textit{namespace} per una +serie di proprietà generali dei processi (come l'elenco dei \ids{PID}, +l'albero dei file, i \itindex{mount~point} \textit{mount point}, la rete, +ecc.), che consentono di creare gruppi di processi che vivono in una sorta di +spazio separato dagli altri, che costituisce poi quello che viene chiamato un +\textit{container}. + +La \textit{system call} richiede soltanto due argomenti: il +primo, \param{flags}, consente di controllare le modalità di creazione del +nuovo \textit{task}, il secondo, \param{child\_stack}, imposta l'indirizzo +dello \itindex{stack} \textit{stack} per il nuovo \textit{task}, e deve essere +indicato quando si intende creare un \textit{thread}. L'esecuzione del +programma creato da \func{sys\_clone} riprende, come per \func{fork}, da +dopo l'esecuzione della stessa. + +La necessità di avere uno \itindex{stack} \textit{stack} alternativo c'è solo +quando si intende creare un \textit{thread}, in tal caso infatti il nuovo +\textit{task} vede esattamente la stessa memoria del \textit{task} +``\textsl{padre}'',\footnote{in questo caso per padre si intende semplicemente + il \textit{task} che ha eseguito \func{sys\_clone} rispetto al \textit{task} + da essa creato, senza nessuna delle implicazioni che il concetto ha per i + processi.} e nella sua esecuzione alla prima chiamata di una funzione +andrebbe a scrivere sullo \textit{stack} usato anche dal padre (si ricordi +quanto visto in sez.~\ref{sec:proc_mem_layout} riguardo all'uso dello +\textit{stack}). + +Per evitare di doversi garantire contro la evidente possibilità di +\itindex{race~condition} \textit{race condition} che questa situazione +comporta (vedi sez.~\ref{sec:proc_race_cond} per una spiegazione della +problematica) è necessario che il chiamante allochi preventivamente un'area di +memoria. In genere lo si fa con una \func{malloc} che allochi un buffer che +la funzione imposterà come \textit{stack} del nuovo processo, avendo +ovviamente cura di non utilizzarlo direttamente nel processo chiamante. + +In questo modo i due \textit{task} avranno degli \textit{stack} indipendenti e +non si dovranno affrontare problematiche di \itindex{race~condition} +\textit{race condition}. Si tenga presente inoltre che in molte architetture +di processore lo \textit{stack} cresce verso il basso, pertanto in tal caso +non si dovrà specificare per \param{child\_stack} il puntatore restituito da +\func{malloc}, ma un puntatore alla fine del buffer da essa allocato. + +Dato che tutto ciò è necessario solo per i \textit{thread} che condividono la +memoria, la \textit{system call}, a differenza della funzione di libreria che +vedremo a breve, consente anche di passare per \param{child\_stack} il valore +\val{NULL}, che non imposta un nuovo \textit{stack}. Se infatti si crea un +processo, questo ottiene un suo nuovo spazio degli indirizzi,\footnote{è + sottinteso cioè che non si stia usando il flag \const{CLONE\_VM} che vedremo + a breve.} ed in questo caso si applica la semantica del +\itindex{copy~on~write} \textit{copy on write} illustrata in +sez.~\ref{sec:proc_fork}, per cui le pagine dello \textit{stack} verranno +automaticamente copiate come le altre e il nuovo processo avrà un suo +\textit{stack} totalmente indipendente da quello del padre. + +Dato che l'uso principale della nuova \textit{system call} è quello relativo +alla creazione dei \textit{thread}, la \acr{glibc} definisce una funzione di +libreria con una sintassi diversa, orientata a questo scopo, e la +\textit{system call} resta accessibile solo se invocata esplicitamente come +visto in sez.~\ref{sec:proc_syscall}.\footnote{ed inoltre per questa + \textit{system call} non è disponibile la chiamata veloce con + \texttt{vsyscall}.} La funzione di libreria si chiama semplicemente +\funcd{clone} ed il suo prototipo è: + +\begin{funcproto}{ +\fhead{sched.h} +\fdecl{int clone(int (*fn)(void *), void *child\_stack, int flags, void *arg, + ... \\ +\phantom{int clone(}/* pid\_t *ptid, struct user\_desc *tls, pid\_t *ctid */ )} +\fdesc{Crea un nuovo processo o \textit{thread}.} +} +{La funzione ritorna il \textit{Thread ID} assegnato al nuovo processo in caso + di successo e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei + valori: +\begin{errlist} + \item[\errcode{EAGAIN}] sono già in esecuzione troppi processi. + \item[\errcode{EINVAL}] si è usata una combinazione non valida di flag o + un valore nullo per \param{child\_stack}. + \item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare una nuova + \struct{task\_struct} o per copiare le parti del contesto del chiamante + necessarie al nuovo \textit{task}. + \item[\errcode{EPERM}] non si hanno i privilegi di amministratore + richiesti dai flag indicati. +\end{errlist}} +\end{funcproto} + +% NOTE: una pagina con la descrizione degli argomenti: +% * http://www.lindevdoc.org/wiki/Clone + +La funzione prende come primo argomento \param{fn} il puntatore alla funzione +che verrà messa in esecuzione nel nuovo processo, che può avere un unico +argomento di tipo puntatore a \ctyp{void}, il cui valore viene passato dal +terzo argomento \param{arg}. Per quanto il precedente prototipo possa +intimidire nella sua espressione, in realtà l'uso è molto semplice basterà +definire una qualunque funzione \param{fn} che restituisce un intero ed ha +come argomento un puntatore a \ctyp{void}, e \code{fn(arg)} sarà eseguita in +un nuovo processo. + +Il nuovo processo resterà in esecuzione fintanto che la funzione \param{fn} +non ritorna, o esegue \func{exit} o viene terminata da un segnale. Il valore +di ritorno della funzione (o quello specificato con \func{exit}) verrà +utilizzato come stato di uscita della funzione. I tre +argomenti \param{ptid}, \param{tls} e \param{ctid} sono opzionali e sono +presenti solo a partire dal kernel 2.6 e sono stati aggiunti come supporto per +le funzioni di gestione dei \textit{thread} (la \textit{Native Thread Posix + Library}, vedi sez.~\ref{sec:linux_ntpl}) nella \acr{glibc}, essi vengono +utilizzati soltanto se si sono specificati rispettivamente i flag +\const{CLONE\_PARENT\_SETTID}, \const{CLONE\_SETTLS} e +\const{CLONE\_CHILD\_SETTID}. + +La funzione ritorna un l'identificatore del nuovo \textit{task}, denominato +\texttt{Thread ID} (da qui in avanti \ids{TID}) il cui significato è analogo +al \ids{PID} dei normali processi e che a questo corrisponde qualora si crei +un processo. + +Il comportamento di \func{clone}, che si riflette sulle caratteristiche del +nuovo processo da essa creato, è controllato principalmente +dall'argomento \param{flags}, che deve essere specificato come maschera +binaria, ottenuta con un OR aritmetico di una delle costanti del seguente +elenco, che illustra quelle attualmente disponibili:\footnote{si fa + riferimento al momento della stesura di questa sezione, cioè con il kernel + 3.2.} + +\begin{basedescript}{\desclabelwidth{2.cm}\desclabelstyle{\nextlinelabel}} + +\item[\const{CLONE\_CHILD\_CLEARTID}] cancella il valore del \ids{TID} +\item[\const{CLONE\_CHILD\_SETTID}] +\item[\const{CLONE\_FILES}] +\item[\const{CLONE\_FS}] +\item[\const{CLONE\_IO}] +\item[\const{CLONE\_NEWIPC}] +\item[\const{CLONE\_NEWNET}] +\item[\const{CLONE\_NEWNS}] +\item[\const{CLONE\_NEWPID}] +\item[\const{CLONE\_NEWUTS}] +\item[\const{CLONE\_PARENT}] +\item[\const{CLONE\_PARENT\_SETTID}] +\item[\const{CLONE\_PID}] +\item[\const{CLONE\_PTRACE}] +\item[\const{CLONE\_SETTLS}] +\item[\const{CLONE\_SIGHAND}] +\item[\const{CLONE\_STOPPED}] +\item[\const{CLONE\_SYSVSEM}] +\item[\const{CLONE\_THREAD}] +\item[\const{CLONE\_UNTRACED}] +\item[\const{CLONE\_VFORK}] +\item[\const{CLONE\_VM}] +\end{basedescript} + + +%TODO trattare unshare + + +\subsection{La funzione \func{ptrace}} +\label{sec:process_ptrace} + +Da fare + +% TODO: trattare PTRACE_SEIZE, aggiunta con il kernel 3.1 + + +\subsection{La gestione delle operazioni in virgola mobile} +\label{sec:process_fenv} + +Da fare. + +% TODO eccezioni ed arrotondamenti per la matematica in virgola mobile +% consultare la manpage di fenv, math_error, fpclassify, matherr, isgreater, +% isnan, nan, INFINITY + + +\subsection{L'accesso alle porte di I/O} +\label{sec:process_io_port} + +% +% TODO l'I/O sulle porte di I/O +% consultare le manpage di ioperm, iopl e outb +% non c'entra nulla qui, va trovato un altro posto (altri meccanismi di I/O in +% fileintro ?) + +Da fare + + +%\subsection{La gestione di architetture a nodi multipli} +%\label{sec:process_NUMA} + +% TODO trattare i cpuset, che attiene anche a NUMA, e che possono essere usati +% per associare l'uso di gruppi di processori a gruppi di processi (vedi +% manpage omonima) +% TODO trattare getcpu, che attiene anche a NUMA, mettere qui anche +% sched_getcpu, che potrebbe essere indipendente ma richiama getcpu + +%TODO trattare le funzionalità per il NUMA +% vedi man numa e, mbind, get_mempolicy, set_mempolicy, +% le pagine di manuale relative +% vedere anche dove metterle... + + +\section{Problematiche di programmazione multitasking} +\label{sec:proc_multi_prog} + +Benché i processi siano strutturati in modo da apparire il più possibile come +indipendenti l'uno dall'altro, nella programmazione in un sistema multitasking +occorre tenere conto di una serie di problematiche che normalmente non +esistono quando si ha a che fare con un sistema in cui viene eseguito un solo +programma alla volta. + +Per questo motivo, essendo questo argomento di carattere generale, ci è parso +opportuno introdurre sinteticamente queste problematiche, che ritroveremo a +più riprese in capitoli successivi, in questa sezione conclusiva del capitolo +in cui abbiamo affrontato la gestione dei processi, sottolineando come esse +diventino cogenti quando invece si usano i \textit{thread}. + + +\subsection{Le operazioni atomiche} +\label{sec:proc_atom_oper} + +La nozione di \textsl{operazione atomica} deriva dal significato greco della +parola atomo, cioè indivisibile; si dice infatti che un'operazione è atomica +quando si ha la certezza che, qualora essa venga effettuata, tutti i passaggi +che devono essere compiuti per realizzarla verranno eseguiti senza possibilità +di interruzione in una fase intermedia. + +In un ambiente multitasking il concetto è essenziale, dato che un processo può +essere interrotto in qualunque momento dal kernel che mette in esecuzione un +altro processo o dalla ricezione di un segnale. Occorre pertanto essere +accorti nei confronti delle possibili \itindex{race~condition} \textit{race + condition} (vedi sez.~\ref{sec:proc_race_cond}) derivanti da operazioni +interrotte in una fase in cui non erano ancora state completate. + +Nel caso dell'interazione fra processi la situazione è molto più semplice, ed +occorre preoccuparsi della atomicità delle operazioni solo quando si ha a che +fare con meccanismi di intercomunicazione (che esamineremo in dettaglio in +cap.~\ref{cha:IPC}) o nelle operazioni con i file (vedremo alcuni esempi in +sez.~\ref{sec:file_shared_access}). In questi casi in genere l'uso delle +appropriate funzioni di libreria per compiere le operazioni necessarie è +garanzia sufficiente di atomicità in quanto le \textit{system call} con cui +esse sono realizzate non possono essere interrotte (o subire interferenze +pericolose) da altri processi. + +Nel caso dei segnali invece la situazione è molto più delicata, in quanto lo +stesso processo, e pure alcune \textit{system call}, possono essere interrotti +in qualunque momento, e le operazioni di un eventuale \textit{signal handler} +sono compiute nello stesso spazio di indirizzi del processo. Per questo, anche +il solo accesso o l'assegnazione di una variabile possono non essere più +operazioni atomiche (torneremo su questi aspetti in +sez.~\ref{sec:sig_adv_control}). + +Qualora invece si usino i \textit{thread}, in cui lo spazio degli indirizzi è +condiviso, il problema è sempre presente, perché qualunque \textit{thread} può +interromperne un altro in qualunque momento e l'atomicità di qualunque +operazione è messa in discussione, per cui l'assenza di eventuali +\itindex{race~condition} \textit{race condition} deve essere sempre verificata +nei minimi dettagli. + +In questo caso il sistema provvede un tipo di dato, il \type{sig\_atomic\_t}, +il cui accesso è assicurato essere atomico. In pratica comunque si può +assumere che, in ogni piattaforma su cui è implementato Linux, il tipo +\ctyp{int}, gli altri interi di dimensione inferiore ed i puntatori sono +atomici. Non è affatto detto che lo stesso valga per interi di dimensioni +maggiori (in cui l'accesso può comportare più istruzioni in assembler) o per +le strutture di dati. In tutti questi casi è anche opportuno marcare come +\direct{volatile} le variabili che possono essere interessate ad accesso +condiviso, onde evitare problemi con le ottimizzazioni del codice. + + + +\subsection{Le \textit{race condition} ed i \textit{deadlock}} +\label{sec:proc_race_cond} + +\itindbeg{race~condition} + +Si definiscono \textit{race condition} tutte quelle situazioni in cui processi +diversi operano su una risorsa comune, ed in cui il risultato viene a +dipendere dall'ordine in cui essi effettuano le loro operazioni. Il caso +tipico è quello di un'operazione che viene eseguita da un processo in più +passi, e può essere compromessa dall'intervento di un altro processo che +accede alla stessa risorsa quando ancora non tutti i passi sono stati +completati. + +Dato che in un sistema multitasking ogni processo può essere interrotto in +qualunque momento per farne subentrare un altro in esecuzione, niente può +assicurare un preciso ordine di esecuzione fra processi diversi o che una +sezione di un programma possa essere eseguita senza interruzioni da parte di +altri. Queste situazioni comportano pertanto errori estremamente subdoli e +difficili da tracciare, in quanto nella maggior parte dei casi tutto +funzionerà regolarmente, e solo occasionalmente si avranno degli errori. + +Per questo occorre essere ben consapevoli di queste problematiche, e del fatto +che l'unico modo per evitarle è quello di riconoscerle come tali e prendere +gli adeguati provvedimenti per far sì che non si verifichino. Casi tipici di +\textit{race condition} si hanno quando diversi processi accedono allo stesso +file, o nell'accesso a meccanismi di intercomunicazione come la memoria +condivisa. + +In questi casi, se non si dispone della possibilità di eseguire atomicamente +le operazioni necessarie, occorre che quelle parti di codice in cui si +compiono le operazioni sulle risorse condivise (le cosiddette +\index{sezione~critica} \textsl{sezioni critiche}) del programma, siano +opportunamente protette da meccanismi di sincronizzazione (torneremo su queste +problematiche di questo tipo in cap.~\ref{cha:IPC}). + +Nel caso dei \textit{thread} invece la situazione è molto più delicata e +sostanzialmente qualunque accesso in memoria (a buffer, variabili o altro) può +essere soggetto a \textit{race condition} dato potrebbe essere interrotto in +qualunque momento da un altro \textit{thread}. In tal caso occorre pianificare +con estrema attenzione l'uso delle variabili ed utilizzare i vari meccanismi +di sincronizzazione che anche in questo caso sono disponibili (torneremo su +queste problematiche di questo tipo in cap.~\ref{sez:thread_xxx}) + +\itindbeg{deadlock} Un caso particolare di \textit{race condition} sono poi i +cosiddetti \textit{deadlock} (traducibile in \textsl{condizione di stallo}), +che particolarmente gravi in quanto comportano spesso il blocco completo di un +servizio, e non il fallimento di una singola operazione. Per definizione un +\textit{deadlock} è una situazione in cui due o più processi non sono più in +grado di proseguire perché ciascuno aspetta il risultato di una operazione che +dovrebbe essere eseguita dall'altro. + +L'esempio tipico di una situazione che può condurre ad un +\textit{deadlock} è quello in cui un flag di +``\textsl{occupazione}'' viene rilasciato da un evento asincrono (come un +segnale o un altro processo) fra il momento in cui lo si è controllato +(trovandolo occupato) e la successiva operazione di attesa per lo sblocco. In +questo caso, dato che l'evento di sblocco del flag è avvenuto senza che ce ne +accorgessimo proprio fra il controllo e la messa in attesa, quest'ultima +diventerà perpetua (da cui il nome di \textit{deadlock}). + +In tutti questi casi è di fondamentale importanza il concetto di atomicità +visto in sez.~\ref{sec:proc_atom_oper}; questi problemi infatti possono essere +risolti soltanto assicurandosi, quando essa sia richiesta, che sia possibile +eseguire in maniera atomica le operazioni necessarie. + +\itindend{race~condition} +\itindend{deadlock} + + +\subsection{Le funzioni rientranti} +\label{sec:proc_reentrant} + +\index{funzioni!rientranti|(} + +Si dice \textsl{rientrante} una funzione che può essere interrotta in +qualunque punto della sua esecuzione ed essere chiamata una seconda volta da +un altro \itindex{thread} \textit{thread} di esecuzione senza che questo +comporti nessun problema nell'esecuzione della stessa. La problematica è +comune nella programmazione \itindex{thread} \textit{multi-thread}, ma si +hanno gli stessi problemi quando si vogliono chiamare delle funzioni +all'interno dei gestori dei segnali. + +Fintanto che una funzione opera soltanto con le variabili locali è rientrante; +queste infatti vengono allocate nello \itindex{stack} \textit{stack}, ed +un'altra invocazione non fa altro che allocarne un'altra copia. Una funzione +può non essere rientrante quando opera su memoria che non è nello +\itindex{stack} \textit{stack}. Ad esempio una funzione non è mai rientrante +se usa una \index{variabili!globali} variabile globale o +\index{variabili!statiche} statica. + +Nel caso invece la funzione operi su un oggetto allocato dinamicamente, la +cosa viene a dipendere da come avvengono le operazioni: se l'oggetto è creato +ogni volta e ritornato indietro la funzione può essere rientrante, se invece +esso viene individuato dalla funzione stessa due chiamate alla stessa funzione +potranno interferire quando entrambe faranno riferimento allo stesso oggetto. +Allo stesso modo una funzione può non essere rientrante se usa e modifica un +oggetto che le viene fornito dal chiamante: due chiamate possono interferire +se viene passato lo stesso oggetto; in tutti questi casi occorre molta cura da +parte del programmatore. + +In genere le funzioni di libreria non sono rientranti, molte di esse ad +esempio utilizzano \index{variabili!statiche} variabili statiche, la +\acr{glibc} però mette a disposizione due macro di compilatore, +\macro{\_REENTRANT} e \macro{\_THREAD\_SAFE}, la cui definizione attiva le +versioni rientranti di varie funzioni di libreria, che sono identificate +aggiungendo il suffisso \code{\_r} al nome della versione normale. + +\index{funzioni!rientranti|)} -\subsection{Le funzioni \texttt{seteuid} e \texttt{setegid}} -\label{sec:proc_seteuid} +% LocalWords: multitasking like VMS child process identifier pid sez shell fig +% LocalWords: parent kernel init pstree keventd kswapd table struct linux call +% 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: 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: SIGABRT SIGCHLD SIGHUP foreground SIGCONT termination signal ANY +% LocalWords: handler kill EINTR POSIX options WNOHANG ECHILD option WUNTRACED +% LocalWords: dump bits rusage getrusage heap const filename argv envp EACCES +% 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 pending utime cutime ustime fcntl linker +% 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: getegid IDS NFS setuid setgid all' logout utmp screen xterm TODO +% LocalWords: setreuid setregid FIXME ruid rgid seteuid setegid setresuid size +% LocalWords: setresgid getresuid getresgid value result argument setfsuid DAC +% LocalWords: setfsgid NGROUPS sysconf getgroups getgrouplist groups ngroups +% LocalWords: setgroups initgroups patch LIDS CHOWN OVERRIDE Discrectionary PF +% LocalWords: SEARCH chattr sticky NOATIME socket domain immutable append mmap +% LocalWords: broadcast multicast multicasting memory locking mlock mlockall +% 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: 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: min getparam getscheduler interval robin ENOSYS fifo ping long +% LocalWords: affinity setaffinity unsigned mask cpu NUMA CLR ISSET SETSIZE RR +% LocalWords: getaffinity assembler deadlock REENTRANT SAFE tgz MYPGRP l'OR rr +% LocalWords: WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG WCOREDUMP WIFSTOPPED +% LocalWords: WSTOPSIG opt char INTERP arg SIG IGN DFL mascheck grp FOWNER RAW +% LocalWords: FSETID SETPCAP BIND SERVICE ADMIN PACKET IPC OWNER MODULE RAWIO +% LocalWords: PACCT RESOURCE TTY CONFIG SETFCAP hdrp datap libcap lcap text tp +% LocalWords: get ncap caps CapInh CapPrm fffffeff CapEff getcap STAT dall'I +% LocalWords: inc PRIO SUSv PRGR prio SysV SunOS Ultrix sched timespec len sig +% LocalWords: cpusetsize cpuset atomic tickless redirezione WCONTINUED stopped +% LocalWords: waitid NOCLDSTOP ENOCHLD WIFCONTINUED ifdef endif idtype siginfo +% LocalWords: infop ALL WEXITED WSTOPPED WNOWAIT signo CLD EXITED KILLED page +% LocalWords: CONTINUED sources forking Spawned successfully executing exiting +% LocalWords: next cat for COMMAND pts bash defunct TRAPPED DUMPED PR effects +% LocalWords: SIGKILL static RLIMIT preemption PREEMPT VOLUNTARY IDLE RTPRIO +% LocalWords: completely fair compat uniform CFQ queuing elevator dev cfq RT +% LocalWords: documentation block syscall ioprio IPRIO CLASS class best effort +% LocalWords: refresh semop dnotify MADV DONTFORK prctl WCLONE WALL big mount +% LocalWords: WNOTHREAD DUMPABLE KEEPCAPS IRIX CAPBSET endianness endian flags +% LocalWords: little PPC PowerPC FPEMU NOPRINT SIGFPE FPEXC point FP SW malloc +% LocalWords: exception EXC ENABLE OVF overflow UND underflow RES INV DISABLED +% LocalWords: NONRECOV ASYNC KEEP securebits NAME NUL PDEATHSIG SECCOMP VM FS +% LocalWords: secure computing sigreturn TIMING STATISTICAL TSC MCE conditions +% LocalWords: timestamp Stamp SIGSEGV UNALIGN SIGBUS MCEERR AO failure early +% LocalWords: namespace vsyscall SETTID FILES NEWIPC NEWNET NEWNS NEWPID ptid +% 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 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "gapil" +%%% End: