X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;f=prochand.tex;h=86c7a7d6b1a63c2e712746287e0f4788dbbc42f7;hb=4cbeb0e4fa1d31da798c8e68108eb6785586ab34;hp=d476db8c3ea5ccdfc18d719bcb76b7f50b23cd01;hpb=645d4b1cd40dae38150acba000c434636f89e862;p=gapil.git diff --git a/prochand.tex b/prochand.tex index d476db8..5c526df 100644 --- a/prochand.tex +++ b/prochand.tex @@ -1,62 +1,75 @@ +%% prochand.tex +%% +%% Copyright (C) 2000-2011 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. - -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. Infine nella sezione finale affronteremo alcune -problematiche generiche della programmazione in ambiente multitasking. +Come accennato nell'introduzione in un sistema Unix 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. \section{Introduzione} \label{sec:proc_gen} -Partiremo con una introduzione generale ai concetti che stanno alla base della +Inizieremo con un'introduzione generale ai concetti che stanno alla base della gestione dei processi in un sistema unix-like. 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. +caratteristiche, dando una panoramica sull'uso delle principali funzioni di +gestione. -\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 cosiddetto \textit{process identifier} o, più brevemente, -\acr{pid}. - -Una seconda caratteristica di un sistema unix è 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 -eseguirà, in un passo successivo, il programma voluto: questo è ad esempio +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 univoco, il cosiddetto \textit{process identifier} o, più brevemente, +\acr{pid}, assegnato in forma progressiva (vedi sez.~\ref{sec:proc_pid}) +quando il processo viene creato. + +Una seconda caratteristica di un sistema Unix è 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 è che ogni processo è sempre stato generato da un +Una terza caratteristica è che ogni processo è sempre stato generato da un altro, che viene chiamato 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 è +punto di partenza esiste un processo speciale (che normalmente è \cmd{/sbin/init}), che viene lanciato dal kernel alla conclusione della fase di avvio; essendo questo il primo processo lanciato dal sistema ha sempre il -\acr{pid} uguale a 1 e non è figlio di nessun altro processo. +\acr{pid} uguale a 1 e non è figlio di nessun altro processo. -Ovviamente \cmd{init} è un processo speciale che in genere si occupa di far +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 +inoltre \cmd{init} è essenziale per svolgere una serie di compiti amministrativi nelle operazioni ordinarie del sistema (torneremo su alcuni di -essi in \secref{sec:proc_termination}) e non può mai essere terminato. La +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 +\cmd{init} si fosse corrotto) è ad esempio possibile lanciare una shell al suo posto, passando la riga \cmd{init=/bin/sh} come parametro di avvio. \begin{figure}[!htb] @@ -95,61 +108,111 @@ init-+-keventd |-snort `-wwwoffled \end{verbatim} %$ - \caption{L'albero dei processi, così come riportato dal comando + \caption{L'albero dei processi, così come riportato dal comando \cmd{pstree}.} \label{fig:proc_tree} \end{figure} Dato che tutti i processi attivi nel sistema sono comunque generati da -\cmd{init} o da uno dei suoi figli\footnote{in realtà questo non è del tutto - vero, in Linux ci sono alcuni processi che pur comparendo come figli di - init, o con \acr{pid} successivi, sono in realtà generati direttamente dal - kernel, (come \cmd{keventd}, \cmd{kswapd}, etc.)} si possono classificare i -processi con la relazione padre/figlio in una organizzazione gerarchica ad -albero, in maniera analoga a come i file sono organizzati in un albero di -directory (si veda \secref{sec:file_file_struct}); in \curfig\ si è mostrato il -risultato del comando \cmd{pstree} che permette di mostrare questa struttura, -alla cui base c'è \cmd{init} che è progenitore di tutti gli altri processi. - - -\subsection{Una panoramica sulle funzioni di gestione} +\cmd{init} o da uno dei suoi figli\footnote{in realtà questo non è del tutto + vero, in Linux ci sono alcuni processi speciali che pur comparendo come + figli di \cmd{init}, o con \acr{pid} successivi, sono in realtà generati + direttamente dal kernel, (come \cmd{keventd}, \cmd{kswapd}, ecc.).} si +possono classificare i processi con la relazione padre/figlio in +un'organizzazione gerarchica ad albero, in maniera analoga a come i file sono +organizzati in un albero di directory (si veda +sez.~\ref{sec:file_organization}); 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. + +Il kernel mantiene una tabella dei processi attivi, la cosiddetta +\itindex{process~table} \textit{process table}; per ciascun processo viene +mantenuta una voce, costituita da una struttura \struct{task\_struct}, nella +tabella dei processi che contiene tutte le informazioni rilevanti per quel +processo. Tutte le strutture usate a questo scopo sono dichiarate nell'header +file \file{linux/sched.h}, ed uno schema semplificato, che riporta la +struttura delle principali informazioni 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 system call ed ad ogni interrupt,\footnote{più 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.\footnote{a partire dal kernel 2.6.21 è stato + introdotto (a cura di Ingo Molnar) 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.} + +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{Una panoramica sulle funzioni fondamentali} \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}). +Tradizionalmente in un sistema unix-like i processi vengono sempre creati da +altri processi tramite la funzione \func{fork}; il nuovo processo (che viene +chiamato \textsl{figlio}) creato dalla \func{fork} è una copia identica del +processo processo originale (detto \textsl{padre}), ma ha un nuovo \acr{pid} e +viene eseguito in maniera indipendente (le differenze fra padre e figlio sono +affrontate in dettaglio in sez.~\ref{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} (si veda -\secref{sec:proc_wait}); queste funzioni restituiscono anche una informazione -abbastanza limitata (lo stato di terminazione) sulle cause della terminazione -del processo. +sez.~\ref{sec:proc_wait}); queste funzioni restituiscono anche un'informazione +abbastanza limitata sulle cause della terminazione del processo figlio. 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_conclusion}). 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. +risolvibile esso può essere terminato con la funzione \func{exit} (si veda +quanto discusso in sez.~\ref{sec:proc_conclusion}). La vita del processo però +termina completamente 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 +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 è +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}. +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. +caricare un altro programma da disco sostituendo quest'ultimo all'immagine +corrente; questo fa sì che l'immagine precedente venga completamente +cancellata. Questo significa che quando il nuovo programma termina, 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 @@ -157,232 +220,230 @@ 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} +\section{Le funzioni di base}% della gestione dei processi} \label{sec:proc_handling} -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 loro creazione, terminazione, e la messa in esecuzione di altri +In questa sezione tratteremo le problematiche della gestione dei processi +all'interno del sistema, illustrandone tutti i dettagli. Inizieremo con 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. \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. +Come accennato nell'introduzione, ogni processo viene identificato dal sistema +da un numero identificativo univoco, 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 \acr{glibc} il tipo usato è +\ctyp{int}). + +Il \acr{pid} viene assegnato in forma progressiva\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 \acr{pid}, + \acr{pgid} o \acr{sid} (vedi sez.~\ref{sec:sess_proc_group}).} ogni volta +che un nuovo processo viene creato, fino ad un limite che, essendo il +\acr{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, sono definiti dalla macro \const{PID\_MAX} in + \file{threads.h} e direttamente in \file{fork.c}, con il kernel 2.5.x e la + nuova interfaccia per i \itindex{thread} \textit{thread} creata da Ingo + Molnar anche il meccanismo di allocazione dei \acr{pid} è stato modificato; + il valore massimo è impostabile attraverso il file + \procfile{/proc/sys/kernel/pid\_max} e di default vale 32768.} che serve a +riservare i \acr{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 \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}). Questi due identificativi possono essere -ottenuti da programma usando le funzioni: +\textit{parent process ID}). Questi due identificativi possono essere +ottenuti usando le due funzioni \funcd{getpid} e \funcd{getppid}, i cui +prototipi sono: \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. + \headdecl{sys/types.h} + \headdecl{unistd.h} + \funcdecl{pid\_t getpid(void)} + + Restituisce il \acr{pid} del processo corrente. + + \funcdecl{pid\_t getppid(void)} + + Restituisce il \acr{pid} del padre del processo corrente. -Entrambe le funzioni non riportano condizioni di errore. +\bodydesc{Entrambe le funzioni non riportano condizioni di errore.} \end{functions} \noindent esempi dell'uso di queste funzioni sono riportati in -\figref{fig:proc_fork_code}, nel programma di esempio \file{ForkTest.c}. +fig.~\ref{fig:proc_fork_code}, nel programma \file{ForkTest.c}. -Il fatto che il \acr{pid} sia un numero univoco per il sistema lo rende il -candidato ideale per generare ulteriori 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. +Il fatto che il \acr{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 +\acr{pid} per generare un \itindex{pathname} \textit{pathname} univoco, che +non potrà essere replicato da un altro processo che usi la stessa funzione. Tutti i processi figli dello stesso processo padre sono detti -\textit{sibling}, questa è una delle relazioni usate nel \textsl{controllo di +\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 -\secref{cha:session}, dove esamineremo gli altri identificativi associati ad +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 \acr{pid} e al \acr{ppid}, e a quelli usati per il controllo di -sessione, ad ogni processo sono associati altri identificatori, usati per il -controllo di accesso, che servono per determinare se il processo può o meno -eseguire le operazioni richieste, a seconda dei privilegi e dell'identità di -chi lo ha posto in esecuzione; su questi torneremo in dettagli più avanti in -\secref{sec:proc_perms}. +Oltre al \acr{pid} e al \acr{ppid}, (e a quelli che vedremo in +sez.~\ref{sec:sess_proc_group}, relativi al controllo di sessione), ad ogni +processo vengono associati degli altri identificatori 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}} +\subsection{La funzione \func{fork} e le funzioni di creazione dei processi} \label{sec:proc_fork} -La funzione \func{fork} è la funzione fondamentale della gestione dei -processi: come si è detto l'unico modo di creare un nuovo processo è -attraverso l'uso di questa funzione, essa quindi riveste un ruolo centrale -tutte le volte che si devono scrivere programmi che usano il multitasking. Il -prototipo della funzione è: +La funzione \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 dalle \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{functions} \headdecl{sys/types.h} \headdecl{unistd.h} \funcdecl{pid\_t fork(void)} - Restituisce zero al padre e il \acr{pid} al figlio in caso di successo, - ritorna -1 al padre (senza creare il figlio) in caso di errore; - \texttt{errno} può assumere i valori: + Crea un nuovo processo. + + \bodydesc{In caso di successo restituisce il \acr{pid} del figlio al padre e + zero al figlio; ritorna -1 al padre (senza creare il figlio) in caso di + errore; \var{errno} può assumere i valori: \begin{errlist} - \item \macro{EAGAIN} non ci sono risorse sufficienti per creare un'altro + \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 \macro{ENOMEM} non è stato possibile allocare la memoria per le + 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{errlist}} \end{functions} Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che -il processo figlio continuano ad essere eseguiti normalmente alla istruzione -seguente la \func{fork}; il processo figlio è però una copia del padre, e -riceve una copia dei segmenti di testo, stack e dati (vedi -\secref{sec:proc_mem_layout}), ed esegue esattamente lo stesso codice del -padre, ma la memoria è copiata, non condivisa\footnote{In generale il segmento - di testo, che è identico, è condiviso e tenuto in read-only, Linux poi - utilizza la tecnica del \textit{copy-on-write}, per cui la memoria degli - altri segmenti viene copiata dal kernel per il nuovo processo solo in caso - di scrittura, rendendo molto più efficiente il meccanismo} pertanto padre e -figlio vedono variabili diverse. - -La differenza che si ha nei due processi è che nel processo padre il valore di -ritorno della funzione fork è il \acr{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 -\textbf{due} volte: una nel padre e una nel figlio. La sola differenza che si -ha nei due processi è il valore di ritorno restituito dalla funzione, che nel -padre è il \acr{pid} del figlio mentre nel figlio è zero; in questo modo il -programma può identificare se viene eseguito dal padre o dal figlio. - -La scelta di questi valori non è casuale, un processo infatti può avere più -figli, ed il valore di ritorno di \func{fork} è l'unico modo che permette di -identificare quello appena creato; al contrario un figlio ha sempre un solo -padre (il cui \acr{pid} può sempre essere ottenuto con \func{getppid}, vedi -\secref{sec:proc_pid}) e si usa il valore nullo, che non può essere il -\acr{pid} di nessun processo. - -\begin{figure}[!htb] - \footnotesize - \begin{lstlisting}{} -#include /* error definitions and routines */ -#include /* C standard library */ -#include /* unix standard library */ -#include /* standard I/O library */ -#include /* string functions */ - -/* Help printing routine */ -void usage(void); - -int main(int argc, char *argv[]) -{ -/* - * Variables definition - */ - int nchild, i; - pid_t pid; - int wait_child = 0; - int wait_parent = 0; - int wait_end = 0; - ... /* handling options */ - nchild = atoi(argv[optind]); - printf("Test for forking %d child\n", nchild); - /* loop to fork children */ - for (i=0; i output [piccardi@selidor sources]$ cat output Process 1967: forking 3 child @@ -463,215 +542,243 @@ Spawned 2 child, pid 1969 Go to next child Spawned 3 child, pid 1970 Go to next child -\end{verbatim} -\normalsize -che come si vede è completamente diverso da quanto ottenevamo sul terminale. +\end{Verbatim} +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 \capref{cha:file_unix_interface} e in -\secref{cha:files_std_interface}. Qui basta accennare che si sono usate le +Il comportamento delle varie funzioni di interfaccia con i file è analizzato +in gran dettaglio in cap.~\ref{cha:file_unix_interface} e in +cap.~\ref{cha:files_std_interface}. Qui basta accennare che si sono usate le funzioni standard della libreria del C che prevedono l'output bufferizzato; e -questa bufferizzazione 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 (nel qual caso il buffer viene scaricato ad ogni a capo). +questa bufferizzazione (trattata 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 (nel qual caso il +buffer viene scaricato ad ogni carattere di a capo). Nel primo esempio allora avevamo che ad ogni chiamata a \func{printf} il buffer veniva scaricato, e le singole righe erano stampate 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, per questo -motivo, 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 all'uscita del figlio il buffer -viene scritto su disco, 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, troviamo anche l'output del padre. - -Ma l'esempio ci mostra un'altro aspetto fondamentale dell'interazione con i -file, che era 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 della condivisione dei file in unix è trattato -in dettaglio in \secref{sec:file_sharing}), ma anche che, a differenza di -quanto avviene per le variabili, la posizione corrente sul file è condivisa -fra il padre e tutti i processi figli. - -Quello che succede è che quando lo standard output del padre viene rediretto, -lo stesso avviene anche per tutti i figli; la funzione \func{fork} infatti ha -la caratteristica di duplicare (allo stesso modo in cui lo fa la funzione -\func{dup}, trattata in \secref{sec:file_dup}) nei figli tutti i file -descriptor aperti nel padre, il che comporta che padre e figli condividono le -stesse voci della file table (per la spiegazione di questi termini si veda -\secref{sec:file_sharing}) e quindi anche l'offset corrente nel file. - -In questo modo se un processo scrive sul file aggiornerà l'offset sulla file -table, e tutti gli altri processi che condividono la file table vedranno il -nuovo valore; in questo modo si evita, in casi come quello appena mostrato in +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 della condivisione dei file è trattato in dettaglio in +sez.~\ref{sec:file_sharing}), ma anche che, a differenza di quanto avviene per +le variabili, la posizione corrente sul file è condivisa fra il padre e tutti +i processi figli. + +Quello che succede è che quando lo standard output 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 file descriptor 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} (per la spiegazione di questi termini +si veda sez.~\ref{sec:file_sharing}) fra cui c'è anche la posizione corrente +nel file. + +In questo modo se un processo scrive sul 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 ed attende la sua conclusione per proseguire, ed entrambi -scrivono sullo stesso file, ad esempio lo standard output (un caso tipico è la -shell). Se l'output viene rediretto con questo comportamento avremo che il -padre potrà continuare a scrivere automaticamente in coda a quanto scritto dal -figlio; se così non fosse ottenere questo comportamento sarebbe estremamente -complesso necessitando di una qualche forma di comunicazione fra i due -processi. - -In generale comunque non è buona norma far scrivere più processi sullo stesso -file senza una qualche forma di sincronizzazione in quanto, come visto con il -nostro esempio, le varie scritture risulteranno mescolate fra loro in una -sequenza impredicibile. Le modalità con cui in genere si usano i file dopo una -\func{fork} sono sostanzialmente due: -\begin{enumerate} +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 è la shell quando lancia un +programma, il cui output va sullo standard output. In questo modo, anche se +l'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 - degli offset dopo eventuali operazioni di lettura e scrittura effettuate dal - figlio è automatica. + è 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 deve chiudere i file che non gli servono una volta che la - \func{fork} è stata eseguita, per evitare ogni forma di interferenza. -\end{enumerate} + 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: +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 \textit{close-on-exec} (vedi -\secref{sec:proc_exec} e \secref{sec:file_fcntl}) se settati. -\item gli identificatori per il controllo di accesso: il \textit{real user - id}, il \textit{real group id}, l'\textit{effective user id}, - l'\textit{effective group id} e i \textit{supplementary group id} (vedi - \secref{sec:proc_user_group}). -\item gli identificatori per il controllo di sessione: il \textit{process - group id} e il \textit{session id} e il terminale di controllo (vedi - \secref{sec:sess_xxx} e \secref{sec:sess_xxx}). -\item i flag di \acr{suid} e \acr{sgid} (vedi \secref{sec:file_suid_sgid}). +\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}); +\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 directory di lavoro e la directory radice (vedi - \secref{sec:file_work_dir}). -\item la maschera dei permessi di creazione (vedi \secref{sec:file_umask}). -\item la maschera dei segnali bloccati e le azioni installate (vedi -\secref{sec:sig_xxx}). + 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 -\secref{sec:ipc_xxx}). -\item i limiti sulle risorse (vedi \secref{sec:sys_xxx}). -\item le variabili di ambiente (vedi \secref{sec:proc_environ}). + 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}). \end{itemize*} -le differenze fra padre e figlio dopo la \func{fork} invece sono: +Le differenze fra padre e figlio dopo la \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 \textit{process id}. -\item il \textit{parent process id} (quello del figlio viene settato al - \acr{pid} del padre). -\item i valori dei tempi di esecuzione (\var{tms\_utime}, \var{tms\_stime}, - \var{tms\_cutime}, \var{tms\_uetime}) che nel figlio sono posti a zero. -\item i \textit{file lock} (vedi \secref{sec:file_locking}, che non vengono ereditati dal figlio. -\item gli allarmi ed i segnali pendenti (vedi \secref{sec:sig_xxx}), che per il figlio vengono cancellati. +\item il valore di ritorno di \func{fork}; +\item il \acr{pid} (\textit{process id}), assegnato ad un nuovo valore univoco; +\item il \acr{ppid} (\textit{parent process id}), quello del figlio viene + impostato al \acr{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; +\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*} - -\subsection{La funzione \func{vfork}} -\label{sec:proc_vfork} - -La funzione \func{vfork} è 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 +Una seconda funzione storica usata per la creazione di un nuovo processo è +\func{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 +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 fatto solo per poi eseguire una \func{exec}. La funzione +\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 \textit{copy on write} la perdita di prestazioni è -assolutamente trascurabile, e l'uso di questa funzione (che resta un caso -speciale della funzione \func{clone}), è deprecato, per questo eviteremo di -trattarla ulteriormente. +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.} +\subsection{La conclusione di un processo} \label{sec:proc_termination} -In \secref{sec:proc_conclusion} abbiamo già affrontato le modalità con cui -concludere un programma, ma dal punto di vista del programma stesso; avendo a -che fare con un sistema multitasking occorre adesso affrontare l'argomento dal -punto di vista generale di come il sistema gestisce la conclusione dei -processi. +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 multitasking resta da affrontare l'argomento dal punto di vista +di come il sistema gestisce la conclusione dei processi. -Abbiamo già visto in \secref{sec:proc_conclusion} le tre modalità con cui un +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 stream), il ritorno dalla funzione \func{main} (equivalente alla chiamata di \func{exit}), e la chiamata ad \func{\_exit} (che passa direttamente alle operazioni di terminazione del processo da parte del kernel). -Ma oltre alla conclusione normale abbiamo accennato che esistono anche delle -modalità di conclusione anomala; queste sono in sostanza due: il programma può +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} per invocare una chiusura anomala, o essere -terminato da un segnale. In realtà anche la prima modalità si riconduce alla -seconda, dato che \func{abort} si limita a generare il segnale -\macro{SIGABRT}. +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 +Qualunque sia la modalità di conclusione di un processo, il kernel esegue comunque una serie di operazioni: 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: +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 descrittori dei file sono chiusi. -\item viene memorizzato lo stato di terminazione del processo. -\item ad ogni processo figlio viene assegnato un nuovo padre. -\item viene inviato il segnale \macro{SIGCHLD} al processo padre. -\item se il processo è un leader di sessione viene mandato un segnale di - \macro{SIGHUP} a tutti i processi in background e il terminale di controllo - viene disconnesso. -\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 \macro{SIGHUP} e \macro{SIGCONT}. +\item tutti i file descriptor 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*} -ma al di la di queste operazioni è necessario poter disporre di un meccanismo -ulteriore che consenta di sapere come questa terminazione è avvenuta; dato che -in un sistema unix-like tutto viene gestito attraverso i processi il -meccanismo scelto consiste nel riportare lo stato di terminazione -(\textit{termination status}) di cui sopra al processo padre. - -Nel caso di conclusione normale, lo stato di uscita del processo viene -caratterizzato tramite il valore del cosiddetto \textit{exit status}, cioè il + +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 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 alle funzioni \func{exit} o \func{\_exit} (o dal valore di ritorno per \func{main}). Ma se il processo viene concluso in maniera anomala -il programma non può specificare nessun \textit{exit status}, ed è il kernel +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. +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 -\secref{sec:proc_wait}) è sempre quest'ultimo; in caso di conclusione normale +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. 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 ha 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 figlio -venga \textsl{adottato} da \cmd{init}: come già accennato quando un processo -termina il kernel controlla se è il padre di altri processi in esecuzione: in +alla sua creazione è scontato che ogni nuovo processo ha 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}. Come già accennato quando un processo +termina, il kernel controlla se è il padre di altri processi in esecuzione: in caso positivo allora il \acr{ppid} di tutti questi processi viene sostituito -con il \acr{pid} di \cmd{init} (e cioè con 1); in questo modo ogni processo -avrà sempre un padre (nel caso \textsl{adottivo}) cui riportare il suo stato -di terminazione. Come verifica di questo comportamento possiamo eseguire il -comando \cmd{forktest} imponendo a ciascun processo figlio due -secondi di attesa prima di uscire, il risultato è: - -\footnotesize -\begin{verbatim} +con il \acr{pid} di \cmd{init} (e 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{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm] [piccardi@selidor sources]$ ./forktest -c2 3 Process 1972: forking 3 child Spawned 1 child, pid 1973 @@ -686,40 +793,37 @@ Go to next child [piccardi@selidor sources]$ Child 3, parent 1, exiting Child 2, parent 1, exiting Child 1, parent 1, exiting -\end{verbatim} -\normalsize -come si può notare in questo caso il processo padre si conclude prima dei +\end{Verbatim} +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 +terminano, e come si può notare in questo caso, al contrario di quanto visto in precedenza, essi riportano 1 come \acr{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 +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 \acr{pid}, i tempi di CPU usati -dal processo (vedi \secref{sec:intro_unix_time}) e lo stato di terminazione -\footnote{NdA verificare esattamente cosa c'è!}, 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 \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 \cmd{Z} nella colonna che ne indica lo stato. Quando il padre -effettuerà la lettura dello stato di uscita anche questa informazione, non più -necessaria, verrà scartata e la terminazione potrà dirsi completamente -conclusa. +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 \index{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 uscita anche questa +informazione, non più necessaria, verrà scartata e la terminazione potrà dirsi +completamente conclusa. Possiamo utilizzare il nostro programma di prova per analizzare anche questa -condizione: lanciamo il comando \cmd{forktest} in background, 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: - -\footnotesize -\begin{verbatim} +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{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm] [piccardi@selidor sources]$ ps T PID TTY STAT TIME COMMAND 419 pts/0 S 0:00 bash @@ -728,186 +832,287 @@ otterremo: 570 pts/0 Z 0:00 [forktest ] 571 pts/0 Z 0:00 [forktest ] 572 pts/0 R 0:00 ps T -\end{verbatim} %$ -\normalsize -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 zombie e l'indicazione che sono stati terminati. - -La possibilità di avere degli 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 \secref{sec:sig_xxx} e \secref{sec:proc_wait}). Questa -operazione è necessaria perché anche se gli \textit{zombie} non consumano -risorse di memoria o processore, occupano comunque una voce nella tabella dei -processi, che a lungo andare potrebbe esaurirsi. +\end{Verbatim} +%$ +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 \index{zombie} \textit{zombie} e l'indicazione che +sono stati terminati. + +La possibilità di avere degli \index{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}). Questa operazione è necessaria perché anche se gli +\index{zombie} \textit{zombie} non consumano risorse di memoria o processore, +occupano comunque una voce nella tabella dei processi, che a lungo andare +potrebbe esaurirsi. Si noti che quando un processo adottato da \cmd{init} termina, esso non -diviene uno \textit{zombie}; questo perché una delle funzioni di \cmd{init} è -appunto quella di chiamare la funzione \func{wait} per i processi 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 zombie: alla sua terminazione infatti tutti i suoi figli -vengono ereditati (compresi gli zombie) verranno adottati da \cmd{init}, il -quale provvederà a completarne la terminazione. - -Si tenga presente infine che siccome gli zombie sono processi già usciti, non -c'è modo di eliminarli con il comando \cmd{kill}; l'unica possibilità è quella -di terminare il processo che li ha generati, in modo che \cmd{init} possa -adottarli e provvedere a concludere la terminazione. - - -\subsection{Le funzioni \func{wait} e \func{waitpid}} +diviene uno \index{zombie} \textit{zombie}; questo perché una delle funzioni +di \cmd{init} è appunto quella di chiamare la funzione \func{wait} per i +processi 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 \index{zombie} \textit{zombie}: +alla sua terminazione infatti tutti i suoi figli (compresi gli \index{zombie} +\textit{zombie}) verranno adottati da \cmd{init}, il quale provvederà a +completarne la terminazione. + +Si tenga presente infine che siccome gli \index{zombie} \textit{zombie} sono +processi già usciti, non c'è modo di eliminarli con il comando \cmd{kill}; +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 provvedere a concluderne la terminazione. + + +\subsection{Le funzioni di attesa e ricezione degli stati di uscita} \label{sec:proc_wait} -Abbiamo già accennato come uno degli usi possibili delle capacità multitasking -di un sistema unix-like consista nella creazione di programmi di tipo server, -in cui un processo principale attende le richieste che vengono poi soddisfatte -creando una serie di processi figli. Si è già sottolineato al paragrafo -precedente come in questo caso diventi necessario gestire esplicitamente la -conclusione dei vari processi figli onde evitare di riempire di -\textit{zombie} la tabella dei processi; le funzioni deputate a questo compito -sono sostanzialmente due, \func{wait} e \func{waitpid}. La prima, il cui -prototipo è: +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 \index{zombie} \textit{zombie} la tabella dei processi; +le funzioni deputate a questo compito sono principalmente due, la prima è +\funcd{wait} ed il suo prototipo è: \begin{functions} \headdecl{sys/types.h} \headdecl{sys/wait.h} -\funcdecl{pid\_t wait(int * status)} +\funcdecl{pid\_t wait(int *status)} -Sospende il processo corrente finché un figlio non è uscito, o finché un -segnale termina il processo o chiama una funzione di gestione. Se un figlio è -già uscito la funzione ritorna immediatamente. Al ritorno lo stato di -termininazione del processo viene salvato nella variabile puntata da -\var{status} e tutte le informazioni relative al processo (vedi -\secref{sec:proc_termination}) vengono rilasciate. +Sospende il processo corrente finché un figlio non è uscito, o finché un +segnale termina il processo o chiama una funzione di gestione. -La funzione restituisce il \acr{pid} del figlio in caso di successo e -1 in -caso di errore; \var{errno} può assumere i valori: +\bodydesc{La funzione restituisce il \acr{pid} del figlio in caso di successo + e -1 in caso di errore; \var{errno} può assumere i valori: \begin{errlist} - \item \macro{EINTR} la funzione è stata interrotta da un segnale. - \end{errlist} + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \end{errlist}} \end{functions} - -è presente fin dalle prime versioni di unix; la funzione ritorna alla -conclusione del primo figlio (o immediatamente se un figlio è già uscito). Nel -caso un processo abbia più figli il valore di ritorno permette di identificare -qual'è quello che è uscito. - -Questa funzione però ha il difetto di essere poco flessibile, in quanto -ritorna all'uscita di un figlio qualunque. Nelle occasioni in cui è necessario -attendere la conclusione di un processo specifico occorre predisporre un -meccanismo che tenga conto dei processi già terminati, e ripeta la chiamata -alla funzione nel caso il processo cercato sia ancora attivo. - -Per questo motivo lo standard POSIX.1 ha introdotto la funzione \func{waitpid} -che effettua lo stesso servizio, ma dispone di una serie di funzionalità più -ampie, legate anche al controllo di sessione. Dato che è possibile ottenere -lo stesso comportamento di \func{wait} si consiglia di utilizzare sempre -questa funzione; il suo prototipo è: +\noindent + +Questa funzione è presente fin dalle prime versioni di Unix; essa ritorna non +appena un qualunque processo figlio termina. Se un figlio è già terminato +prima della chiamata la funzione ritorna immediatamente, se più di un figlio è +già terminato occorre continuare chiamare la funzione più volte se si vuole +recuperare lo stato di terminazione di tutti quanti. + +Al ritorno della funzione lo stato di terminazione del figlio viene salvato +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 \acr{pid} del processo di cui si è ricevuto lo +stato di terminazione, cosa che permette di identificare qual è il figlio che +è terminato. + +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 un processo specifico occorrerebbe +predisporre un meccanismo che tenga conto dei processi già terminati, e +provvedere a ripetere la chiamata alla funzione nel caso il processo cercato +sia ancora attivo. + +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, +\funcd{waitpid}, il cui prototipo è: \begin{functions} \headdecl{sys/types.h} \headdecl{sys/wait.h} -\funcdecl{pid\_t waitpid(pid\_t pid, int * status, int options)} +\funcdecl{pid\_t waitpid(pid\_t pid, int *status, int options)} +Attende la conclusione di un processo figlio. -La funzione restituisce il \acr{pid} del processo che è uscito, 0 se è stata -specificata l'opzione \macro{WNOHANG} e il processo non è uscito e -1 per un -errore, nel qual caso \var{errno} assumerà i valori: +\bodydesc{La funzione restituisce il \acr{pid} del processo che è uscito, 0 se + è stata specificata l'opzione \const{WNOHANG} e il processo non è uscito e + -1 per un errore, nel qual caso \var{errno} assumerà i valori: \begin{errlist} - \item \macro{EINTR} se non è stata specificata l'opzione \macro{WNOHANG} e - la funzione è stata interrotta da un segnale. - \item \macro{ECHILD} il processo specificato da \var{pid} non esiste o non è - figlio del processo chiamante. - \end{errlist} + \item[\errcode{EINTR}] non è stata specificata l'opzione \const{WNOHANG} e + la funzione è stata interrotta da un segnale. + \item[\errcode{ECHILD}] il processo specificato da \param{pid} non esiste o + non è figlio del processo chiamante. + \item[\errcode{EINVAL}] si è specificato un valore non valido per + l'argomento \param{options}. + \end{errlist}} \end{functions} -Le differenze principali fra le due funzioni sono che \func{wait} si blocca -sempre fino a che un processo figlio non termina, mentre \func{waitpid} ha la -possibilità si specificare un'opzione \macro{WNOHANG} che ne previene il -blocco; inoltre \func{waitpid} può specificare quale processo attendere sulla -base del valore specificato tramite la variabile \var{pid}, secondo lo -specchietto riportato in \ntab: +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|p{10cm}|} + \begin{tabular}[c]{|c|c|p{8cm}|} \hline - \textbf{Valore} & \textbf{Significato}\\ + \textbf{Valore} & \textbf{Costante} &\textbf{Significato}\\ \hline \hline - $<-1$& attende per un figlio il cui \textit{process group} è uguale al - valore assoluto di \var{pid}. \\ - $-1$ & attende per un figlio qualsiasi, usata in questa maniera è - equivalente a \func{wait}.\\ - $0$ & attende per un figlio il cui \textit{process group} è uguale a - quello del processo chiamante. \\ - $>0$ & attende per un figlio il cui \acr{pid} è uguale al - valore di \var{pid}.\\ + $<-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 \acr{pid} è uguale + al valore di \param{pid}.\\ \hline \end{tabular} - \caption{Significato dei valori del parametro \var{pid} della funzione + \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ò essere modificato passando delle -opportune opzioni tramite la variabile \var{option}. I valori possibili sono -il già citato \macro{WNOHANG}, che previene il blocco della funzione quando il -processo figlio non è terminato, e \macro{WUNTRACED} (usata per il controllo -di sessione, trattato in \capref{cha:session}) che fa ritornare la funzione -anche per i processi figli che sono bloccati ed il cui stato non è stato -ancora riportato al padre. Il valore dell'opzione deve essere specificato come -maschera binaria ottenuta con l'OR delle suddette costanti con zero. - -La terminazione di un processo figlio è chiaramente un evento asincrono -rispetto all'esecuzione di un programma e può avvenire in un qualunque -momento, per questo motivo, come si è visto nella sezione precedente, una -delle azioni prese dal kernel alla conclusione di un processo è quella di -mandare un segnale di \macro{SIGCHLD} al padre. Questo segnale viene ignorato -di default, ma costituisce il meccanismo di comunicazione asincrona con cui il -kernel avverte un processo padre che uno dei suoi figli è terminato. - -In genere in un programma non si vuole essere forzati ad attendere la -conclusione di un processo per proseguire, specie se tutto questo serve solo -per leggerne lo stato di chiusura (ed evitare la presenza di \textit{zombie}), -per questo la modalità più usata per chiamare queste funzioni è quella di -utilizzarle all'interno di un \textit{signal handler} (torneremo sui segnali e -su come gestire \macro{SIGCHLD} in \secref{sec:sig_sigwait_xxx}) nel qual -caso, dato che il segnale è generato dalla terminazione un figlio, avremo la -certezza che la chiamata a \func{wait} non si bloccherà. +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 dei flag riportati nella prima +parte in tab.~\ref{tab:proc_waitpid_options} che possono essere combinati fra +loro con un OR aritmetico. Nella seconda parte della stessa tabella si sono +riportati anche alcuni valori non standard specifici 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}) 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]{|c|p{10cm}|} + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Macro} & \textbf{Descrizione}\\ + \hline + \hline + \const{WNOHANG} & La funzione ritorna immediatamente anche se non è + terminato nessun processo figlio. \\ + \const{WUNTRACED} & Ritorna anche se un processo figlio è stato fermato. \\ + \const{WCONTINUED}& Ritorna anche quando un processo figlio che era stato + fermato ha ripreso l'esecuzione.\footnotemark \\ + \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. \\ + \const{\_\_WALL} & Attende per qualunque processo figlio. \\ + \const{\_\_WNOTHREAD}& Non attende per i figli di altri \textit{thread} + dello stesso gruppo. \\ + \hline + \end{tabular} + \caption{Costanti che identificano i bit dell'argomento \param{options} + della funzione \func{waitpid}.} + \label{tab:proc_waitpid_options} +\end{table} + +\footnotetext{disponibile solo a partire dal kernel 2.6.10.} + +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 la funzione ritornerà un +valore nullo anziché positivo.\footnote{anche in questo caso un valore + positivo indicherà il \acr{pid} del processo di cui si è ricevuto lo stato + ed un valore negativo un errore.} + +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 \acr{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 è +dettagliato 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,\footnote{una revisione del 2001 dello standard POSIX.1 + che ha aggiunto dei requisiti e delle nuove funzioni, come \func{waitid}.} +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\footnote{lo standard POSIX.1 + originale infatti lascia indefinito il comportamento di queste funzioni + quando \signal{SIGCHLD} viene ignorato.} e si comportano sempre nello stesso +modo, indipendentemente dal fatto \signal{SIGCHLD} sia ignorato o meno: +attendono la terminazione di un processo figlio e ritornano il relativo +\acr{pid} e lo stato di terminazione nell'argomento \param{status}. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{10cm}|} \hline \textbf{Macro} & \textbf{Descrizione}\\ \hline \hline \macro{WIFEXITED(s)} & Condizione vera (valore non nullo) per un processo - figlio che sia terminato normalmente. \\ + figlio che sia terminato normalmente. \\ \macro{WEXITSTATUS(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 \func{main}). Può essere valutata solo se - \macro{WIFEXITED} ha restituito un valore non nullo.\\ - \macro{WIFSIGNALED(s)} & Vera se il processo figlio è terminato - in maniera anomala a causa di un segnale che non è stato catturato (vedi - \secref{sec:sig_notification}).\\ - \macro{WTERMSIG(s)} & restituisce il numero del segnale che ha causato - la terminazione anomala del processo. Può essere valutata solo se - \macro{WIFSIGNALED} ha restituito un valore non nullo.\\ + stato di uscita del processo (passato attraverso + \func{\_exit}, \func{exit} o come valore di + ritorno di \func{main}); può essere valutata solo + se \val{WIFEXITED} ha restituito un valore non + nullo.\\ + \macro{WIFSIGNALED(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(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(s)} & Vera se il processo terminato ha generato un - file si \textit{core dump}. Può essere valutata solo se - \macro{WIFSIGNALED} ha restituito un valore non nullo\footnote{questa - macro non è definita dallo standard POSIX.1, ma è presente come estensione - sia in Linux che in altri unix}.\\ + file di \itindex{core~dump} \textit{core + dump}; può essere valutata solo se + \val{WIFSIGNALED} ha restituito un valore non + nullo.\footnotemark \\ \macro{WIFSTOPPED(s)} & Vera se il processo che ha causato il ritorno di - \func{waitpid} è bloccato. L'uso è possibile solo avendo specificato - l'opzione \macro{WUNTRACED}. \\ - \macro{WSTOPSIG(s)} & restituisce il numero del segnale che ha bloccato - il processo, Può essere valutata solo se \macro{WIFSTOPPED} ha - restituito un valore non nullo. \\ + \func{waitpid} è bloccato; l'uso è possibile solo + con \func{waitpid} avendo specificato l'opzione + \const{WUNTRACED}.\\ + \macro{WSTOPSIG(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(s)}& Vera se il processo che ha causato il ritorno è + stato riavviato da un + \signal{SIGCONT}.\footnotemark \\ \hline \end{tabular} \caption{Descrizione delle varie macro di preprocessore utilizzabili per @@ -915,143 +1120,257 @@ certezza che la chiamata a \func{wait} non si bloccher \label{tab:proc_status_macro} \end{table} -Entrambe le funzioni restituiscono lo stato di terminazione del processo -tramite il puntatore \var{status} (se non interessa memorizzare lo stato si -può passare un puntatore nullo). Il valore restituito da entrambe le funzioni -dipende dall'implementazione, e tradizionalmente alcuni bit sono riservati per -memorizzare lo stato di uscita (in genere 8) altri per indicare il segnale che -ha causato la terminazione (in caso di conclusione anomala), uno per indicare -se è stato generato un core file, etc.\footnote{le definizioni esatte si - possono trovare in \file{}}. 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 \curtab\ (si tenga presente che -queste macro prendono come parametro la variabile di tipo \type{int} puntata -da \var{status}). +\footnotetext[20]{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}.} + +\footnotetext{è presente solo a partire dal kernel 2.6.10.} + +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 \index{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} (se non +interessa memorizzare lo stato si può passare un puntatore nullo). Il valore +restituito da entrambe le funzioni dipende dall'implementazione, ma +tradizionalmente alcuni bit (in genere 8) sono riservati per memorizzare lo +stato di uscita, e 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{}.} + +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 -\macro{WTERMSIG} può essere controllato contro le costanti definite in -\file{signal.h}, e stampato usando le funzioni definite in -\secref{sec:sig_strsignal}. +\val{WTERMSIG} può essere confrontato con le costanti che identificano i +segnali definite in \file{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 è \funcd{waitid} ed il +suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/wait.h} + + \funcdecl{int waitid(idtype\_t idtype, id\_t id, siginfo\_t *infop, int + options)} + + Attende la conclusione di un processo figlio. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 per un errore, + nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINTR}] se non è stata specificata l'opzione \const{WNOHANG} e + la funzione è stata interrotta da un segnale. + \item[\errcode{ECHILD}] il processo specificato da \param{pid} non esiste o + non è figlio del processo chiamante. + \item[\errcode{EINVAL}] si è specificato un valore non valido per + l'argomento \param{options}. + \end{errlist}} +\end{functions} -\subsection{Le funzioni \func{wait3} e \func{wait4}} -\label{sec:proc_wait4} +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. -Linux, seguendo una estensione di BSD, supporta altre due funzioni per la -lettura dello stato di terminazione di un processo, analoghe a \func{wait} e -\func{waitpid}, ma che prevedono un ulteriore parametro attraverso il quale il -kernel può restituire al processo padre ulteriori informazioni sulle risorse -usate dal processo terminato e dai vari figli. Queste funzioni, che diventano -accessibili definendo la costante \macro{\_USE\_BSD}, sono: +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Macro} & \textbf{Descrizione}\\ + \hline + \hline + \const{P\_PID} & Indica la richiesta di attendere per un processo figlio + il cui \acr{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{Macro} & \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 \acr{pid} del figlio. +\item[\var{si\_uid}] con l'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 sono \funcd{wait3} e \funcd{wait4}, che diventano accessibili +definendo la macro \macro{\_USE\_BSD}; i loro prototipi sono: \begin{functions} - \headdecl{sys/times.h} - \headdecl{sys/types.h} - \headdecl{sys/wait.h} - \headdecl{sys/resource.h} - \funcdecl{pid\_t wait4(pid\_t pid, int * status, int options, struct rusage - * rusage)} - La funzione è identica a \func{waitpid} sia per comportamento che per i - valori dei parametri, ma restituisce in \var{rusage} un sommario delle - risorse usate dal processo (per i dettagli vedi \secref{sec:xxx_limit_res}) + \headdecl{sys/times.h} \headdecl{sys/types.h} \headdecl{sys/wait.h} + \headdecl{sys/resource.h} + + \funcdecl{pid\_t wait4(pid\_t pid, int *status, int options, struct rusage + *rusage)} + È identica a \func{waitpid} sia per comportamento che per i valori degli + argomenti, ma restituisce in \param{rusage} un sommario delle risorse usate + dal processo. + \funcdecl{pid\_t wait3(int *status, int options, struct rusage *rusage)} - Prima versione, equivalente a \func{wait4(-1, \&status, opt, rusage)} è + Prima versione, equivalente a \code{wait4(-1, \&status, opt, rusage)} è ormai deprecata in favore di \func{wait4}. \end{functions} \noindent -la struttura \type{rusage} è definita in \file{sys/resource.h}, e viene -utilizzata anche dalla funzione \func{getrusage} per ottenere le risorse di -sistema usate dal processo; in Linux è definita come: -\begin{figure}[!htb] - \footnotesize - \centering - \begin{minipage}[c]{15cm} - \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} -struct rusage { - struct timeval ru_utime; /* user time used */ - struct timeval ru_stime; /* system time used */ - long ru_maxrss; /* maximum resident set size */ - long ru_ixrss; /* integral shared memory size */ - long ru_idrss; /* integral unshared data size */ - long ru_isrss; /* integral unshared stack size */ - long ru_minflt; /* page reclaims */ - long ru_majflt; /* page faults */ - long ru_nswap; /* swaps */ - long ru_inblock; /* block input operations */ - long ru_oublock; /* block output operations */ - long ru_msgsnd; /* messages sent */ - long ru_msgrcv; /* messages received */ - long ru_nsignals; ; /* signals received */ - long ru_nvcsw; /* voluntary context switches */ - long ru_nivcsw; /* involuntary context switches */ -}; - \end{lstlisting} - \end{minipage} - \normalsize - \caption{La struttura \var{rusage} per la lettura delle informazioni dei - delle risorse usate da un processo.} - \label{fig:proc_rusage_struct} -\end{figure} -In genere includere esplicitamente \file{} non è più necessario, -ma aumenta la portabilità, e serve in caso si debba accedere ai campi di -\var{rusage} definiti come \type{struct timeval}. La struttura è ripresa dalla -versione 4.3 Reno di BSD, attualmente (con il kernel 2.4.x) i soli campi che -sono mantenuti sono: \var{ru\_utime}, \var{ru\_stime}, \var{ru\_minflt}, -\var{ru\_majflt}, e \var{ru\_nswap}. +la struttura \struct{rusage} è definita in \file{sys/resource.h}, e viene +utilizzata anche dalla funzione \func{getrusage} (vedi +sez.~\ref{sec:sys_resource_use}) per ottenere le risorse di sistema usate da un +processo; la sua definizione è riportata in fig.~\ref{fig:sys_rusage_struct}. - -\subsection{Le funzioni \func{exec}} +\subsection{La funzione \func{exec} e le funzioni di 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 +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 \acr{pid} del processo non cambia, dato che non viene -creato un nuovo processo, la funzione semplicemente rimpiazza lo stack, o -heap, i dati ed il testo del processo corrente con un nuovo programma letto da -disco. - -Ci sono sei diverse versioni di \func{exec} (per questo la si è chiamata -famiglia di funzioni) che possono essere usate per questo compito, che in -realtà (come mostrato in \figref{fig:proc_exec_relat}), costituiscono un -front-end a \func{execve}. Il prototipo di quest'ultima è: +creato un nuovo processo, la funzione semplicemente rimpiazza lo +\itindex{stack} \textit{stack}, lo \itindex{heap} \textit{heap}, i +\index{segmento!dati} dati ed il \index{segmento!testo} testo del processo +corrente con un nuovo programma letto da disco. + +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}), sono tutte un front-end a +\funcd{execve}. Il prototipo di quest'ultima è: \begin{prototype}{unistd.h} -{int execve(const char * filename, char * const argv [], char * const envp[])} +{int execve(const char *filename, char *const argv[], char *const envp[])} + Esegue il programma contenuto nel file \param{filename}. - La funzione esegue il file o lo script indicato da \var{filename}, - passandogli la lista di argomenti indicata da \var{argv} e come ambiente la - lista di stringhe indicata da \var{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 - \func{main} è dichiarata nella forma \func{main(int argc, char *argv[], char - *envp[])}. - - La funzione ritorna -1 solo in caso di errore, nel qual caso caso la - \var{errno} può assumere i valori: + \bodydesc{La funzione ritorna solo in caso di errore, restituendo -1; nel + qual caso \var{errno} può assumere i valori: \begin{errlist} - \item \macro{EACCES} il file non è eseguibile, oppure il filesystem è - montato in \cmd{noexec}, oppure non è un file normale o un interprete. - \item \macro{EPERM} il file ha i bit \acr{suid} o \acr{sgid} ma l'utente non - è root o il filesystem è montato con \cmd{nosuid}, oppure - \item \macro{ENOEXEC} il file è in un formato non eseguibile o non + \item[\errcode{EACCES}] il file non è eseguibile, oppure il filesystem è + montato in \cmd{noexec}, oppure non è un file regolare o un interprete. + \item[\errcode{EPERM}] il file ha i bit \itindex{suid~bit} \acr{suid} o + \itindex{sgid~bit} \acr{sgid}, l'utente non è root, il processo viene + tracciato, o il filesystem è montato con l'opzione \cmd{nosuid}. + \item[\errcode{ENOEXEC}] il file è in un formato non eseguibile o non riconosciuto come tale, o compilato per un'altra architettura. - \item \macro{ENOENT} il file o una delle librerie dinamiche o l'interprete + \item[\errcode{ENOENT}] il file o una delle librerie dinamiche o l'interprete necessari per eseguirlo non esistono. - \item \macro{ETXTBSY} L'eseguibile è aperto in scrittura da uno o più + \item[\errcode{ETXTBSY}] l'eseguibile è aperto in scrittura da uno o più processi. - \item \macro{EINVAL} L'eseguibile ELF ha più di un segmento - \macro{PF\_INTERP}, cioè chiede di essere eseguito da più di un interprete. - \item \macro{ELIBBAD} Un interprete ELF non è in un formato riconoscibile. + \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{E2BIG}] la lista degli argomenti è troppo grande. \end{errlist} - ed inoltre anche \macro{EFAULT}, \macro{ENOMEM}, \macro{EIO}, - \macro{ENAMETOOLONG}, \macro{E2BIG}, \macro{ELOOP}, \macro{ENOTDIR}, - \macro{ENFILE}, \macro{EMFILE}. + ed inoltre anche \errval{EFAULT}, \errval{ENOMEM}, \errval{EIO}, + \errval{ENAMETOOLONG}, \errval{ELOOP}, \errval{ENOTDIR}, \errval{ENFILE}, + \errval{EMFILE}.} \end{prototype} -Le altre funzioni della famiglia servono per fornire all'utente una serie -possibile di diverse interfacce per la creazione di un nuovo processo. I loro +La funzione \func{exec} esegue il file o lo script indicato da +\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 \func{main} è dichiarata nella forma +\code{main(int argc, char *argv[], char *envp[])}. + +Le altre funzioni della famiglia servono per fornire all'utente una serie di +possibili diverse interfacce per la creazione di un nuovo processo. I loro prototipi sono: \begin{functions} \headdecl{unistd.h} @@ -1063,21 +1382,22 @@ prototipi sono: \funcdecl{int execvp(const char *file, char *const argv[])} Sostituiscono l'immagine corrente del processo con quella indicata nel primo -argomento. I parametri successivi consentono di specificare gli argomenti a +argomento. Gli argomenti successivi consentono di specificare gli argomenti a linea di comando e l'ambiente ricevuti dal nuovo processo. -Queste funzioni ritornano solo in caso di errore, restituendo -1; nel qual -caso \var{errno} andrà ad assumere i valori visti in precedenza per -\func{execve}. +\bodydesc{Queste funzioni ritornano solo in caso di errore, restituendo -1; + nel qual caso \var{errno} assumerà i valori visti in precedenza per + \func{execve}.} \end{functions} -Per capire meglio le differenze fra le funzioni della famiglia si può fare -riferimento allo specchietto riportato in \ntab. La prima differenza riguarda -le modalità di passaggio dei parametri che poi andranno a costituire gli -argomenti a linea di comando (cioè i valori di \var{argv} e \var{argc} visti -dalla funzione \func{main} del programma chiamato). +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 +prima differenza 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 \func{main} del programma +chiamato). -Queste modalità sono due e sono riassunte dagli mnenonici \func{v} e \func{l} +Queste modalità sono due e sono riassunte dagli mnemonici \code{v} e \code{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, @@ -1085,12 +1405,10 @@ 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: -\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} - char * arg0, char * arg1, ..., char * argn, NULL -\end{lstlisting} +\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. +per indicare il nome del file che contiene il programma che verrà eseguito. \begin{table}[!htb] \footnotesize @@ -1100,15 +1418,15 @@ per indicare il nome del file che contiene il programma che verr \multicolumn{1}{|c|}{\textbf{Caratteristiche}} & \multicolumn{6}{|c|}{\textbf{Funzioni}} \\ \hline - &\func{execl\ }&\func{execlp}&\func{execle} - &\func{execv\ }& \func{execvp}& \func{execve} \\ + &\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$& \\ - ricerca su \var{PATH}&$\bullet$&&$\bullet$&$\bullet$&&$\bullet$ \\ + 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$& \\ @@ -1119,109 +1437,181 @@ per indicare il nome del file che contiene il programma che verr \label{tab:proc_exec_scheme} \end{table} -La seconda differenza fra le funzioni riguarda le modalità con cui si -specifica il programma che si vuole eseguire. Con lo mnemonico \func{p} si +La seconda differenza fra le funzioni riguarda le modalità con cui si +specifica il programma che si vuole eseguire. Con lo mnemonico \code{p} si indicano le due funzioni che replicano il comportamento della shell nello -specificare il comando da eseguire; quando il parametro \var{file} non -contiene una \file{/} 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 \var{PATH}. Il file che -viene posto in esecuzione è il primo che viene trovato. Se si ha un errore di -permessi negati (cioè l'esecuzione della sottostante \func{execve} ritorna un -\macro{EACCESS}), la ricerca viene proseguita nelle eventuali ulteriori -directory indicate nel \var{PATH}, solo se non viene trovato nessun altro file -viene finalmente restituito \macro{EACCESS}. +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 \var{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 \var{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 dal parametro \var{path}, che viene interpretato come il -\textit{pathname} del programma. +indicato dall'argomento \param{path}, che viene interpretato come il +\itindex{pathname} \textit{pathname} del programma. -\begin{figure}[htb] - \centering - \includegraphics[width=13cm]{img/exec_rel} - \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}} +\begin{figure}[!htb] + \centering \includegraphics[width=12cm]{img/exec_rel} + \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}.} \label{fig:proc_exec_relat} \end{figure} -La terza differenza è come viene passata la lista delle variabili di ambiente. -Con lo mnemonico \func{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 \macro{NULL}), le altre usano il -valore della variabile \var{environ} (vedi \secref{sec:proc_environ}) del -processo di partenza per costruire l'ambiente. +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 \acr{pid}, il nuovo programma fatto partire da -\func{exec} assume anche una serie di altre proprietà del processo chiamante; -la lista completa è la seguente: +\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} (\acr{pid}) ed il \textit{parent process ID} - (\acr{ppid}). -\item il \textit{real user ID} ed il \textit{real group ID} (vedi - \secref{sec:proc_user_group}). -\item i \textit{supplementary group ID} (vedi \secref{sec:proc_user_group}). -\item il \textit{session ID} ed il \textit{process group ID} (vedi - \secref{sec:sess_xxx}). -\item il terminale di controllo (vedi \secref{sec:sess_xxx}). -\item il tempo restante ad un allarme. +\item il \textit{process id} (\acr{pid}) ed il \textit{parent process id} + (\acr{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 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 la directory radice e la directory di lavoro corrente (vedi - \secref{sec:file_work_dir}). -\item la maschera di creazione dei file (\var{umask}, vedi - \secref{sec:file_umask}) ed i \textit{lock} sui file (vedi - \secref{sec:file_locking}). -\item i segnali sospesi (\textit{pending}) e la maschera dei segnali (si veda - \secref{sec:sig_xxx}). -\item i limiti sulle risorse (vedi \secref{sec:sys_limits}).. -\item i valori delle variabili \var{tms\_utime}, \var{tms\_stime}, - \var{tms\_cutime}, \var{tms\_ustime} (vedi \secref{sec:xxx_xxx}). + 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 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 \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 blocchi sulla memoria (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*} + +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}, anche se questo con Linux non avviene.\footnote{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 +caratteristiche specifiche di Linux, che non vengono preservate +nell'esecuzione della funzione \func{exec}, queste sono: +\begin{itemize*} +\item le operazione 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*} -Oltre a questo i segnali che sono stati settati per essere ignorati nel -processo chiamante mantengono lo stesso settaggio pure nel nuovo programma, -tutti gli altri segnali vengono settati alla loro azione di default. Un caso -speciale è il segnale \macro{SIGCHLD} che, quando settato a \macro{SIG\_IGN}, -può anche non essere resettato a \macro{SIG\_DFL} (si veda -\secref{sec:sig_xxx}). - -La gestione dei file aperti dipende dal valore del flag di -\textit{close-on-exec} per ciascun file descriptor (si veda -\secref{sec:file_fcntl}); i file per cui è settato vengono chiusi, tutti gli -altri file restano aperti. Questo significa che il comportamento di default è -che i file restano aperti attraverso una \func{exec}, a meno di una chiamata -esplicita a \func{fcntl} che setti 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} che effettua da sola il settaggio del flag di -\textit{close-on-exec} sulle directory che apre, in maniera trasparente -all'utente. - -Abbiamo detto che il \textit{real user ID} ed il \textit{real group ID} -restano gli stessi all'esecuzione di \func{exec}; lo stesso vale per -l'\textit{effective user ID} ed l'\textit{effective group ID}, tranne il caso -in cui il file che si va ad eseguire ha o il \acr{suid} bit o lo \acr{sgid} -bit settato, nel qual caso \textit{effective user ID} e \textit{effective - group ID} vengono settati rispettivamente all'utente o al gruppo cui il file -appartiene (per i dettagli vedi \secref{sec:proc_perms}). - -Se il file da eseguire è in formato \emph{a.out} e necessita di librerie -condivise, viene lanciato il \textit{linker} dinamico \cmd{ld.so} prima del -programma per caricare le librerie necessarie ed effettuare il link -dell'eseguibile. Se il programma è in formato ELF per caricare le librerie -dinamiche viene usato l'interprete indicato nel segmento \macro{PT\_INTERP}, -in genere questo è \file{/lib/ld-linux.so.1} per programmi linkati con le -\emph{libc5}, e \file{/lib/ld-linux.so.2} per programmi linkati con le -\emph{glibc}. Infine nel caso il file sia uno script esso deve iniziare con -una linea nella forma \cmd{\#!/path/to/interpreter} dove l'interprete indicato -deve esse un valido programma (binario, non un altro script) che verrà -chiamato come se si fosse eseguito il comando \cmd{interpreter [arg] - filename}. - -Con la famiglia delle \func{exec} si chiude il novero delle funzioni su cui è -basata la gestione dei processi in unix: con \func{fork} si crea un nuovo -processo, con \func{exec} si avvia un nuovo programma, con \func{exit} e -\func{wait} si effettua e verifica la conclusione dei programmi. Tutte le -altre funzioni sono ausiliarie e servono la lettura e il settaggio dei vari -parametri connessi ai processi. +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 anche sez.~\ref{sec:file_fcntl}) per ciascun 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 le \acr{libc5}, e \sysfile{/lib/ld-linux.so.2} per programmi +collegati con le \acr{glibc}. + +Infine nel caso il file sia uno script esso deve iniziare con una linea nella +forma \cmd{\#!/path/to/interpreter [argomenti]} dove l'interprete indicato +deve essere un programma valido (binario, 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 + \href{http://www.in-ulm.de/~mascheck/various/shebang/} + {\textsf{http://www.in-ulm.de/\tild mascheck/various/shebang/}}.} + +Con la famiglia delle \func{exec} si chiude il novero delle funzioni su cui è +basata la gestione 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. @@ -1229,128 +1619,146 @@ parametri connessi ai processi. \label{sec:proc_perms} In questa sezione esamineremo le problematiche relative al controllo di -accesso dal punto di vista del processi; vedremo quali sono gli identificatori +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, e le varie funzioni per la loro manipolazione diretta e tutte -le problematiche connesse alla gestione accorta dei privilegi. - - -\subsection{Utente e gruppo di un processo} -\label{sec:proc_user_group} - -Come accennato in \secref{sec:intro_multiuser} il modello base\footnote{in - realtà già esistono estensioni di questo modello base, che lo rendono più - flessibile e controllabile, come le \textit{capabilities}, le ACL per i file - o il \textit{Mandatory Access Control} di SELinux} di sicurezza di un -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. - -%Benché il sistema sia piuttosto semplice (è basato su un solo livello di -% separazione) il sistema permette una -%notevole flessibilità, - -Abbiamo già accennato come il sistema associ ad ogni utente e gruppo due -identificatori univoci, lo \acr{uid} e il \acr{gid}; questi servono al kernel -per identificare uno specifico utente o un gruppo di utenti, per poi poter +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.} 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. + +Abbiamo già accennato come il sistema associ ad ogni utente e gruppo due +identificatori univoci, lo user-ID ed il group-ID; 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 \secref{sec:file_access_control} vedremo come ad ogni file vengano +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 \acr{uid} ed un \acr{gid}) che vengono controllati dal kernel nella gestione dei permessi di accesso. -Dato che tutte le operazioni del sistema vengono compiute dai processi, è +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 processo, e pertanto -anche a ciascuno di essi è associato un utente e a un gruppo. - -Un semplice controllo di una corrispondenza fra identificativi però 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 gli unix prevedono che i processi abbiano almeno due gruppi di -identificatori, chiamati rispettivamente \textit{real} ed \textit{effective}. +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 gli Unix +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|p{6.5cm}|} + \begin{tabular}[c]{|c|c|l|p{7.3cm}|} \hline - \textbf{Suffisso} & \textbf{Significato} & \textbf{Utilizzo} \\ + \textbf{Suffisso} & \textbf{Gruppo} & \textbf{Denominazione} + & \textbf{Significato} \\ \hline \hline - \acr{uid} & \textit{real user id} & indica l'utente che ha lanciato - il programma\\ - \acr{gid} & \textit{real group id} & indica il gruppo dell'utente - che ha lanciato il programma \\ - \acr{euid} & \textit{effective user id} & indica l'utente usato - dal programma nel controllo di accesso \\ - \acr{egid} & \textit{effective group id} & indica il gruppo - usato dal programma nel controllo di accesso \\ - -- & \textit{supplementary group id} & indica i gruppi cui - l'utente appartiene \\ - -- & \textit{saved user id} & copia dell'\acr{euid} iniziale\\ - -- & \textit{saved group id} & copia dell'\acr{egid} iniziale \\ - \acr{fsuid} & \textit{filesystem user id} & indica l'utente effettivo per - il filesystem \\ - \acr{fsgid} & \textit{filesystem group id} & indica il gruppo effettivo - per il filesystem \\ + \acr{uid} & \textit{real} & \textsl{user-ID reale} + & Indica l'utente che ha lanciato il programma.\\ + \acr{gid} & '' &\textsl{group-ID reale} + & Indica il gruppo principale dell'utente che ha lanciato + il programma.\\ + \hline + \acr{euid} & \textit{effective} &\textsl{user-ID effettivo} + & Indica l'utente usato nel controllo di accesso.\\ + \acr{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} + & È una copia dell'\acr{euid} iniziale.\\ + -- & '' & \textsl{group-ID salvato} + & È una copia dell'\acr{egid} iniziale.\\ + \hline + \acr{fsuid} & \textit{filesystem} &\textsl{user-ID di filesystem} + & Indica l'utente effettivo per l'accesso al filesystem. \\ + \acr{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 con - indicazione dei suffissi usate dalle varie funzioni di manipolazione.} + indicazione dei suffissi usati dalle varie funzioni di manipolazione.} \label{tab:proc_uid_gid} \end{table} -Al primo gruppo appartengono il \textit{real user ID} e il \textit{real group - ID}: questi vengono settati al login ai valori corrispondenti all'utente con -cui si accede al sistema (e relativo gruppo di default). Servono per -l'identificazione dell'utente e normalmente non vengono mai cambiati. In -realtà vedremo (in \secref{sec:proc_setuid}) che è possibile modificarli, ma -solo ad un processo che abbia i privilegi di amministratore; questa -possibilità è usata ad esempio da \cmd{login} che una volta completata la -procedura di autenticazione lancia una shell per la quale setta questi -identificatori ai valori corrispondenti all'utente che entra nel sistema. - -Al secondo gruppo appartengono l'\textit{effective user ID} e -l'\textit{effective group ID} (a cui si aggiungono gli eventuali -\textit{supplementary group id} dei gruppi dei quale l'utente fa parte). -Questi sono invece gli identificatori usati nella verifiche dei permessi del -processo e per il controllo di accesso ai file (argomento affrontato in -dettaglio in \secref{sec:file_perm_overview}). +Al primo gruppo appartengono l'\textsl{user-ID reale} ed il \textsl{group-ID + 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. + +Al secondo gruppo appartengono lo \textsl{user-ID effettivo} ed il +\textsl{group-ID effettivo} (a cui si aggiungono gli eventuali \textsl{group-ID + 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 -\textsl{reale} tranne nel caso in cui, come visto in \secref{sec:proc_exec}, -il programma che si è posto in esecuzione abbia i bit \acr{suid} o \acr{sgid} -settati (il significato di questi bit è affrontato in dettaglio in -\secref{sec:file_suid_sgid}). In questo caso essi saranno settati all'utente e -al gruppo proprietari del file; questo consente, per programmi in cui ci sia -necessità, di dare a qualunque utente normale privilegi o permessi di -un'altro (o dell'amministratore). - -Come nel caso del \acr{pid} e del \acr{ppid} tutti questi identificatori -possono essere letti dal processo attraverso delle opportune funzioni, i cui -prototipi sono i seguenti: +\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 necessità, di dare a qualunque utente normale privilegi o permessi +di un altro (o dell'amministratore). + +Come nel caso del \acr{pid} e del \acr{ppid}, anche tutti questi +identificatori possono essere letti attraverso le rispettive funzioni: +\funcd{getuid}, \funcd{geteuid}, \funcd{getgid} e \funcd{getegid}, i loro +prototipi sono: \begin{functions} \headdecl{unistd.h} \headdecl{sys/types.h} - \funcdecl{uid\_t getuid(void)} restituisce il \textit{real user ID} del - processo corrente. - - \funcdecl{uid\_t geteuid(void)} restituisce l'\textit{effective user ID} del + \funcdecl{uid\_t getuid(void)} Restituisce l'\textsl{user-ID reale} del processo corrente. - \funcdecl{gid\_t getgid(void)} restituisce il \textit{real group ID} del + \funcdecl{uid\_t geteuid(void)} Restituisce l'\textsl{user-ID effettivo} del processo corrente. - \funcdecl{gid\_t getegid(void)} restituisce l'\textit{effective group ID} del + \funcdecl{gid\_t getgid(void)} Restituisce il \textsl{group-ID reale} del processo corrente. - \noindent Queste funzioni non riportano condizioni di errore. + \funcdecl{gid\_t getegid(void)} Restituisce il \textsl{group-ID effettivo} + del processo corrente. + + \bodydesc{Queste funzioni non riportano condizioni di errore.} \end{functions} -In generale l'uso di privilegi superiori deve essere limitato il più +In generale l'uso di privilegi superiori 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 @@ -1358,401 +1766,2204 @@ 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}, analoghi ai precedenti. 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 +\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{saved user id} e il \textit{saved group id} sono copie -dell'\textit{effective user id} e dell'\textit{effective group id} del -processo padre, e vengono settati dalla funzione \func{exec} all'avvio del -processo, come copie dell'\textit{effective user id} e dell'\textit{effective - group id} dopo che questo sono stati settati tenendo conto di eventuali -\acr{suid} o \acr{sgid}. Essi quindi consentono di tenere traccia di quale -fossero utente e gruppo effettivi all'inizio dell'esecuzione di un nuovo -programma. - -Il \textit{filesystem user id} e il \textit{filesystem group id} sono una -estensione introdotta in Linux per rendere più sicuro l'uso di NFS (torneremo -sull'argomento in \secref{sec:proc_setfsuid}). Essi sono una replica dei -corrispondenti \textit{effective id}, ai quali si sostituiscono per tutte le -operazioni di verifica dei permessi relativi ai file (trattate in -\secref{sec:file_perm_overview}). Ogni cambiamento effettuato sugli -\textit{effective id} viene automaticamente riportato su di essi, per cui in -condizioni normali se ne può tranquillamente ignorare l'esistenza, in quanto -saranno del tutto equivalenti ai precedenti. - -Uno specchietto riassuntivo, contenente l'elenco completo degli identificatori -di utente e gruppo associati dal kernel ad ogni processo, è riportato in -\tabref{tab:proc_uid_gid}. - - -\subsection{Le funzioni \func{setuid} e \func{setgid}} +L'\textsl{user-ID salvato} ed il \textsl{group-ID salvato} sono copie +dell'\textsl{user-ID effettivo} e del \textsl{group-ID effettivo} del processo +padre, e vengono impostati dalla funzione \func{exec} all'avvio del processo, +come copie dell'\textsl{user-ID effettivo} e del \textsl{group-ID 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'\textsl{user-ID di filesystem} e il \textsl{group-ID 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 due funzioni che vengono usate per cambiare identità (cioè utente e gruppo -di appartenenza) ad un processo sono rispettivamente \func{setuid} e -\func{setgid}; come accennato in \secref{sec:proc_user_group} in Linux esse -seguono la semantica POSIX che prevede l'esistenza di \textit{saved user id} e -\textit{saved group id}; i loro prototipi sono: +Le due funzioni più comuni che vengono usate per cambiare identità (cioè +utente e gruppo di appartenenza) ad un processo sono rispettivamente +\funcd{setuid} e \funcd{setgid}; come accennato in +sez.~\ref{sec:proc_access_id} in Linux esse seguono la semantica POSIX che +prevede l'esistenza dell'\textit{user-ID salvato} e del \textit{group-ID + salvato}; i loro prototipi sono: \begin{functions} \headdecl{unistd.h} \headdecl{sys/types.h} -\funcdecl{int setuid(uid\_t uid)} setta l'\textit{user ID} del processo +\funcdecl{int setuid(uid\_t uid)} Imposta l'\textsl{user-ID} del processo corrente. -\funcdecl{int setgid(gid\_t gid)} setta il \textit{group ID} del processo +\funcdecl{int setgid(gid\_t gid)} Imposta il \textsl{group-ID} del processo corrente. -Le funzioni restituiscono 0 in caso di successo e -1 in caso di fallimento: -l'unico errore possibile è \macro{EPERM}. +\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso + di fallimento: l'unico errore possibile è \errval{EPERM}.} \end{functions} -Il funzionamento di queste due funzioni è analogo, per cui considereremo solo +Il funzionamento di queste due funzioni è analogo, per cui considereremo solo la prima; la seconda si comporta esattamente allo stesso modo facendo -riferimento al \textit{group id} invece che all'\textit{user id}. Gli -eventuali \textit{supplementary group id} non vengono modificati da nessuna -delle funzioni che tratteremo in questa sezione. - - -L'effetto della chiamata è diverso a seconda dei privilegi del processo; se -l'\textit{effective user id} è zero (cioè è quello dell'amministratore di -sistema) allora tutti gli identificatori (\textit{real}, \textit{effective} -e \textit{saved}) vengono settati al valore specificato da \var{uid}, -altrimenti viene settato solo l'\textit{effective user id}, e soltanto se il -valore specificato corrisponde o al \textit{real user id} o al \textit{saved - user id}. Negli altri casi viene segnalato un errore (con \macro{EPERM}). - -Come accennato l'uso principale di queste funzioni è quello di poter -consentire ad un programma con i bit \acr{suid} o \acr{sgid} settati di -riportare l'\textit{effective user id} 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 dell'uso di queste funzioni prediamo quello con cui -viene gestito l'accesso al file \file{/var/log/utmp}. In questo file viene -registrato chi sta usando il sistema al momento corrente; chiaramente non può +riferimento al \textsl{group-ID} invece che all'\textsl{user-ID}. Gli +eventuali \textsl{group-ID supplementari} non vengono modificati. + +L'effetto della chiamata è diverso a seconda dei privilegi del processo; se +l'\textsl{user-ID 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'\textsl{user-ID effettivo}, e soltanto se il +valore specificato corrisponde o all'\textsl{user-ID reale} o +all'\textsl{user-ID 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'\textsl{user-ID 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/log/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 -\file{/var/log/wtmp} su cui vengono registrati login e logout) appartengono ad -un gruppo dedicato (\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} settato. - -Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato la -situazione degli identificatori è la seguente: +\sysfile{/var/log/wtmp} su cui vengono registrati login e logout) appartengono +ad un gruppo dedicato (\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} - \textit{real group id} &=& \textrm{\acr{gid} (del chiamante)} \\ - \textit{effective group id} &=& \textrm{\acr{utmp}} \\ - \textit{saved group id} &=& \textrm{\acr{utmp}} + \textsl{group-ID reale} &=& \textrm{\acr{gid} (del chiamante)} \\ + \textsl{group-ID effettivo} &=& \textrm{\acr{utmp}} \\ + \textsl{group-ID salvato} &=& \textrm{\acr{utmp}} \end{eqnarray*} -in questo modo, dato che l'\textit{effective group id} è quello giusto, il -programma può accedere a \file{/var/log/utmp} in scrittura ed aggiornarlo, a -questo punto il programma può eseguire una \func{setgid(getgid())} per settare -l'\textit{effective group id} a quello dell'utente (e dato che il \textit{real - group id} 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: +in questo modo, dato che il \textsl{group-ID effettivo} è quello giusto, il +programma può accedere a \sysfile{/var/log/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} - \textit{real group id} &=& \textrm{\acr{gid} (invariato)} \\ - \textit{effective group id} &=& \textrm{\acr{gid}} \\ - \textit{saved group id} &=& \textrm{\acr{utmp} (invariato)} + \textsl{group-ID reale} &=& \textrm{\acr{gid} (invariato)} \\ + \textsl{group-ID effettivo} &=& \textrm{\acr{gid}} \\ + \textsl{group-ID salvato} &=& \textrm{\acr{utmp} (invariato)} \end{eqnarray*} e ogni processo lanciato dal terminale avrebbe comunque \acr{gid} come -\textit{effective group id}. All'uscita dal terminale, per poter di nuovo -aggiornare lo stato di \file{/var/log/utmp} il programma eseguirà una -\func{setgid(utmp)} (dove \var{utmp} è il valore numerico associato al gruppo -\acr{utmp}, ottenuto ad esempio con una \func{getegid}), dato che in questo -caso il valore richiesto corrisponde al \textit{saved group id} la funzione -avrà successo e riporterà la situazione a: +\textsl{group-ID effettivo}. All'uscita dal terminale, per poter di nuovo +aggiornare lo stato di \sysfile{/var/log/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} - \textit{real group id} &=& \textrm{\acr{gid} (invariato)} \\ - \textit{effective group id} &=& \textrm{\acr{utmp}} \\ - \textit{saved group id} &=& \textrm{\acr{utmp} (invariato)} + \textsl{group-ID reale} &=& \textrm{\acr{gid} (invariato)} \\ + \textsl{group-ID effettivo} &=& \textrm{\acr{utmp}} \\ + \textsl{group-ID salvato} &=& \textrm{\acr{utmp} (invariato)} \end{eqnarray*} -consentendo l'accesso a \file{/var/log/utmp}. - -Occorre però tenere conto che tutto questo non è possibile con un processo con -i privilegi di root, in tal caso infatti l'esecuzione 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'\textit{effective user id} del processo per cedere i privilegi occorre -ricorrere ad altre funzioni (si veda ad esempio \secref{sec:proc_seteuid}). - - -\subsection{Le funzioni \func{setreuid} e \func{setresuid}} -\label{sec:proc_setreuid} - -Queste due funzioni derivano da BSD che non supportando\footnote{almeno fino - alla versione 4.3+BSD TODO, verificare e aggiornare la nota} i \textit{saved - id} le usava per poter scambiare fra di loro effective e real id. I -prototipi sono: +consentendo l'accesso a \sysfile{/var/log/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'\textsl{user-ID effettivo} del processo per cedere i privilegi occorre +ricorrere ad altre funzioni. + +Le due funzioni \funcd{setreuid} e \funcd{setregid} derivano da BSD che, non +supportando\footnote{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{functions} \headdecl{unistd.h} \headdecl{sys/types.h} -\funcdecl{int setreuid(uid\_t ruid, uid\_t euid)} setta il \textit{real user - ID} e l'\textit{effective user ID} del processo corrente ai valori -specificati da \var{ruid} e \var{euid}. +\funcdecl{int setreuid(uid\_t ruid, uid\_t euid)} Imposta l'\textsl{user-ID + reale} e l'\textsl{user-ID effettivo} del processo corrente ai valori +specificati da \param{ruid} e \param{euid}. -\funcdecl{int setregid(gid\_t rgid, gid\_t egid)} setta il \textit{real group - ID} e l'\textit{effective group ID} del processo corrente ai valori -specificati da \var{rgid} e \var{egid}. +\funcdecl{int setregid(gid\_t rgid, gid\_t egid)} Imposta il \textsl{group-ID + reale} ed il \textsl{group-ID effettivo} del processo corrente ai valori +specificati da \param{rgid} e \param{egid}. -Le funzioni restituiscono 0 in caso di successo e -1 in caso di fallimento: -l'unico errore possibile è \macro{EPERM}. +\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso + di fallimento: l'unico errore possibile è \errval{EPERM}.} \end{functions} -I processi non privilegiati possono settare i \textit{real id} soltanto ai -valori dei loro \textit{effective id} o \textit{real id} e gli -\textit{effective id} ai valori dei loro \textit{real id}, \textit{effective - id} o \textit{saved id}; valori diversi comportano il fallimento della -chiamata; l'amministratore invece può specificare un valore qualunque. -Specificando un valore di -1 l'identificatore corrispondente viene lasciato -inalterato. - -Con queste funzione si possono scambiare fra loro \textit{real id} e -\textit{effective id}, e pertanto è possibile implementare un comportamento -simile a quello visto in precedenza per \func{setgid}, cedendo i privilegi con -un primo scambio, e recuperandoli, eseguito il lavoro non privilegiato, con un -secondo scambio. - -In questo caso però occorre porre molta attenzione quando si creano nuovi +La due funzioni sono analoghe ed il loro comportamento è identico; quanto +detto per la prima riguardo l'user-ID, si applica immediatamente alla seconda +per il group-ID. I processi non privilegiati possono impostare solo i valori +del loro user-ID effettivo o reale; 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 user-ID reale e +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, 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 \textit{real id} privilegiato, che dovrà +questo caso infatti essi avranno un user-ID 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 i \textit{real id} agli -\textit{effective id}) in caso contrario quest'ultimo potrebbe a sua volta -effettuare uno scambio e riottenere privilegi non previsti. +programma (occorrerà cioè eseguire un'altra chiamata dopo la \func{fork} e +prima della \func{exec} per uniformare l'user-ID reale a quello effettivo) in +caso contrario il nuovo programma potrebbe a sua volta effettuare uno scambio +e riottenere privilegi non previsti. Lo stesso problema di propagazione dei privilegi ad eventuali processi figli -si porrebbe per i \textit{saved id}. Queste funzioni derivano da -un'implementazione che non ne prevede la presenza, e quindi non è possibile -usarle per correggere la situazione come nel caso precedente, per questo -motivo tutte le volte che uno degli identificatori viene modificato ad un -valore diverso dal precedente \textit{real id}, il \textit{saved id} viene -sempre settato al valore dell'\textit{effective id}. +si pone per l'user-ID salvato: questa funzione deriva da un'implementazione 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'user-ID reale +corrente, l'user-ID salvato viene automaticamente uniformato al valore +dell'user-ID effettivo. + +Altre due funzioni, \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{functions} +\headdecl{unistd.h} +\headdecl{sys/types.h} +\funcdecl{int seteuid(uid\_t uid)} Imposta l'user-ID effettivo del processo +corrente a \param{uid}. -\subsection{Le funzioni \func{setresuid} e \func{setresgid}} -\label{sec:proc_setresuid} +\funcdecl{int setegid(gid\_t gid)} Imposta il group-ID effettivo del processo +corrente a \param{gid}. -Queste due funzioni sono una estensione introdotta in Linux dal kernel 2.1.44, -e permettono un completo controllo su tutti gli identificatori (\textit{real}, -\textit{effective} e \textit{saved}), i prototipi sono: +\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso + di fallimento: l'unico errore è \errval{EPERM}.} +\end{functions} + +Come per le precedenti le due funzioni sono identiche, per cui tratteremo solo +la prima. Gli utenti normali possono impostare l'user-ID effettivo solo al +valore dell'user-ID reale o dell'user-ID salvato, l'amministratore può +specificare qualunque valore. Queste funzioni sono usate per permettere +all'amministratore di impostare solo l'user-ID effettivo, dato che l'uso +normale di \func{setuid} comporta l'impostazione di tutti gli identificatori. + + +Le due funzioni \funcd{setresuid} e \funcd{setresgid} sono invece +un'estensione introdotta in Linux,\footnote{per essere precisi 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{functions} \headdecl{unistd.h} \headdecl{sys/types.h} -\funcdecl{int setresuid(uid\_t ruid, uid\_t euid, uid\_t suid)} setta il -\textit{real user ID}, l'\textit{effective user ID} e il \textit{saved user - ID} del processo corrente ai valori specificati rispettivamente da -\var{ruid}, \var{euid} e \var{suid}. +\funcdecl{int setresuid(uid\_t ruid, uid\_t euid, uid\_t suid)} Imposta +l'user-ID reale, l'user-ID effettivo e l'user-ID salvato del processo corrente +ai valori specificati rispettivamente da \param{ruid}, \param{euid} e +\param{suid}. -\funcdecl{int setresgid(gid\_t rgid, gid\_t egid, gid\_t sgid)} setta il -\textit{real group ID}, l'\textit{effective group ID} e il \textit{saved group - ID} del processo corrente ai valori specificati rispettivamente da -\var{rgid}, \var{egid} e \var{sgid}. +\funcdecl{int setresgid(gid\_t rgid, gid\_t egid, gid\_t sgid)} Imposta il +group-ID reale, il group-ID effettivo ed il group-ID salvato del processo +corrente ai valori specificati rispettivamente da \param{rgid}, \param{egid} e +\param{sgid}. -Le funzioni restituiscono 0 in caso di successo e -1 in caso di fallimento: -l'unico errore possibile è \macro{EPERM}. +\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso + di fallimento: l'unico errore è \errval{EPERM}.} \end{functions} -I processi non privilegiati possono cambiare uno qualunque degli -identificatori usando uno qualunque dei valori correnti di \textit{real id}, -\textit{effective id} o \textit{saved id}, l'amministratore può specificare i -valori che vuole; un valore di -1 per un qualunque parametro lascia inalterato -l'identificatore corrispondente. +Le due funzioni sono identiche, quanto detto per la prima riguardo gli user-ID +si applica alla seconda per i group-ID. I processi non privilegiati possono +cambiare uno qualunque degli user-ID solo ad un valore corrispondente o +all'user-ID 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 esistono anche due controparti che permettono di leggere +in blocco i vari identificatori: \funcd{getresuid} e \funcd{getresgid}; i loro +prototipi sono: +\begin{functions} +\headdecl{unistd.h} +\headdecl{sys/types.h} +\funcdecl{int getresuid(uid\_t *ruid, uid\_t *euid, uid\_t *suid)} Legge +l'user-ID reale, l'user-ID effettivo e l'user-ID salvato del processo corrente. + +\funcdecl{int getresgid(gid\_t *rgid, gid\_t *egid, gid\_t *sgid)} Legge il +group-ID reale, il group-ID effettivo e il group-ID salvato del processo +corrente. -\subsection{Le funzioni \func{seteuid} e \func{setegid}} -\label{sec:proc_seteuid} +\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso di + fallimento: l'unico errore possibile è \errval{EFAULT} se gli indirizzi delle + variabili di ritorno non sono validi.} +\end{functions} -Queste funzioni sono un'estensione allo standard POSIX.1 (ma sono comunque -supportate dalla maggior parte degli unix) e usate per cambiare gli -\textit{effective id}; i loro prototipi sono: +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'user-ID effettivo o l'user-ID 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'user-ID 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 usate per cambiare questi identificatori sono \funcd{setfsuid} +e \funcd{setfsgid}, ovviamente sono specifiche di Linux e non devono essere +usate se si intendono scrivere programmi portabili; i loro prototipi sono: \begin{functions} -\headdecl{unistd.h} -\headdecl{sys/types.h} +\headdecl{sys/fsuid.h} + +\funcdecl{int setfsuid(uid\_t fsuid)} Imposta l'user-ID di filesystem del +processo corrente a \param{fsuid}. -\funcdecl{int seteuid(uid\_t uid)} setta l'\textit{effective user ID} del -processo corrente a \var{uid}. +\funcdecl{int setfsgid(gid\_t fsgid)} Imposta il group-ID di filesystem del +processo corrente a \param{fsgid}. + +\bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso + di fallimento: l'unico errore possibile è \errval{EPERM}.} +\end{functions} +\noindent queste 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_sysconf}), 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 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{functions} + \headdecl{sys/types.h} + \headdecl{unistd.h} + + \funcdecl{int getgroups(int size, gid\_t list[])} + + Legge gli identificatori dei gruppi supplementari. + + \bodydesc{La funzione restituisce il numero di gruppi letti in caso di + successo e -1 in caso di fallimento, nel qual caso \var{errno} assumerà + i 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{functions} -\funcdecl{int setegid(gid\_t gid)} setta l'\textit{effective group ID} del -processo corrente a \var{gid}. +La funzione legge gli identificatori dei gruppi supplementari del processo sul +vettore \param{list} di dimensione \param{size}. Non è specificato se la +funzione inserisca o meno nella lista il group-ID effettivo del processo. Se si +specifica un valore di \param{size} uguale a 0 \param{list} non viene +modificato, ma si ottiene il numero di gruppi supplementari. -Le funzioni restituiscono 0 in caso di successo e -1 in caso di fallimento: -l'unico errore possibile è \macro{EPERM}. +Una seconda funzione, \funcd{getgrouplist}, può invece essere usata per +ottenere tutti i gruppi a cui appartiene un certo utente; il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{grp.h} + + \funcdecl{int getgrouplist(const char *user, gid\_t group, gid\_t *groups, + int *ngroups)} Legge i gruppi supplementari. + + \bodydesc{La funzione legge fino ad un massimo di \param{ngroups} valori, + restituisce 0 in caso di successo e -1 in caso di fallimento.} \end{functions} -Gli utenti normali possono settare l'\textit{effective id} solo al valore del -\textit{real id} o del \textit{saved id}, l'amministratore può specificare -qualunque valore. Queste funzioni sono usate per permettere a root di settare -solo l'\textit{effective id}, dato che l'uso normale di \func{setuid} comporta -il settaggio di tutti gli identificatori. +La funzione legge i gruppi supplementari dell'utente specificato da +\param{user}, eseguendo una scansione del database dei gruppi (si veda +sez.~\ref{sec:sys_user_group}). Ritorna poi in \param{groups} la lista di +quelli a cui l'utente appartiene. Si noti che \param{ngroups} è passato come +puntatore perché, qualora il valore specificato sia troppo piccolo, la +funzione ritorna -1, passando indietro il numero dei gruppi trovati. + +Per impostare i gruppi supplementari di un processo ci sono due funzioni, che +possono essere usate solo se si hanno i privilegi di amministratore. La prima +delle due è \funcd{setgroups}, ed il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{grp.h} + + \funcdecl{int setgroups(size\_t size, gid\_t *list)} + + Imposta i gruppi supplementari del processo. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + fallimento, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EFAULT}] \param{list} non ha un indirizzo valido. + \item[\errcode{EPERM}] il processo non ha i privilegi di amministratore. + \item[\errcode{EINVAL}] il valore di \param{size} è maggiore del valore + massimo consentito. + \end{errlist}} +\end{functions} + +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 è +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 \funcd{initgroups} il cui prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{grp.h} + + \funcdecl{int initgroups(const char *user, gid\_t group)} + + Inizializza la lista dei gruppi supplementari. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + fallimento, nel qual caso \var{errno} assumerà gli stessi valori di + \func{setgroups} più \errval{ENOMEM} quando non c'è memoria sufficiente + per allocare lo spazio per informazioni dei gruppi.} +\end{functions} + +La funzione esegue la scansione del database dei gruppi (usualmente +\conffile{/etc/group}) cercando i gruppi di cui è membro l'utente \param{user} +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 user space, anche quando si hanno più processori (e dei processi che sono +eseguiti davvero in contemporanea), le politiche di 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 +\textbf{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. -\subsection{Le funzioni \func{setfsuid} e \func{setfsgid}} -\label{sec:proc_setfsuid} - -Queste funzioni sono usate per settare gli identificatori usati da Linux per -il controllo dell'accesso ai file. Come già accennato in -\secref{sec:proc_user_group} in Linux è definito questo ulteriore gruppo di -identificatori, che di norma sono assolutamente equivalenti agli -\textit{effective id}, dato che ogni cambiamento di questi ultimi viene -immediatamente riportato sui \textit{filesystem id}. - -C'è un solo caso in cui si ha necessità di introdurre una differenza fra -\textit{effective id} e \textit{filesystem id}, 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'\textit{effective id} o il \textit{real id} il server si -espone alla ricezione di eventuali segnali ostili da parte dell'utente di cui -ha temporaneamente assunto l'identità. Cambiando solo il \textit{filesystem - id} si ottengono i privilegi necessari per accedere ai file, mantenendo -quelli originari per quanto riguarda tutti gli altri controlli di accesso. - -Le due funzioni usate per cambiare questi identificatori sono \func{setfsuid} -e \func{setfsgid}, ovviamente sono specifiche di Linux e non devono essere -usate se si intendono scrivere programmi portabili; i loro prototipi sono: +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|p{2.4cm}|c|p{9cm}|} + \hline + \textbf{Stato} & \texttt{STAT} & \textbf{Descrizione} \\ + \hline + \hline + \textbf{Runnable}& \texttt{R} & Il processo è in esecuzione o è pronto ad + essere eseguito (cioè è in attesa che gli + venga assegnata la CPU).\\ + \textbf{Sleep} & \texttt{S} & Il processo è in attesa di un + risposta dal sistema, ma può essere + interrotto da un segnale.\\ + \textbf{Uninterrutible Sleep}& \texttt{D} & Il processo è in + attesa di un risposta dal sistema (in + genere per I/O), e non può essere + interrotto in nessuna circostanza.\\ + \textbf{Stopped} & \texttt{T} & Il processo è stato fermato con un + \signal{SIGSTOP}, o è tracciato.\\ + \textbf{Zombie}\index{zombie} & \texttt{Z} & Il processo è terminato ma il + suo stato di terminazione non è ancora + stato letto dal padre.\\ + \textbf{Killable}& \texttt{D} & Un nuovo stato introdotto con il kernel + 2.6.25, sostanzialmente identico + all'\textbf{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 (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 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, possano 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 +real-time,\footnote{per sistema 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 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 +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 \textbf{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 \textbf{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 di \var{nice} nullo e nessuno è privilegiato rispetto agli altri; +specificando un valore 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 modificare la \textit{niceness} di +un processo; la più semplice è funzione \funcd{nice}, che opera sul processo +corrente, il suo prototipo è: +\begin{prototype}{unistd.h} +{int nice(int inc)} + Aumenta il valore di \var{nice} per il processo corrente. + + \bodydesc{La funzione ritorna zero o il nuovo valore di \var{nice} in caso + di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere + i valori: + \begin{errlist} + \item[\errcode{EPERM}] non si ha il permesso di specificare un valore + di \param{inc} negativo. + \end{errlist}} +\end{prototype} + +L'argomento \param{inc} indica l'incremento da effettuare rispetto al valore +di \var{nice} corrente: quest'ultimo 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 più avanti) la priorità dei propri processi. + +Gli standard SUSv2 e POSIX.1 prevedono che la funzione ritorni il nuovo valore +di \var{nice} del processo; tuttavia la 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$ è un valore di \var{nice} legittimo e questo +comporta una confusione con una eventuale condizione di errore. La system call +originaria inoltre non consente, se non dotati di adeguati privilegi, di +diminuire un valore di \var{nice} precedentemente innalzato. + +Fino alle \acr{glibc} 2.2.4 la funzione di libreria riportava direttamente il +risultato dalla 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 system call, con questa +versione viene restituito come valore di ritorno il valore di \var{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 \funcd{getpriority}, derivata da BSD; il suo prototipo è: +\begin{prototype}{sys/resource.h} +{int getpriority(int which, int who)} + +Restituisce il valore di \var{nice} per l'insieme dei processi specificati. + + \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{ESRCH}] non c'è nessun processo che corrisponda ai valori di + \param{which} e \param{who}. + \item[\errcode{EINVAL}] il valore di \param{which} non è valido. + \end{errlist}} +\end{prototype} +\noindent nelle vecchie versioni può essere necessario includere anche +\file{}, questo non è più necessario con versioni recenti delle +librerie, ma è comunque utile per portabilità. + +La funzione permette, a seconda del valore di \param{which}, di leggere la +priorità di un processo, di un gruppo di processi (vedi +sez.~\ref{sec:sess_proc_group}) o di un utente, specificando un corrispondente +valore per \param{who} secondo la legenda di tab.~\ref{tab:proc_getpriority}; +un valore nullo di quest'ultimo indica 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} + +La funzione restituisce la priorità più alta (cioè il valore più basso) fra +quelle dei processi specificati; di nuovo, dato che $-1$ è un valore +possibile, per poter rilevare una condizione di errore è necessario cancellare +sempre \var{errno} prima della chiamata alla funzione per verificare che essa +resti uguale a zero. + +Analoga a \func{getpriority} è la funzione \funcd{setpriority} che permette di +impostare la priorità di uno o più processi; il suo prototipo è: +\begin{prototype}{sys/resource.h} +{int setpriority(int which, int who, int prio)} + Imposta la priorità per l'insieme dei processi specificati. + + \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore, + nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{ESRCH}] non c'è nessun processo che corrisponda ai valori di + \param{which} e \param{who}. + \item[\errcode{EINVAL}] il valore di \param{which} non è valido. + \item[\errcode{EACCES}] si è richiesto un aumento di priorità senza avere + sufficienti privilegi. + \item[\errcode{EPERM}] un processo senza i privilegi di amministratore ha + cercato di modificare la priorità di un processo di un altro utente. + \end{errlist}} +\end{prototype} + +La funzione imposta la priorità al valore specificato da \param{prio} per +tutti i processi indicati dagli argomenti \param{which} e \param{who}. 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}. 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'user-ID reale o quello +effettivo del processo chiamante corrispondessero all'user-ID 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'user-ID 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}). + + +\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 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 hard real-time. In tal caso infatti gli + interrupt vengono intercettati dall'interfaccia 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 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 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 scheduling che si è +scelta; lo standard ne prevede due: +\begin{basedescript}{\desclabelwidth{1.2cm}\desclabelstyle{\nextlinelabel}} +\item[\textsf{FIFO}] \textit{First In First Out}. Il processo viene eseguito + fintanto che non cede volontariamente la CPU (con \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[\textsf{RR}] \textit{Round Robin}. 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 \textbf{Runnable} entrano nel + \textsl{girotondo}. +\end{basedescript} + +Lo standard POSIX.1-2001 prevede una funzione che consenta sia di modificare +le politiche di scheduling, passando da real-time a ordinarie o viceversa, che +di specificare, in caso di politiche real-time, la eventuale priorità statica; +la funzione è \funcd{sched\_setscheduler} ed il suo prototipo è: +\begin{prototype}{sched.h} +{int sched\_setscheduler(pid\_t pid, int policy, const struct sched\_param *p)} + Imposta priorità e politica di scheduling. + + \bodydesc{La funzione ritorna 0 in caso di successo e $-$1 in caso di + errore, nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \item[\errcode{EINVAL}] il valore di \param{policy} non esiste o il + relativo valore di \param{p} non è valido. + \item[\errcode{EPERM}] il processo non ha i privilegi per attivare la + politica richiesta. + \end{errlist}} +\end{prototype} + +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 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 real-time, quella bassa le politiche ordinarie. Un valore negativo +per \param{policy} mantiene la politica di scheduling corrente. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Policy} & \textbf{Significato} \\ + \hline + \hline + \const{SCHED\_FIFO} & Scheduling real-time con politica \textit{FIFO}. \\ + \const{SCHED\_RR} & Scheduling real-time con politica \textit{Round + Robin}. \\ + \hline + \const{SCHED\_OTHER}& Scheduling ordinario.\\ + \const{SCHED\_BATCH}& Scheduling ordinario con l'assunzione ulteriore di + lavoro \textit{CPU intensive}.\footnotemark\\ + \const{SCHED\_IDLE} & Scheduling di priorità estremamente + bassa.\footnotemark\\ + \hline + \end{tabular} + \caption{Valori dell'argomento \param{policy} per la funzione + \func{sched\_setscheduler}.} + \label{tab:proc_sched_policy} +\end{table} + +\footnotetext[44]{introdotto con il kernel 2.6.16.} +\footnotetext{introdotto con il kernel 2.6.23.} + +Con le versioni più recenti del kernel sono state introdotte anche delle +varianti sulla politica di 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 +\textbf{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 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]{\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 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 i due valori della massima e minima +priorità statica possano essere ottenuti, per ciascuna delle politiche di +scheduling \textit{real-time}, tramite le due funzioni +\funcd{sched\_get\_priority\_max} e \funcd{sched\_get\_priority\_min}, i cui +prototipi sono: \begin{functions} -\headdecl{sys/fsuid.h} + \headdecl{sched.h} + + \funcdecl{int sched\_get\_priority\_max(int policy)} Legge il valore + massimo della priorità statica per la politica di scheduling \param{policy}. + + + \funcdecl{int sched\_get\_priority\_min(int policy)} Legge il valore minimo + della priorità statica per la politica di scheduling \param{policy}. + + \bodydesc{La funzioni ritornano il valore della priorità in caso di successo + e $-1$ in caso di errore, nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{policy} non è valido. + \end{errlist}} +\end{functions} + +Si tenga presente che quando si imposta una politica di 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 \textbf{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 \textbf{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'user-ID effettivo del +processo chiamante corrisponda all'user-ID reale o effettivo del processo +indicato con \param{pid}. + +Fino al kernel 2.6.12 gli utenti normali non potevano impostare politiche +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 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 scheduling indipendentemente dal valore di +\const{RLIMIT\_RTPRIO}. Inoltre, in caso di processo già sottoposto ad una +politica 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 \funcd{sched\_setparam} e \funcd{sched\_getparam} che +consentono rispettivamente di impostarne e leggerne il valore, i loro +prototipi sono: +\begin{functions} + \headdecl{sched.h} + + \funcdecl{int sched\_setparam(pid\_t pid, const struct sched\_param *param)} + Imposta la priorità statica del processo \param{pid}. + + \funcdecl{int sched\_getparam(pid\_t pid, struct sched\_param *param)} + Legge la priorità statica del processo \param{pid}. + + \bodydesc{Entrambe le funzioni ritornano 0 in caso di successo e $-1$ in + caso di errore, nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \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. + \end{errlist}} +\end{functions} + +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 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'header +\file{sched.h}. + +Se invece si vuole sapere quale è politica di scheduling di un processo si può +usare la funzione \funcd{sched\_getscheduler}, il cui prototipo è: +\begin{prototype}{sched.h} +{int sched\_getscheduler(pid\_t pid)} + Legge la politica di scheduling per il processo \param{pid}. + + \bodydesc{La funzione ritorna la politica di scheduling in caso di successo + e $-1$ in caso di errore, nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \item[\errcode{EPERM}] non si hanno privilegi sufficienti per eseguire + l'operazione. + \end{errlist}} +\end{prototype} -\funcdecl{int setfsuid(uid\_t fsuid)} setta il \textit{filesystem user ID} del -processo corrente a \var{fsuid}. +La funzione restituisce il valore, secondo quanto elencato in +tab.~\ref{tab:proc_sched_policy}, della politica di scheduling per il processo +specificato; se l'argomento \param{pid} è nullo viene restituito il valore +relativo al processo chiamante. + +L'ultima funzione 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{prototype}{sched.h} + {int sched\_rr\_get\_interval(pid\_t pid, struct timespec *tp)} Legge in + \param{tp} la durata della \textit{time-slice} per il processo \param{pid}. + + \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore, + nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \item[\errcode{ENOSYS}] la system call non è stata implementata. + \end{errlist}} +\end{prototype} + +La funzione restituisce il valore dell'intervallo di tempo usato per la +politica \textit{round robin} in una struttura \struct{timespec}, (la cui +definizione si può trovare in fig.~\ref{fig:sys_timeval_struct}). In realtà +dato che in Linux questo intervallo di tempo è prefissato e non modificabile, +questa funzione ritorna sempre un valore di 150 millisecondi, e non importa +specificare il PID di un processo reale. + +Come accennato ogni processo può rilasciare volontariamente la CPU in modo da +consentire agli altri processi di essere eseguiti; la funzione che consente di +fare tutto ciò è \funcd{sched\_yield}, il cui prototipo è: +\begin{prototype}{sched.h} + {int sched\_yield(void)} + + Rilascia volontariamente l'esecuzione. + + \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore, + nel qual caso \var{errno} viene impostata opportunamente.} +\end{prototype} -\funcdecl{int setfsgid(gid\_t fsgid)} setta l'\textit{filesystem group ID} del -processo corrente a \var{fsgid}. +Questa funzione ha un utilizzo effettivo soltanto quando si usa lo scheduling +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 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 \texttt{/proc/sys/kernel/sched\_compat\_yield}.} + + + +\subsection{Il controllo dello \textit{scheduler} per i sistemi + multiprocessore} +\label{sec:proc_sched_multiprocess} + +Infine 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 +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 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 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 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 system call per la gestione + della \textit{CPU affinity} sono state introdotte nel kernel 2.5.8, e le + funzioni di libreria nelle \textsl{glibc} 2.3.} è stata introdotta +l'opportuna infrastruttura ed una nuova system call che permette di impostare +su quali processori far eseguire un determinato processo attraverso una +\textsl{maschera di affinità}. La corrispondente funzione di libreria è +\funcd{sched\_setaffinity} ed il suo prototipo è: +\begin{prototype}{sched.h} + {int sched\_setaffinity (pid\_t pid, unsigned int cpusetsize, const + cpu\_set\_t *cpuset)} + Imposta la maschera di affinità del processo \param{pid}. + + \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore, + nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \item[\errcode{EINVAL}] il valore di \param{cpuset} contiene riferimenti a + processori non esistenti nel sistema. + \item[\errcode{EPERM}] il processo non ha i privilegi sufficienti per + eseguire l'operazione. + \end{errlist} + ed inoltre anche \errval{EFAULT}.} +\end{prototype} + + +Questa funzione e la corrispondente \func{sched\_setaffinity} hanno una storia +abbastanza complessa, la system call prevede l'uso di due ulteriori argomenti +di tipo \texttt{unsigned int len} e \texttt{unsigned long *mask}, che +corrispondono al fatto che la implementazione effettiva usa una semplice +maschera binaria. Quando le funzioni vennero incluse nelle \acr{glibc} +assunsero invece il prototipo appena mostrato. A complicare la cosa si +aggiunge il fatto che nella versione 2.3.3 delle \acr{glibc} l'argomento +\param{cpusetsize} è stato eliminato, per poi essere ripristinato nella +versione 2.3.4.\footnote{pertanto se la vostra pagina di manuale non è + aggiornata, o usate quella particolare versione delle \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 \file{sched.h}.} + +La funzione imposta, con l'uso del valore contenuto all'indirizzo +\param{cpuset}, 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 della 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 real-time o +la cui risposta è critica) e si vuole la massima velocità, con questa +interfaccia diventa possibile selezionare gruppi di processori utilizzabili in +maniera esclusiva. Lo stesso dicasi quando l'accesso a certe risorse (memoria +o periferiche) può avere un costo diverso a seconda del processore, come +avviene nelle architetture NUMA (\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. + +Per facilitare l'uso dell'argomento \param{cpuset} le \acr{glibc} hanno +introdotto un apposito dato di tipo, \type{cpu\_set\_t},\footnote{questa è una + estensione specifica delle \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 è +una maschera binaria: in generale è un intero a 32 bit in cui ogni bit +corrisponde ad un processore, ma dato che per architetture particolari il +numero di bit di un intero può non essere sufficiente, è stata creata questa +che è una interfaccia generica che permette di usare a basso livello un tipo +di dato qualunque rendendosi indipendenti dal numero di bit e dalla loro +disposizione. + +Questa interfaccia, oltre alla definizione del tipo di dato apposito, prevede +anche una serie di macro di preprocessore per la manipolazione dello stesso, +che consentono di svuotare un insieme, aggiungere o togliere un processore da +esso o verificare se vi è già presente: +\begin{functions} + \headdecl{sched.h} + \funcdecl{void \macro{CPU\_ZERO}(cpu\_set\_t *set)} + Inizializza l'insieme (vuoto). -Le funzioni restituiscono 0 in caso di successo e -1 in caso di fallimento: -l'unico errore possibile è \macro{EPERM}. + \funcdecl{void \macro{CPU\_SET}(int cpu, cpu\_set\_t *set)} + Inserisce il processore \param{cpu} nell'insieme. + + \funcdecl{void \macro{CPU\_CLR}(int cpu, cpu\_set\_t *set)} + Rimuove il processore \param{cpu} nell'insieme. + + \funcdecl{int \macro{CPU\_ISSET}(int cpu, cpu\_set\_t *set)} + Controlla se il processore \param{cpu} è nell'insieme. +\end{functions} + +Oltre a queste macro, simili alle analoghe usate per gli insiemi di file +descriptor (vedi sez.~\ref{sec:file_select}) è definita la costante +\const{CPU\_SETSIZE} che indica il numero massimo di processori che possono +far parte dell'insieme, e che costituisce un limite massimo al valore +dell'argomento \param{cpu}. + +In generale la maschera di affinità è preimpostata in modo che un processo +possa essere eseguito su qualunque processore, se può comunque leggere il +valore per un processo specifico usando la funzione +\funcd{sched\_getaffinity}, il suo prototipo è: +\begin{prototype}{sched.h} + {int sched\_getaffinity (pid\_t pid, unsigned int cpusetsize, + const cpu\_set\_t *cpuset)} + Legge la maschera di affinità del processo \param{pid}. + + \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore, + nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \item[\errcode{EFAULT}] il valore di \param{cpuset} non è un indirizzo + valido. + \end{errlist} } +\end{prototype} + +La funzione restituirà all'indirizzo specificato da \param{cpuset} il valore +della maschera di affinità del processo, così da poterla riutilizzare per una +successiva reimpostazione. In questo caso non sono necessari privilegi +particolari. + +È 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 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 scheduler CFQ (\textit{Completely Fair Queuing}) è +divenuto possibile, qualora si usi questo 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 + scheduler.} + +La scelta dello scheduler di I/O si può fare in maniera generica a livello di +avvio del kernel assegnando il nome dello stesso al parametro +\texttt{elevator}, mentre se ne può indicare uno per l'accesso al singolo +disco scrivendo nel file \texttt{/sys/block/\textit{dev}/queue/scheduler} +(dove \texttt{\textit{dev}} è il nome del dispositivo associato al disco); gli +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 + scheduler \textit{Completely Fair Queuing}.} che supporta le priorità. Per i +dettagli sulle caratteristiche specifiche degli altri 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 scheduler CFQ ci sono due specifiche system +call, specifiche di Linux, che consentono di leggere ed impostare le priorità +di I/O.\footnote{se usate in corrispondenza ad uno scheduler diverso il loro + utilizzo non avrà alcun effetto.} Dato che non esiste una interfaccia +diretta nelle \acr{glibc} per queste due funzioni occorrerà invocarle tramite +la funzione \func{syscall} (come illustrato in +sez.~\ref{sec:proc_syscall}). Le due funzioni sono \funcd{ioprio\_get} ed +\funcd{ioprio\_set}; i rispettivi prototipi sono: +\begin{functions} + \headdecl{linux/ioprio.h} + \funcdecl{int ioprio\_get(int which, int who)} + \funcdecl{int ioprio\_set(int which, int who, int ioprio)} + + Rileva o imposta la priorità di I/O di un processo. + + \bodydesc{Le funzioni ritornano rispettivamente un intero positivo + (indicante la priorità) o 0 in caso di successo e $-1$ in caso di errore, + nel qual caso \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{ESRCH}] non esiste il processo indicato. + \item[\errcode{EINVAL}] i valori di \param{which} e \param{who} non sono + validi. + \item[\errcode{EPERM}] non si hanno i privilegi per eseguire + l'impostazione (solo per \func{ioprio\_set}). + \end{errlist} } \end{functions} -Queste 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 \textit{real}, \textit{effective} o \textit{saved id}. +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 o 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 scheduling di I/O +del processo, l'altra che esprime, quando la classe di 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}. + +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 +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|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} + +Le classi di scheduling previste dallo scheduler CFQ sono tre, e ricalcano tre +diverse modalità di distribuzione delle risorse analoghe a quelle già adottate +anche nel funzionamento dello 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. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Classe} & \textbf{Significato} \\ + \hline + \hline + \const{IOPRIO\_CLASS\_RT} & Scheduling di I/O \textit{real time}.\\ + \const{IOPRIO\_CLASS\_BE} & Scheduling di I/O ordinario.\\ + \const{IOPRIO\_CLASS\_IDLE}& Scheduling di I/O di priorità minima.\\ + \hline + \end{tabular} + \caption{Costanti che identificano le classi di scheduling di I/O.} + \label{tab:IOsched_class} +\end{table} + +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 user-ID reale corrisponde all'user-ID 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/ + +%TODO trattare le funzionalità per il NUMA +% vedi man numa e le pagine di manuale relative +% vedere anche dove metterle... + + +\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 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}, \func{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 come quelli trattati finora, che di un \textit{thread}, come quelli +che vedremo in sez.~\ref{sec:linux_thread}, in cui la memoria viene condivisa +fra il processo chiamante ed il nuovo processo creato. 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 PID, l'albero dei +file, dei \textit{mount point}, della 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}.} 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}, le \acr{glibc} definiscono 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{functions} + \headdecl{sys/sched.h} + + \funcdecl{int clone(int (*fn)(void *), void *child\_stack, int + flags, void *arg, ... \\ + /* pid\_t *ptid, struct user\_desc *tls, pid\_t *ctid */)} + + Crea un nuovo processo o \textit{thread} eseguendo la funzione \param{fn}. + + \bodydesc{La funzione ritorna al chiamante il \textit{Thread ID} assegnato + al nuovo processo in caso di successo e $-1$ in caso di errore, nel qual + caso \var{errno} può assumere i 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{functions} + +La funzione prende come primo argomento 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} del tipo indicato, 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. + +Il comportamento di \func{clone}, che si riflette sulle caratteristiche del +nuovo processo da essa creato, è controllato dall'argomento \param{flags}, + +\begin{basedescript}{\desclabelstyle{\pushlabel}} + +\item[\const{CLONE\_CHILD\_CLEARTID}] +\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} + + +\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 è \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{functions} + \headdecl{sys/prctl.h} + + \funcdecl{int prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5)} + + Esegue una operazione speciale sul processo corrente. + + \bodydesc{La funzione ritorna 0 o un valore positivo dipendente + dall'operazione in caso di successo e $-1$ in caso di errore, nel qual + caso \var{errno} assumerà valori diversi a seconda del tipo di operazione + richiesta (in genere \errval{EINVAL} o \errval{EPERM}). } +\end{functions} + +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: + +\begin{basedescript}{\desclabelstyle{\pushlabel}} +\item[\const{PR\_CAPBSET\_READ}] Controlla la disponibilità di una delle + \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 + \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} del + processo. L'operazione richiede i privilegi di amministratore (la capacità + \const{CAP\_SETPCAP}), altrimenti la chiamata fallirà con un errore di + \errval{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 \textit{user-ID} 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.\footnote{la 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 \textit{endianess} 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 \textit{endianess} 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 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 \code{(int *)}. Introdotta a + partire dal kernel 2.4.18, solo su 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: \const{PR\_FP\_EXC\_SW\_ENABLE} per usare FPEXC per + le eccezioni, \const{PR\_FP\_EXC\_DIV} per la divisione per zero in virgola + mobile, \const{PR\_FP\_EXC\_OVF} per gli overflow, \const{PR\_FP\_EXC\_UND} + per gli underflow, \const{PR\_FP\_EXC\_RES} per risultati non esatti, + \const{PR\_FP\_EXC\_INV} per operazioni invalide, + \const{PR\_FP\_EXC\_DISABLED} per disabilitare le eccezioni, + \const{PR\_FP\_EXC\_NONRECOV} per utilizzare la modalità di eccezione + asincrona non recuperabile, \const{PR\_FP\_EXC\_ASYNC} per utilizzare la + modalità di eccezione asincrona recuperabile, \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.} 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 \code{(int *)}. Introdotta a + partire dal kernel 2.4.21, solo su PowerPC. +\item[\const{PR\_SET\_KEEPCAPS}] Consente di controllare quali + \textit{capabilities} vengono cancellate quando si esegue un cambiamento di + \textit{user-ID} 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 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 \code{(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 \code{(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 \code{(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 \textit{secure computing mode} il + processo potrà utilizzare soltanto un insieme estremamente limitato di + \textit{system call}: \func{read}, \func{write}, \func{\_exit} e + \func{sigreturn}, ogni altra \textit{system call} porterà all'emissione di + un \func{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 \textit{secure computing mode} + comporterebbe l'emissione di \texttt{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 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}, 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. 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 \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 \code{(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 + \procfile{/proc/sys/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 funzione \func{ptrace}} +\label{sec:process_ptrace} + +Da fare + +% TODO: trattare PTRACE_SEIZE, aggiunta con il kernel 3.1 + + +\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 + +Da fare + +% TODO: funzioni varie sparse citate da qualche parte e da trattare forse in +% una sezione a parte: sigreturn, \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 multiutente -occorre tenere conto di tutta una serie di problematiche che normalmente non +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. +programma alla volta. -Pur non essendo tutto questo direttamente legato alla modalità specifica in -cui il multitasking è implementato in un sistema unix-like, né al solo -concetto di multitasking (le stesse problematiche si presentano ad esempio -nella gestione degli interrupt hardware), in questa sezione conclusiva del -capitolo in cui abbiamo affrontato la gestione dei processi, introdurremo -sinteticamente queste problematiche, che ritroveremo a più riprese in capitoli -successivi, con una breve definizione della terminologia e delle loro -caratteristiche di fondo. +Pur 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. \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 una operazione è atomica +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à +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ò +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 \textit{race condition} (vedi -\secref{sec:proc_race_cond}) derivanti da operazioni interrotte in una fase in -cui non erano ancora state completate. +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 +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 -\capref{cha:IPC}) o nella operazioni con i file (vedremo alcuni esempi in -\secref{sec:file_atomic}). In questi casi in genere l'uso delle appropriate -funzioni di libreria per compiere le operazioni necessarie è garanzia -sufficiente di atomicità in quanto le system call con cui esse sono realizzate +cap.~\ref{cha:IPC}) o nelle operazioni con i file (vedremo alcuni esempi in +sez.~\ref{sec:file_atomic}). In questi casi in genere l'uso delle appropriate +funzioni di libreria per compiere le operazioni necessarie è garanzia +sufficiente di atomicità in quanto le 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 +Nel caso dei segnali invece la situazione è molto più delicata, in quanto lo stesso processo, e pure alcune system call, possono essere interrotti in qualunque momento, e le operazioni di un eventuale \textit{signal handler} -sono compiute nello stesso spazio di indirizzi del processo. Per questo anche -solo il solo accesso o l'assegnazione di una variabile possono non essere più -operazioni atomiche (torneremo su questi aspetti in \secref{sec:sign_xxx}). +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}). 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 -\type{int} (e 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. In questi casi è anche opportuno marcare come \type{volatile} le -variabili che possono essere interessate ad accesso condiviso, onde evitare -problemi con le ottimizzazioni del codice. +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. 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} e i \textit{deadlock}} + +\subsection{Le \textit{race condition} ed i \textit{deadlock}} \label{sec:proc_race_cond} -Si definisce una \textit{race condition} il caso in cui diversi processi -stanno cercando di fare qualcosa con una risorsa comune ed il risultato finale -viene a dipendere dall'ordine di esecuzione dei medesimi. Ovviamente dato che -l'ordine di esecuzione di un processo rispetto agli altri, senza appositi -meccanismi di sincronizzazione, non è assolutamente prevedibile, queste -situazioni sono fonti di errori molto subdoli, che possono verificarsi solo in -condizioni particolari e quindi difficilmente riproducibili. - -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 le risorse -condivise siano opportunamente protette da meccanismi di sincronizzazione -(torneremo su queste problematiche di questo tipo in \secref{sec:ipc_semaph}). - -Un caso particolare di \textit{race condition} sono poi i cosiddetti -\textit{deadlock}; l'esempio tipico è quello di un flag di ``occupazione'' che -viene rilasciato da un evento asincrono fra il controllo (in cui viene trovato -occupato) e la successiva messa in attesa, che a questo punto diventerà -perpetua (da cui il nome di \textit{deadlock}) in quanto l'evento di sblocco -del flag è stato perso fra il controllo e la messa in attesa. +\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}). + +\itindbeg{deadlock} Un caso particolare di \textit{race condition} sono poi i +cosiddetti \textit{deadlock} (traducibile in \textsl{condizioni di stallo}), +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} -Si dice rientrante una funzione che può essere interrotta in qualunque momento -ed essere chiamata da capo (da questo il nome) da un altro filone di -esecuzione (thread e manipolatori di segnali sono i casi in cui occorre -prestare attenzione a questa problematica) senza che questo comporti nessun -problema. - -In genere una funzione non è rientrante se opera direttamente su memoria che -non è nello stack. Ad esempio una funzione non è rientrante se usa una -variabile globale o statica od un oggetto allocato dinamicamente che trova da -sola: due chiamate alla stessa funzione interferiranno. 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. - -Le glibc mettono a disposizione due macro di compilatore \macro{\_REENTRANT} e -\macro{\_THREAD\_SAFE} per assicurare che siano usate delle versioni rientranti -delle funzioni di libreria. - +\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, le +\acr{glibc} però mettono a disposizione due macro di compilatore,\footnote{si + ricordi quanto illustrato in sez.~\ref{sec:intro_gcc_glibc_std}.} +\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|)} + + +% 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 ForkTest 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 Stopped +% LocalWords: Uninterrutible SIGSTOP soft slice nice niceness counter which SC +% LocalWords: getpriority who setpriority RTLinux RTAI Adeos fault FIFO First +% 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 Killable PR +% 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 +% LocalWords: WNOTHREAD DUMPABLE KEEPCAPS IRIX CAPBSET endianess 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 +% LocalWords: secure computing sigreturn TIMING STATISTICAL TSC MCE conditions +% LocalWords: timestamp Stamp SIGSEGV UNALIGN SIGBUS MCEERR AO failure early + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "gapil" +%%% End: