From 6db03808a639644251507d0f90f953117f893866 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 17 Feb 2002 23:14:45 +0000 Subject: [PATCH 01/16] Correzioni varie parte terza --- filedir.tex | 278 ++++++++++++++++++++++++++------------------------ fileintro.tex | 169 +++++++++++++++--------------- 2 files changed, 230 insertions(+), 217 deletions(-) diff --git a/filedir.tex b/filedir.tex index 24f331b..7601b73 100644 --- a/filedir.tex +++ b/filedir.tex @@ -4,11 +4,11 @@ In questo capitolo tratteremo in dettaglio le modalità con cui si gestiscono file e directory, iniziando dalle funzioni di libreria che si usano per copiarli, spostarli e cambiarne i nomi. Esamineremo poi l'interfaccia che -permette la manipolazione dei vari attributi di file e directory ed alla -fine faremo una trattazione dettagliata su come è strutturato il sistema base -di protezioni e controllo di accesso ai file e sulle funzioni che ne -permettono la gestione. Tutto quello che riguarda invece la manipolazione del -contenuto dei file è lasciato ai capitoli successivi. +permette la manipolazione dei vari attributi di file e directory ed alla fine +faremo una trattazione dettagliata su come è strutturato il sistema base di +protezioni e controllo dell'accesso ai file e sulle funzioni che ne permettono +la gestione. Tutto quello che riguarda invece la manipolazione del contenuto +dei file è lasciato ai capitoli successivi. @@ -20,7 +20,7 @@ direttamente dall'architettura del sistema; in questa sezione esamineremo le funzioni usate per manipolazione nel filesytem di file e directory, per la creazione di link simbolici e diretti, per la gestione e la lettura delle directory; il tutto mettendo in evidenza le conseguenze della struttura -standard della gestione dei file in un sistema unix-like, già accennate al +standard della gestione dei file in un sistema unix-like, introdotta nel capitolo precedente. @@ -46,7 +46,8 @@ riferimento al suddetto inode. Questo significa che la realizzazione di un link è immediata in quanto uno stesso file può avere tanti nomi diversi allo stesso tempo, dati da altrettante diverse associazioni allo stesso inode; si noti poi che nessuno di -questi nomi viene ad assumere una particolare preferenza rispetto agli altri. +questi nomi viene ad assumere una particolare preferenza o originalità +rispetto agli altri. Per aggiungere un nome ad un inode si utilizza la funzione \func{link}; si suole chiamare questo tipo di associazione un collegamento diretto (o @@ -90,9 +91,9 @@ meccanismo non Windows). La funzione inoltre opera sia sui file ordinari che sugli altri oggetti del -filesystem, con l'eccezione delle directory. In alcuni versioni di unix solo +filesystem, con l'eccezione delle directory. In alcune versioni di Unix solo l'amministratore è in grado di creare un collegamento diretto ad un'altra -directory, questo viene fatto perché con una tale operazione è possibile +directory: questo viene fatto perché con una tale operazione è possibile creare dei circoli nel filesystem (vedi l'esempio mostrato in \secref{sec:file_symlink}, dove riprenderemo il discorso) che molti programmi non sono in grado di gestire e la cui rimozione diventerebbe estremamente @@ -142,9 +143,10 @@ restrizioni Una delle caratteristiche di queste funzioni è che la creazione/rimozione della nome dalla directory e l'incremento/decremento del numero di riferimenti -nell'inode deve essere una operazione atomica (si veda -\secref{sec:proc_atom_oper}), per questo entrambe queste funzioni sono -realizzate tramite una singola system call. +nell'inode devono essere effettuati in maniera atomica (si veda +\secref{sec:proc_atom_oper}) senza possibili interruzioni fra le due +operazioni, per questo entrambe queste funzioni sono realizzate tramite una +singola system call. Si ricordi infine che il file non viene eliminato dal disco fintanto che tutti i riferimenti ad esso sono stati cancellati, solo quando il \textit{link @@ -223,7 +225,7 @@ esiste, non deve essere una directory (altrimenti si ha l'errore \macro{EISDIR}). Nel caso \var{newpath} indichi un file esistente questo viene cancellato e rimpiazzato (atomicamente). -Se \var{oldpath} è una directory allora \var{newpath} se esiste deve essere +Se \var{oldpath} è una directory allora \var{newpath}, se esiste, deve essere una directory vuota, altrimenti si avranno gli errori \macro{ENOTDIR} (se non è una directory) o \macro{ENOTEMPTY} (se non è vuota). Chiaramente \var{newpath} non può contenere \var{oldpath} altrimenti si avrà un errore @@ -233,9 +235,9 @@ Se \var{oldpath} si riferisce a un link simbolico questo sar \var{newpath} è un link simbolico verrà cancellato come qualunque altro file. Infine qualora \var{oldpath} e \var{newpath} siano due nomi dello stesso file lo standard POSIX prevede che la funzione non dia errore, e non faccia nulla, -lasciando entrambi i nomi; Linux segue questo standard, anche se come fatto -notare dal manuale delle glibc, il comportamento più ragionevole sarebbe -quello di cancellare \var{oldpath}. +lasciando entrambi i nomi; Linux segue questo standard, anche se, come fatto +notare dal manuale delle \textit{glibc}, il comportamento più ragionevole +sarebbe quello di cancellare \var{oldpath}. Il vantaggio nell'uso di questa funzione al posto della chiamata successiva di \func{link} e \func{unlink} è che l'operazione è eseguita atomicamente, non @@ -246,7 +248,7 @@ eseguita. In ogni caso se \var{newpath} esiste e l'operazione fallisce per un qualche motivo (come un crash del kernel), \func{rename} garantisce di lasciare -presente una istanza di \var{newpath}, tuttavia nella sovrascrittura potrà +presente una istanza di \var{newpath}. Tuttavia nella sovrascrittura potrà esistere una finestra in cui sia \var{oldpath} che \var{newpath} fanno riferimento allo stesso file. @@ -256,16 +258,16 @@ riferimento allo stesso file. Come abbiamo visto in \secref{sec:file_link} la funzione \func{link} crea riferimenti agli inodes, pertanto può funzionare soltanto per file che -risiedono sullo stesso filesystem e solo per un filesystem di tipo unix. +risiedono sullo stesso filesystem e solo per un filesystem di tipo Unix. Inoltre abbiamo visto che in Linux non è consentito eseguire un link diretto ad una directory. -Per ovviare a queste limitazioni i sistemi unix supportano un'altra forma di +Per ovviare a queste limitazioni i sistemi Unix supportano un'altra forma di link (i cosiddetti \textit{soft link} o \textit{symbolic link}), che sono, come avviene in altri sistemi operativi, dei file speciali che contengono il semplicemente il riferimento ad un altro file (o directory). In questo modo è -possibile effettuare link anche attraverso filesystem diversi, a file posti in -filesystem che non supportano i link diretti, a delle directory, e anche a +possibile effettuare link anche attraverso filesystem diversi, a file posti +in filesystem che non supportano i link diretti, a delle directory, ed anche a file che non esistono ancora. Il sistema funziona in quanto i link simbolici sono contrassegnati come tali @@ -298,9 +300,8 @@ specificato. La funzione che permette di creare un nuovo link simbolico Si tenga presente che la funzione non effettua nessun controllo sull'esistenza di un file di nome \param{oldpath}, ma si limita ad inserire quella stringa nel link simbolico. Pertanto un link simbolico può anche riferirsi ad un file -che non esiste: quello che viene chiamato un \textit{dangling link}, -letteralmente \textsl{link ciondolante}. - +che non esiste: in questo caso si ha quello che viene chiamato un +\textit{dangling link}, letteralmente un \textsl{link ciondolante}. Come accennato i link simbolici sono risolti automaticamente dal kernel all'invocazione delle varie system call; in \ntab\ si è riportato un elenco @@ -362,12 +363,8 @@ la funzione \func{readlink}, il cui prototipo \var{buff} o -1 per un errore, nel qual caso la variabile \var{errno} viene settata a: \begin{errlist} - \item[\macro{EINVAL}] \var{file} non è un link simbolico o \var{size} non è - positiva. - \item[\macro{EROFS}] La directory su cui si vuole inserire il nuovo link è - su un filesystem montato in sola lettura. - \item[\macro{ENOSPC}] La directory o il filesystem in cui si vuole creare il - link è piena e non c'è ulteriore spazio disponibile. + \item[\macro{EINVAL}] \param{path} non è un link simbolico o \param{size} + non è positiva. \end{errlist} ed inoltre \macro{ENOTDIR}, \macro{ENAMETOOLONG}, \macro{ENOENT}, \macro{EACCES}, \macro{ELOOP}, \macro{EIO}, \macro{EFAULT} e @@ -382,7 +379,7 @@ stringa con un carattere nullo e la tronca alla dimensione specificata da \begin{figure}[htb] \centering - \includegraphics[width=5cm]{img/link_loop} + \includegraphics[width=7cm]{img/link_loop} \caption{Esempio di loop nel filesystem creato con un link simbolico.} \label{fig:file_link_loop} \end{figure} @@ -392,27 +389,28 @@ cosiddetti \textit{loop}. La situazione la struttura della directory \file{/boot}. Come si vede si è creato al suo interno un link simbolico che punta di nuovo a \file{/boot}\footnote{Questo tipo di loop è stato effettuato per poter permettere a \cmd{grub} (un - bootloader estremamente avanzato in grado di accedere direttamente - attraverso vari filesystem al file da lanciare come sistema operativo) di - vedere i file in questa directory, che è montata su una partizione separata - (e che grub vedrebbe come radice), con lo stesso path con cui verrebbero - visti dal sistema operativo.}. + bootloader in grado di leggere direttamente da vari filesystem il file da + lanciare come sistema operativo) di vedere i file in questa directory con lo + stesso path con cui verrebbero visti dal sistema operativo, anche se essi si + trovano, come è solito, su una partizione separata (e che \cmd{grub} + vedrebbe come radice).}. Questo può causare problemi per tutti quei programmi che effettuano la scansione di una directory senza tener conto dei link simbolici, ad esempio se -lanciassimo un comando del tipo \cmd{grep -r linux *}, il loop nella directory -porterebbe il comando ad esaminare \file{/boot}, \file{/boot/boot}, +lanciassimo un comando del tipo \code{grep -r linux *}, il loop nella +directory porterebbe il comando ad esaminare \file{/boot}, \file{/boot/boot}, \file{/boot/boot/boot} e così via. Per questo motivo il kernel e le librerie prevedono che nella risoluzione di un pathname possano essere seguiti un numero limitato di link simbolici, il -cui valore limite è specificato dalla costante \macro{MAXSYMLINKS}; qualora +cui valore limite è specificato dalla costante \macro{MAXSYMLINKS}. Qualora questo limite venga superato viene generato un errore ed \var{errno} viene settata al valore \macro{ELOOP}. -Un punto da tenere sempre presente è il fatto che un link simbolico può fare -riferimento anche ad un file che non esiste; ad esempio possiamo creare un -file temporaneo nella nostra directory con un link del tipo: +Un punto da tenere sempre presente è che, come abbiamo accennato, un link +simbolico può fare riferimento anche ad un file che non esiste; ad esempio +possiamo creare un file temporaneo nella nostra directory con un link del +tipo: \begin{verbatim} $ ln -s /tmp/tmp_file temporaneo \end{verbatim}%$ @@ -424,8 +422,8 @@ quanto aprendo in scrittura \file{temporaneo} verr $ cat temporaneo cat: temporaneo: No such file or directory \end{verbatim}%$ -con un errore che può sembrare sbagliato, dato che invece \cmd{ls} ci -mostrerebbe l'esistenza di \file{temporaneo}. +con un errore che può sembrare sbagliato, dato che una ispezione con \cmd{ls} +ci mostrerebbe invece l'esistenza di \file{temporaneo}. \subsection{La creazione e la cancellazione delle directory} @@ -460,7 +458,7 @@ accedere ai tipi usati da queste funzioni si deve includere il file \macro{ENOENT}, \macro{ENOTDIR}, \macro{ENOMEM}, \macro{ELOOP}, \macro{EROFS}.} \end{prototype} - + La funzione crea una nuova directory vuota (che contiene solo le due voci standard \file{.} e \file{..}). I permessi di accesso (vedi la trattazione in \secref{sec:file_access_control}) specificati da \var{mode} (i cui possibili @@ -512,7 +510,7 @@ Finora abbiamo parlato esclusivamente di file, directory e link simbolici; in degli altri tipi di file, come i file di dispositivo e le fifo (i socket sono un caso a parte, che vedremo in \secref{cha:socket_intro}). -La manipolazione delle caratteristiche di questi filee e la loro cancellazione +La manipolazione delle caratteristiche di questi file e la loro cancellazione può essere effettuata con le stesse funzioni che operano sui file normali; ma quando li si devono creare sono necessarie delle funzioni apposite. La prima di queste funzioni è \func{mknod}, il suo prototipo è: @@ -542,9 +540,9 @@ di queste funzioni La funzione permette di creare un file speciale, ma si può usare anche per creare file normali e fifo; l'argomento \param{mode} specifica il tipo di file che si vuole creare ed i relativi permessi, secondo i valori riportati in -\tabref{tab:file_mode_flags}, che vanno combinato come OR binario. I permessi -sono comunque modificati nella maniera usuale dal valore di \var{umask} (si -veda \secref{sec:file_umask}. +\tabref{tab:file_mode_flags}, che vanno combinati con un OR binario. I +permessi sono comunque modificati nella maniera usuale dal valore di +\var{umask} (si veda \secref{sec:file_umask}). Per il tipo di file può essere specificato solo uno fra: \macro{S\_IFREG} per un file normale (che sarà creato vuoto), \macro{S\_IFBLK} per un device a @@ -560,7 +558,7 @@ usando questa funzione; ma in Linux\footnote{la funzione non agli utenti normali. I nuovi inode creati con \func{mknod} apparterranno al proprietario e al -gruppo del processo che li creati, a meno che non si sia attivato il bit +gruppo del processo che li ha creati, a meno che non si sia attivato il bit \acr{sgid} per la directory o sia stata attivata la semantica BSD per il filesystem (si veda \secref{sec:file_ownership}) in cui si va a creare l'inode. @@ -576,7 +574,7 @@ Per creare una fifo (un file speciale, su cui torneremo in dettaglio in \bodydesc{La funzione restituisce zero in caso di successo e -1 per un errore, nel qual caso \var{errno} assumerà i valori \macro{EACCESS}, \macro{EEXIST}, \macro{ENAMETOOLONG}, \macro{ENOENT}, \macro{ENOSPC}, - \macro{ENOTDIR} e\macro{EROFS}.} + \macro{ENOTDIR} e \macro{EROFS}.} \end{functions} \noindent come per \func{mknod} il file \param{pathname} non deve esistere (neanche come link simbolico); al solito i permessi specificati da @@ -671,8 +669,8 @@ pathname ricavato risalendo all'indietro l'albero della directory, si perderebbe traccia di ogni passaggio attraverso eventuali link simbolici. Per cambiare la directory di lavoro corrente si può usare la funzione -\func{chdir} (omonima dell'analogo comando di shell) il cui nome sta appunto -per \textit{change directory}), il suo prototipo è: +\func{chdir} (equivalente del comando di shell \cmd{cd}) il cui nome sta +appunto per \textit{change directory}, il suo prototipo è: \begin{prototype}{unistd.h}{int chdir(const char *pathname)} Cambia la directory di lavoro corrente in \param{pathname}. @@ -711,7 +709,7 @@ specificata da \param{fd}. \subsection{I file temporanei} \label{sec:file_temp_file} -In molte occasioni è utile poter creare dei file temporanei; benchè la cosa +In molte occasioni è utile poter creare dei file temporanei; benché la cosa sembri semplice in realtà il problema è più sottile di quanto non appaia a prima vista. Infatti anche se sembrerebbe banale generare un nome a caso e creare il file dopo aver controllato che questo non esista, nel momento fra il @@ -750,12 +748,12 @@ esplicitamente, il suo prototipo \macro{ENOMEM} qualora fallisca l'allocazione della stringa.} \end{prototype} -La funzione alloca con \code{malloc} la stringa in cui resituisce il nome, per -cui è sempre rientrante, occorre però ricordarsi di disallocare il puntatore -che restituisce. L'argomento \param{pfx} specifica un prefisso di massimo 5 -caratteri per il nome provvisorio. La funzione assegna come directory per il -file temporaneo (verificando che esista e sia accessibili), la prima valida -delle seguenti: +La funzione alloca con \code{malloc} la stringa in cui restituisce il nome, +per cui è sempre rientrante, occorre però ricordarsi di disallocare il +puntatore che restituisce. L'argomento \param{pfx} specifica un prefisso di +massimo 5 caratteri per il nome provvisorio. La funzione assegna come +directory per il file temporaneo (verificando che esista e sia accessibili), +la prima valida delle seguenti: \begin{itemize*} \item La variabile di ambiente \macro{TMPNAME} (non ha effetto se non è definita o se il programma chiamante è \acr{suid} o \acr{sgid}, vedi @@ -788,8 +786,8 @@ POSIX definisce la funzione \func{tempfile}, il cui prototipo ed inoltre \macro{EFAULT}, \macro{EMFILE}, \macro{ENFILE}, \macro{ENOSPC}, \macro{EROFS} e \macro{EACCESS}.} \end{prototype} -\noindent restituisce direttamente uno stream già aperto (in modalità -\code{r+b}, si veda \secref{sec:file_fopen}) e pronto per l'uso che viene +\noindent essa restituisce direttamente uno stream già aperto (in modalità +\code{r+b}, si veda \secref{sec:file_fopen}) e pronto per l'uso, che viene automaticamente cancellato alla sua chiusura o all'uscita dal programma. Lo standard non specifica in quale directory verrà aperto il file, ma \acr{glibc} prima tentano con \macro{P\_tmpdir} e poi con \file{/tmp}. Questa funzione è @@ -817,9 +815,9 @@ funzione non si pu alle possibili \textit{race condition} date per \func{tmpnam} continuano a valere; inoltre in alcune vecchie implementazioni il valore di usato per sostituire le \code{XXXXXX} viene formato con il \acr{pid} del processo più -una lettera, il che mette a disposizione solo 26 possibilità, e rende il nome -temporaneo facile da indovinare. Per tutti questi motivi la funzione è -deprecata e non dovrebbe mai essere usata. +una lettera, il che mette a disposizione solo 26 possibilità diverse per il +nome del file, e rende il nome temporaneo facile da indovinare. Per tutti +questi motivi la funzione è deprecata e non dovrebbe mai essere usata. @@ -838,9 +836,9 @@ prototipo contenuto di \param{template} è indefinito. \end{errlist}} \end{prototype} -\noindent come per \func{mktemp} \param{template} non può essere una stringa -costante. La funzione apre un file in lettura/scrittura con la funzione -\func{open}, usando l'opzione \macro{O\_EXCL} (si veda +\noindent come per \func{mktemp} anche in questo caso \param{template} non può +essere una stringa costante. La funzione apre un file in lettura/scrittura con +la funzione \func{open}, usando l'opzione \macro{O\_EXCL} (si veda \secref{sec:file_open}), in questo modo al ritorno della funzione si ha la certezza di essere i soli utenti del file. I permessi sono settati al valore \code{0600}\footnote{questo è vero a partire dalle \acr{glibc} 2.0.7, le @@ -913,12 +911,15 @@ queste funzioni sono i seguenti: \macro{ELOOP}, \macro{EFAULT}, \macro{EACCESS}, \macro{ENOMEM}, \macro{ENAMETOOLONG}.} \end{functions} +\noindent il loro comportamento è identico, solo che operano rispettivamente +su un file, su un link simbolico e su un file descriptor. -La struttura \var{stat} è definita nell'header \file{sys/stat.h} e in -generale dipende dall'implementazione, la versione usata da Linux è mostrata -in \nfig, così come riportata dalla man page (in realtà la definizione -effettivamente usata nel kernel dipende dall'architettura e ha altri campi -riservati per estensioni come tempi più precisi, o per il padding dei campi). +La struttura \var{stat} usata da queste funzioni è definita nell'header +\file{sys/stat.h} e in generale dipende dall'implementazione, la versione +usata da Linux è mostrata in \nfig, così come riportata dalla man page di +\func{stat} (in realtà la definizione effettivamente usata nel kernel dipende +dall'architettura e ha altri campi riservati per estensioni come tempi più +precisi, o per il padding dei campi). \begin{figure}[!htb] \footnotesize @@ -956,15 +957,16 @@ del sistema (di quelli definiti in \tabref{tab:xxx_sys_types}, e dichiarati in \subsection{I tipi di file} \label{sec:file_types} -Come riportato in \tabref{tab:file_file_types} in Linux oltre ai file e -alle directory esistono vari altri oggetti che possono stare su un filesystem; -il tipo di file è ritornato dalla \func{stat} nel campo \var{st\_mode} -(che è quello che contiene anche le informazioni relative ai permessi). +Come riportato in \tabref{tab:file_file_types} in Linux oltre ai file e alle +directory esistono vari altri oggetti che possono stare su un filesystem. Il +tipo di file è ritornato dalla \func{stat} come maschera binaria nel campo +\var{st\_mode} (che che contiene anche le informazioni relative ai permessi). Dato che il valore numerico può variare a seconda delle implementazioni, lo standard POSIX definisce un insieme di macro per verificare il tipo di files, -queste vengono usate anche da Linux che supporta pure le estensioni per link -simbolici e socket definite da BSD, l'elenco completo di tutte le macro è +queste vengono usate anche da Linux che supporta pure le estensioni allo +standard per i link simbolici e i socket definite da BSD; l'elenco completo +delle macro con cui è possibile estrarre l'informazione da \var{st\_mode} è riportato in \ntab. \begin{table}[htb] \centering @@ -987,10 +989,17 @@ riportato in \ntab. \label{tab:file_type_macro} \end{table} -Oltre a queste macro è possibile usare direttamente il valore di -\var{st\_mode} per ricavare il significato dei vari bit in esso memorizzati, -per questo sempre in \file{sys/stat.h} sono definiti i flag riportati in -\ntab: +Oltre alle macro di \tabref{tab:file_type_macro} è possibile usare +direttamente il valore di \var{st\_mode} per ricavare il tipo di file +controllando direttamente i vari bit in esso memorizzati. Per questo sempre in +\file{sys/stat.h} sono definite le costanti numeriche riportate in \ntab. + +Il primo valore dell'elenco di \secref{tab:file_mode_flags} è la maschera +binaria che permette di estrarre i bit nei quali viene memorizzato il tipo di +file, i valori successivi sono le costanti corrispondenti ai singoli bit, e +possono essere usati per effettuare la selezione sul tipo di file voluto, con +una opportuna combinazione. + \begin{table}[htb] \centering \footnotesize @@ -1033,11 +1042,9 @@ per questo sempre in \file{sys/stat.h} sono definiti i flag riportati in \label{tab:file_mode_flags} \end{table} -Il primo valore definisce la maschera dei bit usati nei quali viene -memorizzato il tipo di files, mentre gli altri possono essere usati per -effettuare delle selezioni sul tipo di file voluto, combinando opportunamente -i vari flag; ad esempio se si volesse controllare se un file è una directory o -un file ordinario si potrebbe definire la condizione: +Ad esempio se si volesse impostare una condizione che permetta di controllare +se un file è una directory o un file ordinario si potrebbe definire la macro +di preprocessore: \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} #define IS_FILE_DIR(x) (((x) & S_IFMT) & (S_IFDIR | S_IFREG)) \end{lstlisting} @@ -1045,12 +1052,12 @@ in cui prima si estraggono da \var{st\_mode} i bit relativi al tipo di file e poi si effettua il confronto con la combinazione di tipi scelta. -\subsection{La dimensione dei file} +\subsection{Le dimensioni dei file} \label{sec:file_file_size} -Il membro \var{st\_size} contiene la dimensione del file in byte (se il file -è un file normale, nel caso di un link simbolico al dimensione è quella del -pathname che contiene). +Il membro \var{st\_size} contiene la dimensione del file in byte (se il file è +un file normale, nel caso di un link simbolico la dimensione è quella del +pathname che contiene). Il campo \var{st\_blocks} definisce la lunghezza del file in blocchi di 512 byte. Il campo \var{st\_blksize} infine definisce la dimensione preferita per @@ -1060,12 +1067,12 @@ dimensione inferiore sarebbe inefficiente. Si tenga conto che lunghezza del file riportata in \var{st\_size} non è detto che corrisponda all'occupazione dello spazio su disco per via della possibile -esistenza dei cosiddetti \textsl{buchi} (detti normalmente \textit{holes}) che +esistenza dei cosiddetti \textit{holes} (letteralmente \textsl{buchi}) che si formano tutte le volte che si va a scrivere su un file dopo aver eseguito una \func{lseek} (vedi \secref{sec:file_lseek}) oltre la sua conclusione corrente. -In tal caso si avranno differenti risultati a seconda del modi in cui si +In questo caso si avranno risultati differenti a seconda del modo in cui si calcola la lunghezza del file, ad esempio il comando \cmd{du}, (che riporta il numero di blocchi occupati) potrà dare una dimensione inferiore, mentre se si legge dal file (ad esempio usando il comando \cmd{wc -c}), dato che in tal @@ -1079,7 +1086,8 @@ presenti al di l Un file può sempre essere troncato a zero aprendolo con il flag \macro{O\_TRUNC}, ma questo è un caso particolare; per qualunque altra -dimensione si possono usare le due funzioni: +dimensione si possono usare le due funzioni \func{truncate} e +\func{ftruncate}, i cui prototipi sono: \begin{functions} \headdecl{unistd.h} \funcdecl{int truncate(const char *file\_name, off\_t length)} Fa si che la dimensione del file \var{file\_name} sia troncata ad @@ -1130,7 +1138,7 @@ riportato un esempio delle funzioni che effettuano cambiamenti su di essi. \begin{tabular}[c]{|c|l|l|c|} \hline \textbf{Membro} & \textbf{Significato} & \textbf{Funzione} - & \textbf{Opzione} \\ + & \textbf{Opzione di \cmd{ls}} \\ \hline \hline \var{st\_atime}& ultimo accesso ai dati del file &\func{read}, @@ -1187,18 +1195,20 @@ quest'ultimo. \begin{tabular}[c]{|l|c|c|c|c|c|c|l|} \hline \multicolumn{1}{|p{3cm}|}{\centering{\vspace{6pt}\textbf{Funzione}}} & - \multicolumn{3}{|p{3cm}|}{\centering{File o directory di riferimento}}& - \multicolumn{3}{|p{3cm}|}{\centering{Directory genitrice del riferimento}} + \multicolumn{3}{|p{3.6cm}|}{\centering{ + \textbf{File o directory del riferimento}}}& + \multicolumn{3}{|p{3.6cm}|}{\centering{ + \textbf{Directory contenente il riferimento}}} &\multicolumn{1}{|p{3.6cm}|}{\centering{\vspace{6pt}\textbf{Note}}} \\ \cline{2-7} \cline{2-7} \multicolumn{1}{|p{3cm}|}{} - &\multicolumn{1}{|p{.8cm}|}{\centering{\textsl{(a)}}} - &\multicolumn{1}{|p{.8cm}|}{\centering{\textsl{(m)}}} - &\multicolumn{1}{|p{.8cm}|}{\centering{\textsl{(c)}}} - &\multicolumn{1}{|p{.8cm}|}{\centering{\textsl{(a)}}} - &\multicolumn{1}{|p{.8cm}|}{\centering{\textsl{(m)}}} - &\multicolumn{1}{|p{.8cm}|}{\centering{\textsl{(c)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(a)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(m)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(c)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(a)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(m)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(c)}}} &\multicolumn{1}{|p{3cm}|}{} \\ \hline \hline @@ -1229,9 +1239,9 @@ quest'ultimo. \func{read} &$\bullet$& & & & & & \\ \func{remove} - & & &$\bullet$& &$\bullet$&$\bullet$& using + & & &$\bullet$& &$\bullet$&$\bullet$& se esegue \func{unlink}\\ \func{remove} - & & & & &$\bullet$&$\bullet$& using + & & & & &$\bullet$&$\bullet$& se esegue \func{rmdir}\\ \func{rename} & & &$\bullet$& &$\bullet$&$\bullet$& per entrambi gli argomenti\\ \func{rmdir} @@ -1253,7 +1263,7 @@ quest'ultimo. \end{table} Si noti infine come \var{st\_ctime} non abbia nulla a che fare con il tempo di -creazione del file, usato in molti altri sistemi operativi, ma che in unix non +creazione del file, usato in molti altri sistemi operativi, ma che in Unix non esiste. Per questo motivo quando si copia un file, a meno di preservare esplicitamente i tempi (ad esempio con l'opzione \cmd{-p} di \cmd{cp}) esso avrà sempre il tempo corrente come data di ultima modifica. @@ -1316,50 +1326,50 @@ le funzioni usate per gestirne i vari aspetti. \subsection{I permessi per l'accesso ai file} \label{sec:file_perm_overview} -Il controllo di accesso ai file in unix segue un modello abbastanza semplice +Il controllo di accesso ai file in Unix segue un modello abbastanza semplice (ma adatto alla gran parte delle esigenze) in cui si dividono i permessi su tre livelli. Si tenga conto poi che quanto diremo è vero solo per filesystem -di tipo unix, e non è detto che sia applicabile a un filesystem +di tipo Unix, e non è detto che sia applicabile a un filesystem qualunque\footnote{ed infatti non è vero per il filesystem vfat di Windows, per il quale i permessi vengono assegnati in maniera fissa con un opzione in - fase di montaggio}. Esistono inoltre estensioni che permettono di + fase di montaggio.}. Esistono inoltre estensioni che permettono di implementare le ACL (\textit{Access Control List}) che sono un meccanismo di controllo di accesso molto più sofisticato. -Ad ogni file unix associa sempre l'utente che ne è proprietario (il cosiddetto -\textit{owner}) e il gruppo di appartenenza, secondo il meccanismo degli -identificatori di utenti e gruppi (\acr{uid} e \acr{gid}). Questi valori +Ad ogni file Linux associa sempre l'utente che ne è proprietario (il +cosiddetto \textit{owner}) e il gruppo di appartenenza, secondo il meccanismo +degli identificatori di utenti e gruppi (\acr{uid} e \acr{gid}). Questi valori sono accessibili da programma tramite i campi \var{st\_uid} e \var{st\_gid} della struttura \var{stat} (si veda \secref{sec:file_stat}). Ad ogni file -viene inoltre associato un insieme di permessi che sono divisi in tre classi, +viene inoltre associato un insieme di permessi che sono divisi in tre livelli, e cioè attribuiti rispettivamente all'utente proprietario del file, a un qualunque utente faccia parte del gruppo cui appartiene il file, e a tutti gli altri utenti. I permessi, così come vengono presi dai comandi e dalle routine di sistema, -sono espressi da un numero di 12 bit; di questi i nove meno significativi sono +sono espressi da un numero a 12 bit; di questi i nove meno significativi sono usati a gruppi di tre per indicare i permessi base di lettura, scrittura ed esecuzione (indicati nei comandi di sistema con le lettere \cmd{w}, \cmd{r} e \cmd{x}) ed applicabili rispettivamente al proprietario, al gruppo, a tutti -gli altri. I restanti tre bit (\acr{suid}, \acr{sgid}, e -\textsl{sticky}) sono usati per indicare alcune caratteristiche più complesse -su cui torneremo in seguito (vedi \secref{sec:file_suid_sgid} e -\secref{sec:file_sticky}). - -Anche i permessi, come tutte le altre informazioni generali, sono tenuti per -ciascun file nell'inode; in particolare essi sono contenuti in alcuni bit -del campo \var{st\_mode} della struttura letta da \func{stat} (di nuovo si veda -\secref{sec:file_stat} per i dettagli). - -In genere ci si riferisce a questo raggruppamento dei permessi usando le -lettere \cmd{u} (per \textit{user}), \cmd{g} (per \textit{group}) e \cmd{o} -(per \textit{other}), inoltre se si vuole indicare tutti i raggruppamenti -insieme si usa la lettera \cmd{a} (per \textit{all}). Si tenga ben presente -questa distinzione dato che in certi casi, mutuando la terminologia in uso nel -VMS, si parla dei permessi base come di permessi per \textit{owner}, -\textit{group} ed \textit{all}, le cui iniziali possono dar luogo a confusione. -Le costanti che permettono di accedere al valore numerico di questi bit nel -campo \var{st\_mode} sono riportate in \ntab. +gli altri. I restanti tre bit (\acr{suid}, \acr{sgid}, e \textsl{sticky}) +sono usati per indicare alcune caratteristiche più complesse del meccanismo +del controllo di accesso su cui torneremo in seguito (in +\secref{sec:file_suid_sgid} e \secref{sec:file_sticky}). + +Anche i permessi, come tutte le altre informazioni pertinenti al file, sono +memorizzati nell'inode; in particolare essi sono contenuti in alcuni bit del +campo \var{st\_mode} della struttura \func{stat} (si veda +\figref{fig:file_stat_struct}). + +In genere ci si riferisce ai tre livelli dei permessi usando le lettere +\cmd{u} (per \textit{user}), \cmd{g} (per \textit{group}) e \cmd{o} (per +\textit{other}), inoltre se si vuole indicare tutti i raggruppamenti insieme +si usa la lettera \cmd{a} (per \textit{all}). Si tenga ben presente questa +distinzione dato che in certi casi, mutuando la terminologia in uso nel VMS, +si parla dei permessi base come di permessi per \textit{owner}, \textit{group} +ed \textit{all}, le cui iniziali possono dar luogo a confusione. Le costanti +che permettono di accedere al valore numerico di questi bit nel campo +\var{st\_mode} sono riportate in \ntab. \begin{table}[htb] \centering diff --git a/fileintro.tex b/fileintro.tex index 010add3..40d8e25 100644 --- a/fileintro.tex +++ b/fileintro.tex @@ -176,7 +176,8 @@ Si ricordi infine che in ambiente Unix non esistono tipizzazioni dei file di dati e che non c'è nessun supporto del sistema per le estensioni come parte del filesystem. Ciò non ostante molti programmi adottano delle convenzioni per i nomi dei file, ad esempio il codice C normalmente si mette in file con -l'estensione \file{.c}, ma questa è, appunto, solo una convenzione. +l'estensione \file{.c}, ma questa è, per quanto usata ed accettata in maniera +universale, solo una convenzione. @@ -296,23 +297,23 @@ POSIX.1 dei sistemi Unix, ed Per capire fino in fondo le proprietà di file e directory in un sistema unix-like ed il comportamento delle relative funzioni di manipolazione occorre una breve introduzione al funzionamento gestione dei file da parte del kernel -e sugli oggetti su cui è basato un filesystem di tipo unix. In particolare -occorre tenere presente dov'è che si situa la divisione fondamentale fra -kernel space e user space che tracciavamo al \capref{cha:intro_unix}. +e sugli oggetti su cui è basato un filesystem. In particolare occorre tenere +presente dov'è che si situa la divisione fondamentale fra kernel space e user +space che tracciavamo al \capref{cha:intro_unix}. In questa sezione esamineremo come viene implementato l'accesso ai file in Linux, come il kernel può gestire diversi tipi di filesystem, descrivendo -prima le caratteristiche generali di un filesystem Unix, per poi trattare in -maniera un po' più dettagliata il filesystem standard di Linux, l'\acr{ext2}. +prima le caratteristiche generali di un filesystem di un sistema unix-like, +per poi trattare in maniera un po' più dettagliata il filesystem standard di +Linux, l'\acr{ext2}. +% in particolare si riprenderà, approfondendolo sul piano dell'uso nelle +% funzioni di libreria, il concetto di \textit{inode} di cui abbiamo brevemente +% accennato le caratteristiche (dal lato dell'implementazione nel kernel) in +% \secref{sec:file_vfs}. -% in particolare si riprenderà, approfondendolo sul piano -% dell'uso nelle funzioni di libreria, il concetto di \textit{inode} di cui -% abbiamo brevemente accennato le caratteristiche (dal lato dell'implementazione -% nel kernel) in \secref{sec:file_vfs}. - -\subsection{Il \textit{virtual filesystem} di Linux} +\subsection{Il \textit{Virtual Filesystem} di Linux} \label{sec:file_vfs} % Questa sezione riporta informazioni sui dettagli di come il kernel gestisce i @@ -322,13 +323,14 @@ maniera un po' pi % \textit{inode}, \textit{dentry}, \textit{dcache}. In Linux il concetto di \textit{everything is a file} è stato implementato -attraverso il \textit{Virtual File System} (da qui in avanti VFS) che è -l'interfaccia che il kernel rende disponibile ai programmi in user space -attraverso la quale vengono manipolati i file; esso provvede un livello di -indirezione che permette di collegare le operazioni di manipolazione sui file -alle operazioni di I/O e gestisce l'organizzazione di questi ultimi nei vari -modi in cui diversi filesystem la effettuano, permettendo la coesistenza -di filesystem differenti all'interno dello stesso albero delle directory +attraverso il \textit{Virtual Filesystem} (da qui in avanti VFS) che è uno +strato intermedio che il kernel usa per accedere ai più svariati filesystem +mantenendo la stessa interfaccia per i programmi in user space. Esso provvede +un livello di indirezione che permette di collegare le operazioni di +manipolazione sui file alle operazioni di I/O, e gestisce l'organizzazione di +queste ultime nei vari modi in cui diversi filesystem le effettuano, +permettendo la coesistenza di filesystem differenti all'interno dello stesso +albero delle directory. Quando un processo esegue una system call che opera su un file il kernel chiama sempre una funzione implementata nel VFS; la funzione eseguirà le @@ -361,17 +363,17 @@ In questo modo quando viene effettuata la richiesta di montare un nuovo disco (o qualunque altro \textit{block device} che può contenere un filesystem), il VFS può ricavare dalla citata tabella il puntatore alle funzioni da chiamare nelle operazioni di montaggio. Quest'ultima è responsabile di leggere da disco -il superblock (vedi \ref{sec:file_ext2}), inizializzare tutte le -variabili interne e restituire uno speciale descrittore dei filesystem montati -al VFS; attraverso quest'ultimo diventa possibile accedere alle routine -specifiche per l'uso di quel filesystem. +il superblock (vedi \secref{sec:file_ext2}), inizializzare tutte le variabili +interne e restituire uno speciale descrittore dei filesystem montati al VFS; +attraverso quest'ultimo diventa possibile accedere alle routine specifiche per +l'uso di quel filesystem. Il primo oggetto usato dal VFS è il descrittore di filesystem, un puntatore ad una apposita struttura che contiene vari dati come le informazioni comuni ad ogni filesystem, i dati privati relativi a quel filesystem specifico, e i puntatori alle funzioni del kernel relative al filesystem. Il VFS può così -usare le funzioni contenute nel filesystem descriptor per accedere alle routine -specifiche di quel filesystem. +usare le funzioni contenute nel \textit{filesystem descriptor} per accedere +alle routine specifiche di quel filesystem. Gli altri due descrittori usati dal VFS sono relativi agli altri due oggetti su cui è strutturata l'interfaccia. Ciascuno di essi contiene le informazioni @@ -386,28 +388,28 @@ file gi \subsection{Il funzionamento del VFS} \label{sec:file_vfs_work} -La funzione più fondamentale implementata dal VFS è la system call -\func{open} che permette di aprire un file. Dato un pathname viene eseguita -una ricerca dentro la \textit{directory entry cache} (in breve -\textit{dcache}), una tabella di hash che contiene tutte le \textit{directory - entry} (in breve \textit{dentry}) che permette di associare in maniera -rapida ed efficiente il pathname a una specifica dentry. +La funzione più importante implementata dal VFS è la system call \func{open} +che permette di aprire un file. Dato un pathname viene eseguita una ricerca +dentro la \textit{directory entry cache} (in breve \textit{dcache}), una +tabella che contiene tutte le \textit{directory entry} (in breve +\textit{dentry}) che permette di associare in maniera rapida ed efficiente il +pathname a una specifica \textit{dentry}. Una singola \textit{dentry} contiene in genere il puntatore ad un \textit{inode}; quest'ultimo è la struttura base che sta sul disco e che identifica un singolo oggetto del VFS sia esso un file ordinario, una directory, un link simbolico, una FIFO, un file di dispositivo, o una -qualsiasi altra cosa che possa essere rappresentata dal VFS (sui tipi di -``file'' possibili torneremo in seguito). A ciascuno di essi è associata pure -una struttura che sta in memoria, e che oltre alle informazioni sullo -specifico file contiene pure il riferimento alle funzioni (i \textsl{metodi}) -da usare per poterlo manipolare. +qualsiasi altra cosa che possa essere rappresentata dal VFS (i tipi di +``file'' riportati in \tabref{tab:file_file_types}). A ciascuno di essi è +associata pure una struttura che sta in memoria, e che, oltre alle +informazioni sullo specifico file, contiene anche il riferimento alle funzioni +(i \textsl{metodi} del VFS) da usare per poterlo manipolare. Le \textit{dentry} ``vivono'' in memoria e non vengono mai salvate su disco, -vengono usate per motivi di velocità, gli inode invece stanno su disco e -vengono copiati in memoria quando serve, ed ogni cambiamento viene copiato -all'indietro sul disco, gli inode che stanno in memoria sono inode del VFS ed -è ad essi che puntano le singole \textit{dentry}. +vengono usate per motivi di velocità, gli \textit{inode} invece stanno su +disco e vengono copiati in memoria quando serve, ed ogni cambiamento viene +copiato all'indietro sul disco, gli inode che stanno in memoria sono inode del +VFS ed è ad essi che puntano le singole \textit{dentry}. La \textit{dcache} costituisce perciò una sorta di vista completa di tutto l'albero dei file, ovviamente per non riempire tutta la memoria questa vista è @@ -421,9 +423,9 @@ della directory che contiene il file; questo viene installato nelle relative strutture in memoria quando si effettua il montaggio lo specifico filesystem su cui l'inode va a vivere. -Una volta che il VFS ha a disposizione la dentry (ed il relativo inode) -diventa possibile accedere alle varie operazioni sul file come la -\func{open} per aprire il file o la \func{stat} per leggere i dati +Una volta che il VFS ha a disposizione la \textit{dentry} (ed il relativo +\textit{inode}) diventa possibile accedere alle varie operazioni sul file come +la \func{open} per aprire il file o la \func{stat} per leggere i dati dell'inode e passarli in user space. L'apertura di un file richiede comunque un'altra operazione, l'allocazione di @@ -464,44 +466,45 @@ operazioni previste dal kernel \label{tab:file_file_operations} \end{table} -In questo modo per ciascun file diventano utilizzabili una serie di operazioni -(non è dette che tutte siano disponibili), che costituiscono l'interfaccia -astratta del VFS, e qualora se ne voglia eseguire una il kernel andrà ad +In questo modo per ciascun file diventano possibili una serie di operazioni +(non è detto che tutte siano disponibili), che costituiscono l'interfaccia +astratta del VFS. Qualora se ne voglia eseguire una il kernel andrà ad utilizzare la opportuna routine dichiarata in \var{f\_ops} appropriata al tipo -di file in questione. +di file in questione. -Così sarà possibile scrivere sulla porta seriale come su un file di dati -normale; ovviamente certe operazioni (nel caso della seriale ad esempio la -\code{seek}) non saranno disponibili, però con questo sistema l'utilizzo di -diversi filesystem (come quelli usati da Windows o MacOs) è immediato e -(relativamente) trasparente per l'utente ed il programmatore. +In questo modo è possibile scrivere allo stesso modo sulla porta seriale come +su un file di dati normale; ovviamente certe operazioni (nel caso della +seriale ad esempio la \code{seek}) non saranno disponibili, però con questo +sistema l'utilizzo di diversi filesystem (come quelli usati da Windows o +MacOs) è immediato e (relativamente) trasparente per l'utente ed il +programmatore. -\subsection{Il funzionamento di un filesystem unix} +\subsection{Il funzionamento di un filesystem Unix} \label{sec:file_filesystem} -Come già accennato in \secref{sec:file_organization} Linux (ed ogni unix -in generale) organizza i dati che tiene su disco attraverso l'uso di un +Come già accennato in \secref{sec:file_organization} Linux (ed ogni sistema +unix-like) organizza i dati che tiene su disco attraverso l'uso di un filesystem. Una delle caratteristiche di Linux rispetto agli altri Unix è quella di poter supportare grazie al VFS una enorme quantità di filesystem diversi, ognuno dei quali ha una sua particolare struttura e funzionalità -proprie; per questo non entreremo nei dettagli di un filesystem specifico, ma -daremo una descrizione a grandi linee che si adatta alle caratteristiche -comuni di un qualunque filesystem standard unix. +proprie. Per questo per il momento non entreremo nei dettagli di un +filesystem specifico, ma daremo una descrizione a grandi linee che si adatta +alle caratteristiche comuni di qualunque filesystem di sistema unix-like. -Dato un disco lo spazio fisico viene usualmente diviso in partizioni; ogni -partizione può contenere un filesystem; la strutturazione tipica +Lo spazio fisico di un disco viene usualmente diviso in partizioni; ogni +partizione può contenere un filesystem. La strutturazione tipica dell'informazione su un disco è riportata in \nfig; in essa si fa riferimento alla struttura del filesystem \acr{ext2}, che prevede una separazione dei dati in \textit{blocks group} che replicano il superblock (ma sulle caratteristiche di \acr{ext2} torneremo in \secref{sec:file_ext2}). È comunque caratteristica -comune di tutti i filesystem unix, indipendentemente da come poi viene +comune di tutti i filesystem per Unix, indipendentemente da come poi viene strutturata nei dettagli questa informazione, prevedere una divisione fra la lista degli inodes e lo spazio a disposizione per i dati e le directory. \begin{figure}[htb] \centering - \includegraphics[width=9cm]{img/disk_struct} + \includegraphics[width=12cm]{img/disk_struct} \caption{Organizzazione dello spazio su un disco in partizioni e filesystem} \label{fig:file_disk_filesys} \end{figure} @@ -514,15 +517,16 @@ esemplificare la situazione con uno schema come quello esposto in \nfig. \begin{figure}[htb] \centering - \includegraphics[width=11cm]{img/filesys_struct} + \includegraphics[width=12cm]{img/filesys_struct} \caption{Strutturazione dei dati all'interno di un filesystem} \label{fig:file_filesys_detail} \end{figure} -Da \curfig\ si evidenziano alcune caratteristiche base di ogni filesystem su -cui è bene porre attenzione in quanto sono fondamentali per capire il -funzionamento delle funzioni che manipolano i file e le directory su cui -torneremo in seguito; in particolare è opportuno ricordare sempre che: +Da \curfig\ si evidenziano alcune delle caratteristiche di base di un +filesystem, sulle quali è bene porre attenzione visto che sono fondamentali +per capire il funzionamento delle funzioni che manipolano i file e le +directory che tratteremo nel prossimo capitolo; in particolare è opportuno +ricordare sempre che: \begin{enumerate} @@ -535,8 +539,8 @@ torneremo in seguito; in particolare (traduzione approssimata dell'inglese \textit{directory entry}, che non useremo anche per evitare confusione con le \textit{dentry} del kernel di cui si parlava in \secref{sec:file_vfs}). - -\item Come mostrato in \curfig si possono avere più voci che puntano allo + +\item Come mostrato in \curfig\ si possono avere più voci che puntano allo stesso \textit{inode}. Ogni \textit{inode} ha un contatore che contiene il numero di riferimenti (\textit{link count}) che sono stati fatti ad esso; solo quando questo contatore si annulla i dati del file vengono @@ -544,13 +548,13 @@ torneremo in seguito; in particolare file si chiama \func{unlink}, ed in realtà non cancella affatto i dati del file, ma si limita a eliminare la relativa voce da una directory e decrementare il numero di riferimenti nell'\textit{inode}. - + \item Il numero di \textit{inode} nella voce si riferisce ad un \textit{inode} nello stesso filesystem e non ci può essere una directory che contiene riferimenti ad \textit{inodes} relativi ad altri filesystem. Questo limita l'uso del comando \cmd{ln} (che crea una nuova voce per un file esistente, con la funzione \func{link}) al filesystem corrente. - + \item Quando si cambia nome ad un file senza cambiare filesystem il contenuto del file non deve essere spostato, viene semplicemente creata una nuova voce per l'\textit{inode} in questione e rimossa la vecchia (questa è la modalità @@ -559,15 +563,15 @@ torneremo in seguito; in particolare \end{enumerate} -Infine è bene avere presente che essendo file pure loro, esiste un numero di -riferimenti anche per le directory; per cui se ad esempio a partire dalla -situazione mostrata in \curfig\ creiamo una nuova directory \file{img} nella -directory \file{gapil}: avremo una situazione come quella in \nfig, dove per -chiarezza abbiamo aggiunto dei numeri di inode. +Infine è bene avere presente che, essendo file pure loro, esiste un numero di +riferimenti anche per le directory; per cui se a partire dalla situazione +mostrata in \curfig\ creiamo una nuova directory \file{img} nella directory +\file{gapil}: avremo una situazione come quella in \nfig, dove per chiarezza +abbiamo aggiunto dei numeri di inode. \begin{figure}[htb] \centering - \includegraphics[width=11cm]{img/dir_links} + \includegraphics[width=12cm]{img/dir_links} \caption{Organizzazione dei link per le directory} \label{fig:file_dirs_link} \end{figure} @@ -586,12 +590,11 @@ adesso sar Il filesystem standard usato da Linux è il cosiddetto \textit{second extended filesystem}, identificato dalla sigla \acr{ext2}. Esso supporta tutte le -caratteristiche di un filesystem standard unix, è in grado di gestire -filename lunghi (256 caratteri, estendibili a 1012), una dimensione fino a -4~Tb. +caratteristiche di un filesystem standard Unix, è in grado di gestire nomi di +file lunghi (256 caratteri, estendibili a 1012), una dimensione fino a 4~Tb. -Oltre alle caratteristiche standard \acr{ext2} fornisce alcune estensioni -che non sono presenti sugli altri filesystem unix, le cui principali sono le +Oltre alle caratteristiche standard \acr{ext2} fornisce alcune estensioni che +non sono presenti sugli altri filesystem Unix, le cui principali sono le seguenti: \begin{itemize} \item i \textit{file attributes} consentono di modificare il comportamento del @@ -620,7 +623,7 @@ seguenti: log). \end{itemize} -La struttura di \acr{ext2} è stata ispirata a quella del filesystem di BSD, +La struttura di \acr{ext2} è stata ispirata a quella del filesystem di BSD: un filesystem è composto da un insieme di blocchi, la struttura generale è quella riportata in \figref{fig:file_filesys_detail}, in cui la partizione è divisa in gruppi di blocchi. -- 2.30.2 From 9103d0fa93c72112bfa11a7b1a27fc3dd5e1752c Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Tue, 19 Feb 2002 17:01:36 +0000 Subject: [PATCH 02/16] Riscritto meglio privilegi e permessi e spiegata la fuga dalla chroot jail per root. --- filedir.tex | 300 +++++++++++++++++++++++++++++----------------------- 1 file changed, 165 insertions(+), 135 deletions(-) diff --git a/filedir.tex b/filedir.tex index 7601b73..efeb41c 100644 --- a/filedir.tex +++ b/filedir.tex @@ -1326,42 +1326,59 @@ le funzioni usate per gestirne i vari aspetti. \subsection{I permessi per l'accesso ai file} \label{sec:file_perm_overview} -Il controllo di accesso ai file in Unix segue un modello abbastanza semplice -(ma adatto alla gran parte delle esigenze) in cui si dividono i permessi su -tre livelli. Si tenga conto poi che quanto diremo è vero solo per filesystem -di tipo Unix, e non è detto che sia applicabile a un filesystem -qualunque\footnote{ed infatti non è vero per il filesystem vfat di Windows, - per il quale i permessi vengono assegnati in maniera fissa con un opzione in - fase di montaggio.}. Esistono inoltre estensioni che permettono di -implementare le ACL (\textit{Access Control List}) che sono un meccanismo di -controllo di accesso molto più sofisticato. - Ad ogni file Linux associa sempre l'utente che ne è proprietario (il -cosiddetto \textit{owner}) e il gruppo di appartenenza, secondo il meccanismo +cosiddetto \textit{owner}) ed un gruppo di appartenenza, secondo il meccanismo degli identificatori di utenti e gruppi (\acr{uid} e \acr{gid}). Questi valori -sono accessibili da programma tramite i campi \var{st\_uid} e \var{st\_gid} -della struttura \var{stat} (si veda \secref{sec:file_stat}). Ad ogni file -viene inoltre associato un insieme di permessi che sono divisi in tre livelli, -e cioè attribuiti rispettivamente all'utente proprietario del file, a un -qualunque utente faccia parte del gruppo cui appartiene il file, e a tutti gli -altri utenti. - -I permessi, così come vengono presi dai comandi e dalle routine di sistema, -sono espressi da un numero a 12 bit; di questi i nove meno significativi sono -usati a gruppi di tre per indicare i permessi base di lettura, scrittura ed -esecuzione (indicati nei comandi di sistema con le lettere \cmd{w}, \cmd{r} e -\cmd{x}) ed applicabili rispettivamente al proprietario, al gruppo, a tutti -gli altri. I restanti tre bit (\acr{suid}, \acr{sgid}, e \textsl{sticky}) -sono usati per indicare alcune caratteristiche più complesse del meccanismo -del controllo di accesso su cui torneremo in seguito (in +sono accessibili da programma tramite la funzione \func{stat}, e sono +mantenuti nei campi \var{st\_uid} e \var{st\_gid} della struttura \var{stat} +(si veda \secref{sec:file_stat}).\footnote{Questo è vero solo per filesystem + di tipo Unix, ad esempio non è vero per il filesystem vfat di Windows, che + non fornisce nessun supporto per l'accesso multiutente, e per il quale i + permessi vengono assegnati in maniera fissa con un opzione in fase di + montaggio.} + +Il controllo di accesso ai file segue un modello abbastanza semplice che +prevede tre permessi fondamentali strutturati su tre livelli di accesso. +Esistono varie estensioni a questo modello,\footnote{come le \textit{Access + Control List} che possono essere aggiunte al filesystem standard con + opportune patch, e sono presenti in filesystem non ancora inclusi nel kernel + ufficiale come \textsl{xfs}, o meccanismi di controllo ancora più + sofisticati come il \textit{mandatory access control} di SE-Linux} ma nella +maggior parte dei casi il meccanismo standard è più che sufficiente a +soffisfare tutte le necessità più comuni. I tre permessi di base associati ad +ogni file sono: +\begin{itemize*} +\item il permesso di lettura (indicato con la lettera \texttt{r}, dall'inglese + \textit{read}). +\item il permesso di scrittura (indicato con la lettera \texttt{w}, + dall'inglese \textit{write}). +\item il permesso di esecuzione (indicato con la lettera \texttt{x}, + dall'inglese \textit{execute}). +\end{itemize*} +mentre i tre livelli su cui sono divisi i privilegi sono: +\begin{itemize*} +\item i privilegi per l'utente proprietario del file. +\item i privilegi per un qualunque utente faccia parte del gruppo cui + appartiene il file. +\item i privilegi per tutti gli altri utenti. +\end{itemize*} + +L'insieme dei permessi viene espresso con un numero a 12 bit; di questi i nove +meno significativi sono usati a gruppi di tre per indicare i permessi base di +lettura, scrittura ed esecuzione e sono applicati rispettivamente +rispettivamente al proprietario, al gruppo, a tutti gli altri. + +I restanti tre bit (noti come \acr{suid}, \acr{sgid}, e \textsl{sticky}) sono +usati per indicare alcune caratteristiche più complesse del meccanismo del +controllo di accesso su cui torneremo in seguito (in \secref{sec:file_suid_sgid} e \secref{sec:file_sticky}). Anche i permessi, come tutte le altre informazioni pertinenti al file, sono memorizzati nell'inode; in particolare essi sono contenuti in alcuni bit del -campo \var{st\_mode} della struttura \func{stat} (si veda +campo \var{st\_mode} della struttura \func{stat} (si veda di nuovo \figref{fig:file_stat_struct}). -In genere ci si riferisce ai tre livelli dei permessi usando le lettere +In genere ci si riferisce ai tre livelli dei privilegi usando le lettere \cmd{u} (per \textit{user}), \cmd{g} (per \textit{group}) e \cmd{o} (per \textit{other}), inoltre se si vuole indicare tutti i raggruppamenti insieme si usa la lettera \cmd{a} (per \textit{all}). Si tenga ben presente questa @@ -1397,31 +1414,30 @@ che permettono di accedere al valore numerico di questi bit nel campo \label{tab:file_bit_perm} \end{table} -Questi permessi vengono usati in maniera diversa dalle varie funzioni, e a -seconda che si riferiscano a file, link simbolici o directory, qui ci +I permessi vengono usati in maniera diversa dalle varie funzioni, e a seconda +che si riferiscano a dei file, dei link simbolici o delle directory, qui ci limiteremo ad un riassunto delle regole generali, entrando nei dettagli più avanti. La prima regola è che per poter accedere ad un file attraverso il suo pathname occorre il permesso di esecuzione in ciascuna delle directory che compongono -il pathname, e lo stesso vale per aprire un file nella directory corrente (per +il pathname; lo stesso vale per aprire un file nella directory corrente (per la quale appunto serve il diritto di esecuzione). -Per una directory infatti il permesso di esecuzione ha il significato -specifico che essa può essere attraversata nella risoluzione del pathname, ed -è distinto dal permesso di lettura che invece implica che si può leggere il -contenuto della directory. Questo significa che se si ha il permesso di -esecuzione senza permesso di lettura si potrà lo stesso aprire un file in una -directory (se si hanno i permessi opportuni per il medesimo) ma non si potrà -vederlo con \cmd{ls} (per crearlo occorrerà anche il permesso di scrittura per -la directory). - -Avere il permesso di lettura per un file consente di aprirlo con le opzioni di -sola lettura (\macro{O\_RDONLY}) o di lettura/scrittura (\macro{O\_RDWR}) e -leggerne il contenuto. Avere il permesso di scrittura consente di aprire un -file in sola scrittura (\macro{O\_WRONLY}) o lettura/scrittura -(\macro{O\_RDWR}) e modificarne il contenuto, lo stesso permesso è necessario -per poter troncare il file con l'opzione \macro{O\_TRUNC}. +Per una directory infatti il permesso di esecuzione significa che essa può +essere attraversata nella risoluzione del pathname, ed è distinto dal permesso +di lettura che invece implica che si può leggere il contenuto della directory. +Questo significa che se si ha il permesso di esecuzione senza permesso di +lettura si potrà lo stesso aprire un file in una directory (se si hanno i +permessi opportuni per il medesimo) ma non si potrà vederlo con \cmd{ls} +(mentre per crearlo occorrerà anche il permesso di scrittura per la +directory). + +Avere il permesso di lettura per un file consente di aprirlo con le opzioni +(si veda quanto riportato in \tabref{tab:file_open_flags}) di sola lettura o +di lettura/scrittura e leggerne il contenuto. Avere il permesso di scrittura +consente di aprire un file in sola scrittura o lettura/scrittura e modificarne +il contenuto, lo stesso permesso è necessario per poter troncare il file. Non si può creare un file fintanto che non si disponga del permesso di esecuzione e di quello di scrittura per la directory di destinazione; gli @@ -1429,7 +1445,7 @@ stessi permessi occorrono per cancellare un file da una directory (si ricordi che questo non implica necessariamente la rimozione del contenuto del file dal disco), non è necessario nessun tipo di permesso per il file stesso (infatti esso non viene toccato, viene solo modificato il contenuto della directory, -rimuovendo la voce che ad esso fa rifermento). +rimuovendo la voce che ad esso fa riferimento). Per poter eseguire un file (che sia un programma compilato od uno script di shell, od un altro tipo di file eseguibile riconosciuto dal kernel), occorre @@ -1437,22 +1453,23 @@ avere il permesso di esecuzione, inoltre solo i file regolari possono essere eseguiti. I permessi per un link simbolico sono ignorati, contano quelli del file a cui -fa riferimento; per questo in genere \cmd{ls} per un link simbolico riporta -tutti i permessi come concessi; utente e gruppo a cui esso appartiene vengono -ignorati quando il link viene risolto, vengono controllati solo quando viene -richiesta la rimozione del link e quest'ultimo è in una directory con lo -\textsl{sticky bit} settato (si veda \secref{sec:file_sticky}). +fa riferimento; per questo in genere il comando \cmd{ls} riporta per un link +simbolico tutti i permessi come concessi; utente e gruppo a cui esso +appartiene vengono pure ignorati quando il link viene risolto, vengono +controllati solo quando viene richiesta la rimozione del link e quest'ultimo è +in una directory con lo \textsl{sticky bit} settato (si veda +\secref{sec:file_sticky}). La procedura con cui il kernel stabilisce se un processo possiede un certo permesso (di lettura, scrittura o esecuzione) si basa sul confronto fra l'utente e il gruppo a cui il file appartiene (i valori di \var{st\_uid} e \var{st\_gid} accennati in precedenza) e l'\textit{effective user id}, l'\textit{effective group id} e gli eventuali \textit{supplementary group id} -del processo\footnote{in realtà Linux per quanto riguarda l'accesso ai file +del processo.\footnote{in realtà Linux per quanto riguarda l'accesso ai file utilizza al posto degli \textit{effective id} i \textit{filesystem id} (si veda \secref{sec:proc_perms}), ma essendo questi del tutto equivalenti ai primi, eccetto il caso in cui si voglia scrivere un server NFS, ignoreremo - questa differenza}. + questa differenza.} Per una spiegazione dettagliata degli identificatori associati ai processi si veda \secref{sec:proc_perms}; normalmente, a parte quanto vedremo in @@ -1493,7 +1510,7 @@ di accesso sono i seguenti: Si tenga presente che questi passi vengono eseguiti esattamente in quest'ordine. Questo vuol dire che se un processo è il proprietario di un file l'accesso è consentito o negato solo sulla base dei permessi per l'utente; i -permessi per il gruppo non vengono neanche controllati; lo stesso vale se il +permessi per il gruppo non vengono neanche controllati. Lo stesso vale se il processo appartiene ad un gruppo appropriato, in questo caso i permessi per tutti gli altri non vengono controllati. @@ -1502,10 +1519,10 @@ tutti gli altri non vengono controllati. \label{sec:file_suid_sgid} Come si è accennato (in \secref{sec:file_perm_overview}) nei dodici bit del -campo \var{st\_mode} usati per il controllo di accesso oltre ai bit dei -permessi veri e propri, ci sono altri tre bit che vengono usati per indicare -alcune proprietà speciali dei file. Due di questi sono i bit detti -\acr{suid} (o \textit{set-user-ID bit}) e \acr{sgid} (o +campo \var{st\_mode} di \var{stat} che vengono usati per il controllo di +accesso oltre ai bit dei permessi veri e propri, ci sono altri tre bit che +vengono usati per indicare alcune proprietà speciali dei file. Due di questi +sono i bit detti \acr{suid} (da \textit{set-user-ID bit}) e \acr{sgid} (da \textit{set-group-ID bit}) che sono identificati dalle costanti \macro{S\_ISUID} e \macro{S\_ISGID}. @@ -1516,7 +1533,7 @@ processo all'\acr{uid} e al \acr{gid} del processo corrente, che normalmente corrispondono dell'utente con cui si è entrati nel sistema. Se però il file del programma\footnote{per motivi di sicurezza il kernel - ignora i bit \acr{suid} e \acr{sgid} per gli script eseguibili} (che + ignora i bit \acr{suid} e \acr{sgid} per gli script eseguibili.} (che ovviamente deve essere eseguibile) ha il bit \acr{suid} settato, il kernel assegnerà come \textit{effective user id} al nuovo processo l'\acr{uid} del proprietario del file al posto dell'\acr{uid} del processo originario. Avere @@ -1535,16 +1552,15 @@ con i privilegi di root. Chiaramente avere un processo che ha privilegi superiori a quelli che avrebbe normalmente l'utente che lo ha lanciato comporta vari rischi, e questo tipo di programmi devono essere scritti accuratamente per evitare che possano essere -usati per guadagnare privilegi non consentiti (torneremo sull'argomento in -\secref{sec:proc_perms}). - -La presenza dei bit \acr{suid} e \acr{sgid} su un file può essere -rilevata con il comando \cmd{ls -l}, in tal caso comparirà la lettera \cmd{s} -al posto della \cmd{x} in corrispondenza dei permessi di utente o gruppo. La -stessa lettera \cmd{s} può essere usata nel comando \cmd{chmod} per settare -questi bit. Infine questi bit possono essere controllati all'interno di -\var{st\_mode} con l'uso delle due costanti \macro{S\_ISUID} e -\macro{S\_IGID}, i cui valori sono riportati in +usati per guadagnare privilegi non consentiti (l'argomento è affrontato in +dettaglio in \secref{sec:proc_perms}). + +La presenza dei bit \acr{suid} e \acr{sgid} su un file può essere rilevata con +il comando \cmd{ls -l}, che una lettera \cmd{s} al posto della \cmd{x} in +corrispondenza dei permessi di utente o gruppo. La stessa lettera \cmd{s} può +essere usata nel comando \cmd{chmod} per settare questi bit. Infine questi bit +possono essere controllati all'interno di \var{st\_mode} con l'uso delle due +costanti \macro{S\_ISUID} e \macro{S\_IGID}, i cui valori sono riportati in \tabref{tab:file_mode_flags}. Gli stessi bit vengono ad assumere in significato completamente diverso per le @@ -1553,18 +1569,18 @@ con questi bit l'uso della semantica BSD nella creazione di nuovi file (si veda \secref{sec:file_ownership} per una spiegazione dettagliata al proposito). -Infine Linux utilizza il bit \acr{sgid} per una ulteriore estensione -mutuata da SVR4. Il caso in cui il file abbia il bit \acr{sgid} settato ma -non il corrispondente bit di esecuzione viene utilizzato per attivare per -quel file il \textit{mandatory locking} (argomento che affronteremo nei -dettagli in \secref{sec:file_mand_locking}). +Infine Linux utilizza il bit \acr{sgid} per una ulteriore estensione mutuata +da SVR4. Il caso in cui un file ha il bit \acr{sgid} settato senza che lo sia +anche il corrispondente bit di esecuzione viene utilizzato per attivare per +quel file il \textit{mandatory locking} (argomento che affronteremo in +dettagliopiù avanti in \secref{sec:file_mand_locking}). \subsection{Il bit \textsl{sticky}} \label{sec:file_sticky} L'ultimo dei bit rimanenti, identificato dalla costante \macro{S\_ISVTX}, è in -parte un rimasuglio delle origini dei sistemi unix. A quell'epoca infatti la +parte un rimasuglio delle origini dei sistemi Unix. A quell'epoca infatti la memoria virtuale e l'accesso ai files erano molto meno sofisticati e per ottenere la massima velocità possibile per i programmi usati più comunemente si poteva settare questo bit. @@ -1585,11 +1601,12 @@ Le attuali implementazioni di memoria virtuale e filesystem rendono sostanzialmente inutile questo procedimento. Benché ormai non venga più utilizzato per i file, lo \textsl{sticky bit} ha -assunto un uso corrente per le directory\footnote{lo \textsl{sticky bit} per - le directory è una estensione non definita nello standard POSIX, Linux però - la supporta, così come BSD e SVR4}, in questo caso se il bit è settato un -file potrà essere rimosso dalla directory soltanto se l'utente ha il permesso -di scrittura ed inoltre è vera una delle seguenti condizioni: +invece assunto un uso importante per le directory\footnote{lo \textsl{sticky + bit} per le directory è una estensione non definita nello standard POSIX, + Linux però la supporta, così come BSD e SVR4.}; in questo caso se il bit è +settato un file potrà essere rimosso dalla directory soltanto se l'utente ha +il permesso di scrittura su di essa ed inoltre è vera una delle seguenti +condizioni: \begin{itemize*} \item l'utente è proprietario del file \item l'utente è proprietario della directory @@ -1601,20 +1618,22 @@ permessi infatti di solito sono settati come: $ ls -ld /tmp drwxrwxrwt 6 root root 1024 Aug 10 01:03 /tmp \end{verbatim}%$ -in questo modo chiunque può leggere, scrivere ed eseguire i file temporanei -ivi memorizzati, sia crearne di nuovi, ma solo l'utente che ha creato un file -nella directory potrà cancellarlo o rinominarlo, così si può evitare che un -utente possa, più o meno consapevolmente, cancellare i file degli altri. +in questo modo chiunque può creare file in questa directory (che infatti è +normalmente utilizzata per la creazione di file temporanei), ma solo l'utente +che ha creato un certo file potrà cancellarlo o rinominarlo. In questo modo si +evita che un utente possa, più o meno consapevolmente, cancellare i file degli +altri. \subsection{La titolarità di nuovi file e directory} \label{sec:file_ownership} -Vedremo in \secref{sec:file_base_func} come creare nuovi file, ma se è -possibile specificare in sede di creazione quali permessi applicare ad un -file, non si può indicare a quale utente e gruppo esso deve appartenere. Lo -stesso problema di presenta per la creazione di nuove directory (procedimento -descritto in \secref{sec:file_dir_creat_rem}). +Vedremo in \secref{sec:file_base_func} con quali funzioni si possono creare +nuovi file, in tale occasione vedremo che è possibile specificare in sede di +creazione quali permessi applicare ad un file, però non si può indicare a +quale utente e gruppo esso deve appartenere. Lo stesso problema di presenta +per la creazione di nuove directory (procedimento descritto in +\secref{sec:file_dir_creat_rem}). Lo standard POSIX prescrive che l'\acr{uid} del nuovo file corrisponda all'\textit{effective user id} del processo che lo crea; per il \acr{gid} @@ -1633,25 +1652,26 @@ bit \acr{sgid} settato allora viene usata la seconda opzione. Usare la semantica BSD ha il vantaggio che il \acr{gid} viene sempre automaticamente propagato, restando coerente a quello della directory di -partenza, in tutte le sottodirectory. La semantica SVr4 offre una maggiore -possibilità di scelta, ma per ottenere lo stesso risultato necessita che per -le nuove directory venga anche propagato anche il bit \acr{sgid}. Questo è -comunque il comportamento di default di \func{mkdir}, ed é in questo modo ad -esempio che Debian assicura che le sottodirectory create nelle home di un -utente restino sempre con il \acr{gid} del gruppo primario dello stesso. +partenza, in tutte le sottodirectory. La semantica SVr4 offre la possibilità +di scegliere, ma per ottenere lo stesso risultato di coerenza che si ha con +BSD necessita che per le nuove directory venga anche propagato anche il bit +\acr{sgid}. Questo è il comportamento di default di \func{mkdir}, ed é in +questo modo ad esempio che Debian assicura che le sottodirectory create nelle +home di un utente restino sempre con il \acr{gid} del gruppo primario dello +stesso. \subsection{La funzione \func{access}} \label{sec:file_access} -Come detto in \secref{sec:file_access_control} il controllo di accesso ad -un file viene fatto usando \textit{effective user id} e \textit{effective - group id} del processo, ma ci sono casi in cui si può voler effettuare il -controllo usando il \textit{real user id} e il \textit{real group id} (cioè -l'\acr{uid} dell'utente che ha lanciato il programma, che, come accennato in -\secref{sec:file_suid_sgid} e spiegato in \secref{sec:proc_perms} non è -detto sia uguale all'\textit{effective user id}). Per far questo si può usare -la funzione \func{access}, il cui prototipo è: +Come visto in \secref{sec:file_access_control} il controllo di accesso ad un +file viene fatto usando \textit{effective user id} e \textit{effective group + id} del processo; ma ci sono casi in cui è necessario effettuare il +controllo usando il \textit{real user id} ed il \textit{real group id} (cioè +\acr{uid} e \acr{gid} dell'utente che ha lanciato il programma, e che, come +accennato in \secref{sec:file_suid_sgid} e spiegato in +\secref{sec:proc_perms}, non è detto siano uguali agli \textit{effective id}). +Per far questo si può usare la funzione \func{access}, il cui prototipo è: \begin{prototype}{unistd.h} {int access(const char *pathname, int mode)} @@ -1665,7 +1685,7 @@ da \var{pathname}. \macro{EIO}.} \end{prototype} -I valori possibili per il parametro \var{mode} sono esprimibili come +I valori possibili per l'argomento \param{mode} sono esprimibili come combinazione delle costanti numeriche riportate in \ntab\ (attraverso un OR binario). I primi tre valori implicano anche la verifica dell'esistenza del file, se si vuole verificare solo quest'ultima si può usare \macro{F\_OK}, o @@ -1698,17 +1718,17 @@ contrario (o di errore) ritorna -1. \end{table} Un esempio tipico per l'uso di questa funzione è quello di un processo che sta -eseguendo un programma coi privilegi di un altro utente (attraverso l'uso del -\acr{suid} bit) che vuole controllare se l'utente originale ha i permessi per -accedere ad un certo file. +eseguendo un programma coi privilegi di un altro utente (ad esmepio attraverso +l'uso del \acr{suid} bit) che vuole controllare se l'utente originale ha i +permessi per accedere ad un certo file. \subsection{Le funzioni \func{chmod} e \func{fchmod}} \label{sec:file_chmod} Per cambiare i permessi di un file il sistema mette ad disposizione due -funzioni, che operano rispettivamente su un filename e su un file descriptor, -i loro prototipi sono: +funzioni \func{chmod} e \func{fchmod}, che operano rispettivamente su un +filename e su un file descriptor, i loro prototipi sono: \begin{functions} \headdecl{sys/types.h} \headdecl{sys/stat.h} @@ -1778,11 +1798,11 @@ alcune limitazioni, provviste per motivi di sicurezza. Questo significa che anche se si è proprietari del file non tutte le operazioni sono permesse, in particolare: \begin{enumerate} -\item siccome solo l'amministratore può settare lo \textit{sticky bit}; se +\item siccome solo l'amministratore può settare lo \textit{sticky bit}, se l'\textit{effective user id} del processo non è zero esso viene automaticamente cancellato (senza notifica di errore) qualora sia stato indicato in \var{mode}. -\item per via della semantica SVR4 nella creazione dei nuovi file, si può +\item per via della semantica SVr4 nella creazione dei nuovi file, si può avere il caso in cui il file creato da un processo è assegnato a un gruppo per il quale il processo non ha privilegi. Per evitare che si possa assegnare il bit \acr{sgid} ad un file appartenente a un gruppo per cui @@ -1798,7 +1818,7 @@ misura di sicurezza, volta ad scongiurare l'abuso dei bit \acr{suid} e \acr{sgid}; essa consiste nel cancellare automaticamente questi bit qualora un processo che non appartenga all'amministratore scriva su un file. In questo modo anche se un utente malizioso scopre un file \acr{suid} su cui può -scrivere, un eventuale modifica comporterà la perdita di ogni ulteriore +scrivere, una eventuale modifica comporterà la perdita di ogni ulteriore privilegio. \subsection{La funzione \func{umask}} @@ -1817,10 +1837,12 @@ funzione \func{umask}, il cui prototipo delle poche funzioni che non restituisce codici di errore.} \end{prototype} -Questa maschera è una caratteristica di ogni processo e viene utilizzata per -impedire che alcuni permessi possano essere assegnati ai nuovi file in sede di -creazione, i bit indicati nella maschera vengono infatti esclusi quando un -nuovo file viene creato. +Questa maschera è una caratteristica di ogni processo\footnote{è infatti + contenuta nel campo \var{umask} di \var{fs\_struct}, vedi + \figref{fig:proc_task_struct}} e viene utilizzata per impedire che alcuni +permessi possano essere assegnati ai nuovi file in sede di creazione. I bit +indicati nella maschera vengono infatti esclusi quando un nuovo file viene +creato. In genere questa maschera serve per impostare un default che escluda alcuni permessi (usualmente quello di scrittura per il gruppo e gli altri, @@ -1875,11 +1897,11 @@ cambiare il gruppo dei file che gli appartengono solo se il nuovo gruppo suo gruppo primario o uno dei gruppi a cui appartiene. La funzione \func{chown} segue i link simbolici, per operare direttamente su -in link simbolico si deve usare la funzione \func{lchown}\footnote{fino alla +in link simbolico si deve usare la funzione \func{lchown}.\footnote{fino alla versione 2.1.81 in Linux \func{chown} non seguiva i link simbolici, da allora questo comportamento è stato assegnato alla funzione \func{lchown}, introdotta per l'occasione, ed è stata creata una nuova system call per - \func{chown} che seguisse i link simbolici}. La funzione \func{fchown} opera + \func{chown} che seguisse i link simbolici} La funzione \func{fchown} opera su un file aperto, essa è mutuata da BSD, ma non è nello standard POSIX. Un'altra estensione rispetto allo standard POSIX è che specificando -1 come valore per \var{owner} e \var{group} i valori restano immutati. @@ -1905,15 +1927,13 @@ programma ad una sezione limitata del filesystem, per cui ne parleremo in questa sezione. Come accennato in \secref{sec:proc_fork} ogni processo oltre ad una directory -di lavoro corrente, ha anche una directory radice, che è la directory che per -il processo costituisce la radice dell'albero dei file e rispetto alla quale -vengono risolti i pathname assoluti (si ricordi quanto detto in -\secref{sec:file_organization}). - -La radice viene eredidata dal padre per ogni processo figlio; come si può -vedere da \figref{fig:proc_task_struct} è tenuta nella struttura -\type{fs\_struct} insieme alla directory di lavoro corrente e alla -\var{umask}, e quindi di norma coincide con la \file{/} del sistema. +di lavoro corrente, ha anche una directory radice,\footnote{entrambe sono + contenute in due campi di \var{fs\_struct}, vedi + \figref{fig:proc_task_struct}.} che è la directory che per il processo +costituisce la radice dell'albero dei file e rispetto alla quale vengono +risolti i pathname assoluti (si ricordi quanto detto in +\secref{sec:file_organization}). La radice viene eredidata dal padre per ogni +processo figlio, e quindi di norma coincide con la \file{/} del sistema. In certe situazioni però per motivi di sicurezza non si vuole che un processo possa accedere a tutto il filesystem; per questo si può cambiare la directory @@ -1941,13 +1961,23 @@ detto in \secref{sec:proc_fork}, sar tenga presente che la funzione non cambia la directory di lavoro corrente, che potrebbe restare fuori dalla \textit{chroot jail}. -Un caso tipico di uso di \func{chroot} è quello di un server ftp, in questo -caso infatti si vuole che il server veda solo i file che deve trasferire, per -cui in genere si esegue una \func{chroot} sulla directory che contiene i file. -Si tenga presente però che in questo caso occorrerà replicare all'interno -della \textit{chroot jail} tutti i file (in genere programmi e librerie) di -cui il server potrebbe avere bisogno. - +Questo è il motivo per cui la funzione è efficace solo se dopo averla eseguita +si cedono i privilegi di root. Infatti se in qualche modo il processo ha una +directory di lavoro corrente fuori dalla \textit{chroot jail}, potrà comunque +accedere a tutto il filesystem usando pathname relativi. + +Ma quando ad un processo restano i privilegi di root esso potrà sempre portare +la directory di lavoro corrente fuori dalla \textit{chroot jail} creando una +sottodirectory ed eseguendo una \func{chroot} su di essa. Per questo motivo +l'uso di questa funzione non ha molto senso quando un processo necessita dei +privilegi di root per le sue normali operazioni. + +Un caso tipico di uso di \func{chroot} è quello di un server ftp anonimo, in +questo caso infatti si vuole che il server veda solo i file che deve +trasferire, per cui in genere si esegue una \func{chroot} sulla directory che +contiene i file. Si tenga presente però che in questo caso occorrerà +replicare all'interno della \textit{chroot jail} tutti i file (in genere +programmi e librerie) di cui il server potrebbe avere bisogno. %%% Local Variables: %%% mode: latex -- 2.30.2 From 327a8452b33c9626ebec4cfe6c9d6daad54f4b19 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Wed, 20 Feb 2002 16:31:13 +0000 Subject: [PATCH 03/16] Altre correzioni --- fileunix.tex | 185 ++++++++++++++++++++++++++------------------------- 1 file changed, 95 insertions(+), 90 deletions(-) diff --git a/fileunix.tex b/fileunix.tex index 6fe1542..e99db64 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -2,11 +2,11 @@ \label{cha:file_unix_interface} Esamineremo in questo capitolo la prima delle due interfacce di programmazione -per i file, quella dei \textit{file descriptor}, nativa di unix. Questa è +per i file, quella dei \textit{file descriptor}, nativa di Unix. Questa è l'interfaccia di basso livello provvista direttamente dalle system call, che non prevede funzionalità evolute come la bufferizzazione o funzioni di lettura o scrittura formattata, e sulla quale è costruita anche l'interfaccia definita -dallo standard ANSI C che affronteremo in \capref{cha:files_std_interface}. +dallo standard ANSI C che affronteremo al \capref{cha:files_std_interface}. @@ -16,7 +16,7 @@ dallo standard ANSI C che affronteremo in \capref{cha:files_std_interface}. In questa sezione faremo una breve introduzione sulla architettura su cui è basata dell'interfaccia dei \textit{file descriptor}, che, sia pure con differenze nella realizzazione pratica, resta sostanzialmente la stessa in -ogni implementazione di unix. +tutte le implementazione di un sistema unix-like. \subsection{L'architettura dei \textit{file descriptor}} @@ -26,15 +26,16 @@ Per poter accedere al contenuto di un file occorre creare un canale di comunicazione con il kernel che renda possibile operare su di esso (si ricordi quanto visto in \secref{sec:file_vfs_work}). Questo si fa aprendo il file con la funzione \func{open} che provvederà a localizzare l'inode del file e -inizializzare le funzioni che il VFS mette a disposizione (riportate in -\tabref{tab:file_file_operations}). Una volta terminate le operazioni, il file -dovrà essere chiuso, e questo chiuderà il canale di comunicazione impedendo -ogni ulteriore operazione. +inizializzare i puntatori che rendono disponibili le funzioni che il VFS mette +a disposizione (riportate in \tabref{tab:file_file_operations}). Una volta +terminate le operazioni, il file dovrà essere chiuso, e questo chiuderà il +canale di comunicazione impedendo ogni ulteriore operazione. All'interno di ogni processo i file aperti sono identificati da un intero non -negativo, chiamato appunto \textit{file descriptor}, quando un file viene -aperto la funzione restituisce il file descriptor, e tutte le successive -operazioni devono passare il \textit{file descriptor} come argomento. +negativo, chiamato appunto \textit{file descriptor}. Quando un file viene +aperto la funzione \func{open} restituisce questo numero, tutte le ulteriori +operazioni saranno compiute specificando questo stesso valore come argomento +alle varie funzioni dell'interfaccia. Per capire come funziona il meccanismo occorre spiegare a grandi linee come è che il kernel gestisce l'interazione fra processi e file. Il kernel mantiene @@ -74,8 +75,8 @@ file, fra cui: \end{itemize*} In \figref{fig:file_proc_file} si è riportato uno schema in cui è illustrata -questa architettura, in cui si sono evidenziate le interrelazioni fra le varie -strutture di dati sulla quale essa è basata. +questa architettura, ed in cui si sono evidenziate le interrelazioni fra le +varie strutture di dati sulla quale essa è basata. \begin{figure}[htb] \centering \includegraphics[width=13cm]{img/procfile} @@ -84,7 +85,7 @@ strutture di dati sulla quale essa \label{fig:file_proc_file} \end{figure} Ritorneremo su questo schema più volte, dato che esso è fondamentale per -capire i dettagli del funzionamento delle dell'interfaccia dei \textit{file +capire i dettagli del funzionamento dell'interfaccia dei \textit{file descriptor}. @@ -93,25 +94,27 @@ capire i dettagli del funzionamento delle dell'interfaccia dei \textit{file Come accennato i \textit{file descriptor} non sono altro che un indice nella tabella dei file aperti di ciascun processo; per questo motivo essi vengono -assegnati in successione tutte le volte che si apre un nuovo file (se non se -ne è chiuso nessuno in precedenza). +assegnati in successione tutte le volte che si apre un nuovo file (se non ne è +stato chiuso nessuno in precedenza). In tutti i sistemi unix-like esiste una convenzione generale per cui ogni -processo viene lanciato con almeno tre file aperti. Questi, per quanto -dicevamo prima, avranno come \textit{file descriptor} i valori 0, 1 e 2. -Benché questa sia soltanto una convenzione, essa è seguita dalla gran parte -delle applicazioni, e non aderirvi potrebbe portare a gravi problemi di +processo viene lanciato con almeno tre file aperti. Questi, per quanto appena +detto, avranno come \textit{file descriptor} i valori 0, 1 e 2. Benché questa +sia soltanto una convenzione, essa è seguita dalla gran parte delle +applicazioni, e non aderirvi potrebbe portare a gravi problemi di interoperabilità. Il primo file è sempre associato a quello che viene chiamato \textit{standard - input}, è cioè il file da cui il processo si aspetta di ricevere i dati in -ingresso (nel caso della shell, è associato alla lettura della tastiera); il -secondo file è il cosiddetto \textit{standard output}, cioè il file su cui ci -si aspetta debbano essere inviati i dati in uscita (sempre nel caso della -shell, è il terminale su cui si sta scrivendo), il terzo è lo \textit{standard - error}, su cui viene inviato l'output relativo agli errori. -Lo standard POSIX.1 provvede tre costanti simboliche, definite nell'header -\file{unistd.h}, al posto di questi valori numerici: + input}. È cioè il file da cui il processo si aspetta di ricevere i dati in +ingresso (nel caso della shell, è associato all'ingresso dal terminale, e +quindi alla lettura della tastiera). Il secondo file è il cosiddetto +\textit{standard output}, cioè il file su cui ci si aspetta debbano essere +inviati i dati in uscita (sempre nel caso della shell, è associato all'uscita +del terminale, e quindi alla scrittura sullo schermo). Il terzo è lo +\textit{standard error}, su cui viene inviato l'output relativo agli errori, +ed è anch'esso associato all'uscita del termininale. Lo standard POSIX.1 +provvede tre costanti simboliche, definite nell'header \file{unistd.h}, al +posto di questi valori numerici: \begin{table}[htb] \centering \footnotesize @@ -138,12 +141,12 @@ riferimento ad un programma in cui lo \textit{standard input} un file mentre lo \textit{standard output} e lo \textit{standard error} sono entrambi associati ad un altro file (e quindi utilizzano lo stesso inode). -Nelle vecchie versioni di unix (ed anche in Linux fino al kernel 2.0.x) il +Nelle vecchie versioni di Unix (ed anche in Linux fino al kernel 2.0.x) il numero di file aperti era anche soggetto ad un limite massimo dato dalle dimensioni del vettore di puntatori con cui era realizzata la tabella dei file -descriptor dentro \var{file\_struct}; questo limite intrinseco non sussiste -più, dato che si è passati da un vettore ad una linked list, ma restano i -limiti imposti dall'amministratore (vedi \secref{sec:sys_limits}). +descriptor dentro \var{file\_struct}; questo limite intrinseco nei kernel più +recenti non sussiste più, dato che si è passati da un vettore ad una lista, ma +restano i limiti imposti dall'amministratore (vedi \secref{sec:sys_limits}). @@ -151,7 +154,7 @@ limiti imposti dall'amministratore (vedi \secref{sec:sys_limits}). \label{sec:file_base_func} L'interfaccia standard unix per l'input/output sui file è basata su cinque -funzioni fondamentali \func{open}, \func{read}, \func{write}, \func{lseek} e +funzioni fondamentali: \func{open}, \func{read}, \func{write}, \func{lseek} e \func{close}, usate rispettivamente per aprire, leggere, scrivere, spostarsi e chiudere un file. @@ -165,7 +168,7 @@ system call del kernel. \label{sec:file_open} La funzione \func{open} è la funzione fondamentale per accedere ai file, ed è -quella che crea l'associazione fra un pathname ed un file descriptor; il suo +quella che crea l'associazione fra un pathname ed un file descriptor, il suo prototipo è: \begin{functions} \headdecl{sys/types.h} @@ -259,7 +262,7 @@ sempre il file descriptor con il valore pi \macro{O\_APPEND} & il file viene aperto in append mode. Prima di ciascuna scrittura la posizione corrente viene sempre settata alla fine del file. Può causare corruzione del file con NFS se più di un processo scrive - allo stesso tempo\footnotemark.\\ + allo stesso tempo.\footnotemark\\ \macro{O\_NONBLOCK} & il file viene aperto in modalità non bloccante per le operazioni di I/O: questo significa il fallimento di una \func{read} in assenza di dati da leggere e quello di una \func{write} in caso di @@ -305,11 +308,10 @@ sempre il file descriptor con il valore pi Questa caratteristica permette di prevedere qual'è il valore del file descriptor che si otterrà al ritorno di \func{open}, e viene talvolta usata da -alcune applicazioni per sostituire i file corrispondenti ai file standard di -\secref{sec:file_std_descr}: se ad esempio si chiude lo standard input e si -apre subito dopo un nuovo file questo diventerà il nuovo standard input (avrà -cioè il file descriptor 0). - +alcune applicazioni per sostituire i file corrispondenti ai file standard +visti in \secref{sec:file_std_descr}: se ad esempio si chiude lo standard +input e si apre subito dopo un nuovo file questo diventerà il nuovo standard +input (avrà cioè il file descriptor 0). Il nuovo file descriptor non è condiviso con nessun altro processo, (torneremo sulla condivisione dei file, in genere accessibile dopo una \func{fork}, in @@ -317,16 +319,16 @@ sulla condivisione dei file, in genere accessibile dopo una \func{fork}, in restare aperto attraverso una \func{exec} (come accennato in \secref{sec:proc_exec}) ed l'offset è settato all'inizio del file. -Il parametro \var{mode} specifica i permessi con cui il file viene +L'argomento \param{mode} specifica i permessi con cui il file viene eventualmente creato; i valori possibili sono gli stessi già visti in \secref{sec:file_perm_overview} e possono essere specificati come OR binario -delle costanti descritte in \tabref{tab:file_bit_perm}. Questi permessi -filtrati dal valore di \file{umask} (vedi \secref{sec:file_umask}) per il +delle costanti descritte in \tabref{tab:file_bit_perm}. Questi permessi sono +filtrati dal valore di \var{umask} (vedi \secref{sec:file_umask}) per il processo. La funzione prevede diverse opzioni, che vengono specificate usando vari bit -del parametro \var{flags}. Alcuni di questi bit vanno anche a costituire il -flag di stato del file (o \textit{file status flag}), che è mantenuto nel +dell'argomento \param{flags}. Alcuni di questi bit vanno anche a costituire +il flag di stato del file (o \textit{file status flag}), che è mantenuto nel campo \var{f\_flags} della struttura \var{file} (al solito si veda lo schema di \curfig). Essi sono divisi in tre categorie principali: \begin{itemize} @@ -335,11 +337,11 @@ di \curfig). Essi sono divisi in tre categorie principali: lettura/scrittura. Uno di questi bit deve essere sempre specificato quando si apre un file. Vengono settati alla chiamata da \func{open}, e possono essere riletti con una \func{fcntl} (fanno parte del \textit{file status - flag}), ma non modificati. + flag}), ma non possono essere modificati. \item \textsl{i bit delle modalità di apertura}: permettono di specificare alcune delle caratteristiche del comportamento di \func{open} quando viene eseguita. Hanno effetto solo al momento della chiamata della funzione e non - sono memorizzati nè possono essere riletti. + sono memorizzati né possono essere riletti. \item \textsl{i bit delle modalità di operazione}: permettono di specificare alcune caratteristiche del comportamento delle future operazioni sul file (come la \func{read} o la \func{write}). Anch'essi fanno parte del @@ -350,17 +352,17 @@ di \curfig). Essi sono divisi in tre categorie principali: In \tabref{tab:file_open_flags} si sono riportate, ordinate e divise fra loro secondo le tre modalità appena elencate, le costanti mnemoniche associate a -ciascuno di questi bit, dette costanti possono essere combinate fra di loro +ciascuno di questi bit. Dette costanti possono essere combinate fra di loro con un OR aritmetico per costruire il valore (in forma di maschera binaria) -del parametro \var{flags} da passare alla \func{open} per specificarne il +dell'argomento \param{flags} da passare alla \func{open} per specificarne il comportamento. I due flag \macro{O\_NOFOLLOW} e \macro{O\_DIRECTORY} sono estensioni specifiche di Linux, e deve essere usata definita la macro \macro{\_GNU\_SOURCE} per poterli usare. -Nelle prime versioni di unix i flag specificabili per \func{open} erano solo -quelli relativi alle modalità di accesso del file. Per questo motivo per -creare un nuovo file c'era una system call apposita, \func{creat}, il cui -prototipo è: +Nelle prime versioni di Unix i valori di \param{flag} specificabili per +\func{open} erano solo quelli relativi alle modalità di accesso del file. Per +questo motivo per creare un nuovo file c'era una system call apposita, +\func{creat}, il cui prototipo è: \begin{prototype}{fcntl.h} {int creat(const char *pathname, mode\_t mode)} Crea un nuovo file vuoto, con i permessi specificati da \var{mode}. É del @@ -378,8 +380,8 @@ descriptor ritorna disponibile; il suo prototipo \begin{prototype}{unistd.h}{int close(int fd)} Chiude il descrittore \var{fd}. - \bodydesc{La funzione ritorna 0 in caso di successo e -1 n caso di errore. - In questo caso \var{errno} è settata ai valori: + \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore, + ed in questo caso \var{errno} è settata ai valori: \begin{errlist} \item[\macro{EBADF}] \var{fd} non è un descrittore valido. \item[\macro{EINTR}] la funzione è stata interrotta da un segnale. @@ -389,7 +391,7 @@ descriptor ritorna disponibile; il suo prototipo La chiusura di un file rilascia ogni blocco (il \textit{file locking} è trattato in \secref{sec:file_locking}) che il processo poteva avere acquisito -su di esso; se \var{fd} è ultimo (di eventuali copie) riferimento ad un file +su di esso; se \var{fd} è l'ultimo riferimento (di eventuali copie) ad un file aperto, tutte le risorse nella file table vengono rilasciate. Infine se il file descriptor era l'ultimo riferimento ad un file su disco quest'ultimo viene cancellato. @@ -402,8 +404,8 @@ implementano la tecnica del \textit{write-behind}, per cui una \func{write} può avere successo anche se i dati non sono stati scritti, un eventuale errore di I/O allora può sfuggire, ma verrà riportato alla chiusura del file: per questo motivo non effettuare il controllo può portare ad una perdita di dati -inavvertita; in Linux questo comportamento è stato osservato con NFS e le -quote su disco. +inavvertita.\footnote{in Linux questo comportamento è stato osservato con NFS + e le quote su disco.} In ogni caso una \func{close} andata a buon fine non garantisce che i dati siano stati effettivamente scritti su disco, perché il kernel può decidere di @@ -411,7 +413,8 @@ ottimizzare l'accesso a disco ritardandone la scrittura. L'uso della funzione \func{sync} (vedi \secref{sec:file_sync}) effettua esplicitamente il \emph{flush} dei dati, ma anche in questo caso resta l'incertezza dovuta al comportamento dell'hardware (che a sua volta può introdurre ottimizzazioni -dell'accesso al disco). +dell'accesso al disco che ritardano la scrittura dei dati, da cui l'abitudine +di ripetere tre volte il comando prima di eseguire lo shutdown). \subsection{La funzione \func{lseek}} @@ -452,7 +455,7 @@ seguenti valori\footnote{per compatibilit \item[\macro{SEEK\_SET}] si fa riferimento all'inizio del file: il valore di \var{offset} è la nuova posizione. \item[\macro{SEEK\_CUR}] si fa riferimento alla posizione corrente del file: - \var{offset} che può essere negativo e positivo. + \var{offset} può essere negativo e positivo. \item[\macro{SEEK\_END}] si fa riferimento alla fine del file: il valore di \var{offset} può essere negativo e positivo. \end{basedescript} @@ -465,7 +468,7 @@ kernel (cio Dato che la funzione ritorna la nuova posizione, usando il valore zero per \param{offset} si può riottenere la posizione corrente nel file chiamando la -funzione con \code{lseek(fd, 0, SEEK\_CUR}. +funzione con \code{lseek(fd, 0, SEEK\_CUR)}. Si tenga presente inoltre che usare \macro{SEEK\_END} non assicura affatto che successiva scrittura avvenga alla fine del file, infatti se questo è stato @@ -646,7 +649,7 @@ Cerca di scrivere sul file \var{fd}, a partire dalla posizione \var{offset}, in caso di errore, nel qual caso \var{errno} viene settata secondo i valori già visti per \func{write} e \func{lseek}.} \end{prototype} -e per essa valgono le stesse considerazioni fatte per \func{pread}. +\noindent e per essa valgono le stesse considerazioni fatte per \func{pread}. \section{Caratteristiche avanzate} @@ -654,8 +657,9 @@ e per essa valgono le stesse considerazioni fatte per \func{pread}. In questa sezione approfondiremo alcune delle caratteristiche più sottili della gestione file in un sistema unix-like, esaminando in dettaglio il -comportamento delle funzioni base, inoltre tratteremo alcune funzioni che -permettono di eseguire operazioni avanzate con i file. +comportamento delle funzioni base, inoltre tratteremo le funzioni che +permettono di eseguire alcune operazioni avanzate con i file (il grosso +dell'argomento sarà comunque affrontato in \capref{cha:file_advanced}). \subsection{La condivisione dei files} @@ -709,13 +713,13 @@ stesso file, in particolare occorre tenere presente che: \label{fig:file_acc_child} \end{figure} -È comunque possibile che due file descriptor di due processi diversi puntino -alla stessa voce nella \textit{file table}; questo è ad esempio il caso dei -file aperti che vengono ereditati dal processo figlio all'esecuzione di una -\func{fork} (si ricordi quanto detto in \secref{sec:proc_fork}). La situazione -è illustrata in \figref{fig:file_acc_child}; dato che il processo figlio -riceve una copia dello spazio di indirizzi del padre, riceverà anche una copia -di \var{file\_struct} e relativa tabella dei file aperti. +Il secondo caso è quello in cui due file descriptor di due processi diversi +puntino alla stessa voce nella \textit{file table}; questo è ad esempio il +caso dei file aperti che vengono ereditati dal processo figlio all'esecuzione +di una \func{fork} (si ricordi quanto detto in \secref{sec:proc_fork}). La +situazione è illustrata in \figref{fig:file_acc_child}; dato che il processo +figlio riceve una copia dello spazio di indirizzi del padre, riceverà anche +una copia di \var{file\_struct} e relativa tabella dei file aperti. In questo modo padre e figlio avranno gli stessi file descriptor che faranno riferimento alla stessa voce nella \textit{file table}, condividendo così la @@ -724,14 +728,15 @@ posizione corrente sul file. Questo ha le conseguenze descritte a suo tempo in corrente nel file varierà per entrambi i processi (in quanto verrà modificato \var{f\_pos} che è la stesso per entrambi). -Si noti inoltre che anche i flag di stato del file (quelli settati dal -parametro \var{flag} di \func{open}) essendo tenuti nella voce della -\textit{file table} (il campo \var{f\_flag} di \var{file}), vengono in questo -caso condivisi. Ai file però sono associati anche altri flag (l'unico usato al -momento è \macro{FD\_CLOEXEC}), detti \textit{file descriptor flags}, tenuti -invece in \var{file\_struct}; questi sono specifici di ciascun processo, e non -vengono toccati anche in caso di condivisione della voce della \textit{file - table}. +Si noti inoltre che anche i flag di stato del file (quelli settati +dall'argomento \param{flag} di \func{open}) essendo tenuti nella voce della +\textit{file table}\footnote{per la precisione nel campo \var{f\_flags} di + \var{file}}, vengono in questo caso condivisi. Ai file però sono associati +anche altri flag, dei quali l'unico usato al momento è \macro{FD\_CLOEXEC}, +detti \textit{file descriptor flags}. Questi ultimi sono tenuti invece in +\var{file\_struct}, e perciò sono specifici di ciascun processo e non vengono +modificati dalle azioni degli altri anche in caso di condivisione della stessa +voce della \textit{file table}. @@ -762,11 +767,12 @@ file, e la successiva \func{write} sovrascriver Il problema è che usare due system call in successione non è una operazione atomica; il problema è stato risolto introducendo la modalità -\macro{O\_APPEND}, in questo caso infatti, come abbiamo visto, è il kernel che -aggiorna automaticamente la posizione alla fine del file prima di effettuare -la scrittura, e poi estende il file. Tutto questo avviene all'interno di una -singola system call (la \func{write}) che non essendo interrompibile da un -altro processo costituisce una operazione atomica. +\macro{O\_APPEND}. In questo caso infatti, come abbiamo descritto in +precedenza, è il kernel che aggiorna automaticamente la posizione alla fine +del file prima di effettuare la scrittura, e poi estende il file. Tutto questo +avviene all'interno di una singola system call (la \func{write}) che non +essendo interrompibile da un altro processo costituisce una operazione +atomica. Un altro caso tipico in cui è necessaria l'atomicità è quello in cui si vuole creare un file di lock, bloccandosi se il file esiste. In questo caso la @@ -775,11 +781,11 @@ sequenza logica porterebbe a verificare prima l'esistenza del file con una possibilità di una race condition da parte di un altro processo che crea lo stesso file fra il controllo e la creazione. -Per questo motivo sono stati introdotti i due flag \macro{O\_CREAT} e -\macro{O\_EXCL}, in questo modo l'operazione di controllo dell'esistenza del -file (con relativa uscita dalla funzione con un errore) e creazione in caso di -assenza, diventa atomica essendo svolta tutta all'interno di una singola -\func{open}. +Per questo motivo sono stati introdotti pe \func{open} i due flag +\macro{O\_CREAT} e \macro{O\_EXCL}. In questo modo l'operazione di controllo +dell'esistenza del file (con relativa uscita dalla funzione con un errore) e +creazione in caso di assenza, diventa atomica essendo svolta tutta all'interno +di una singola system call. \subsection{La funzioni \func{sync} e \func{fsync}} @@ -839,7 +845,6 @@ file specificato, ed attendono fino alla conclusione delle operazioni; \func{fsync} forza anche la sincronizzazione dei metadata dell'inode (i dati di \var{fstat} come i tempi del file). - Si tenga presente che questo non comporta la sincronizzazione della directory che contiene il file (e scrittura della relativa voce su disco) che deve essere effettuata esplicitamente\footnote{in realtà per @@ -890,7 +895,7 @@ posizione su uno dei due file descriptor essa sar table} a cui entrambi fanno riferimento). L'unica differenza fra i due file descriptor è che ciascuno avrà il suo -\textit{file descriptor flag}; nel caso di \func{dup} il flag di \textit{close +\textit{file descriptor flag}: nel caso di \func{dup} il flag di \textit{close on exec} viene sempre cancellato nella copia. Una diversa versione della funzione, \func{dup2} viene utilizzata per -- 2.30.2 From 607aae2f62d086e9c71d6a571bbbb6908631207c Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 22 Feb 2002 18:25:03 +0000 Subject: [PATCH 04/16] Quattro chiacchiere sullo scheduling --- prochand.tex | 83 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/prochand.tex b/prochand.tex index 4eb3d74..4c16fe2 100644 --- a/prochand.tex +++ b/prochand.tex @@ -290,12 +290,12 @@ 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 +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 della creazione di - un nuovo processo.}, pertanto padre e figlio vedono variabili diverse. + un nuovo processo.} 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 \func{fork} è il \acr{pid} del processo figlio, mentre @@ -1806,10 +1806,81 @@ quando si definisce \macro{\_POSIX\_SOURCE} o si compila con il flag \label{sec:proc_priority} In questa sezione tratteremo più approfonditamente i meccanismi con il quale -lo \textit{scheduler} assegna la CPU ai vari processi attivi, illustrando le -varie funzioni che permettono di leggere e modificare le priorità di -esecuzione dei programmi. - +lo \textit{scheduler}\footnote{che è la parte del kernel che si occupa di + stabilire quale processo dovrà essere posto in esecuzione.} assegna la CPU +ai vari processi attivi. In particolare prendremo in esame i vari meccanismi +con cui viene gestita l'assgnazione del tempo di CPU, ed illustreremo le varie +funzioni di gestione. + + +\subsection{I meccanismi di \textit{scheduling}} +\label{sec:proc_sched} + +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 ogni caso essa dipende in maniera +essenziale anche dal tipo di utilizzo che deve essere fatto del sistema. + +La cosa è resa ancora più complicata dal fatto che con sistemi +multi-processore si introduce anche la complessità dovuta alla scelta di quale +sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni la + presenza di ampie cache può rendere poco efficiente trasferire l'esecuzione + di un processo da una CPU ad un'altra, per cui occorrono meccanismi per + determininare quale è la migliore scelta fra le diverse CPU.} Tutto questo +comunque appartiene alle sottigliezze dell'implementazione del kernel, e dal +punto di vista dei programmi che girano in user space di può pensare sempre +alla risorsa tempo di esecuzione, governata dagli stessi mecca, che nel caso +di più processori sarà a disposizione di più di un processo alla volta. + +Si tenga presente inoltre che l'utilizzo della CPU è soltanto una delle +risorse (insieme alla memoria e all'accesso alle periferiche) che sono +necessarie per l'esecuzione di un programma, e spesso non è neanche la più +importante. Per questo non è affatto detto che dare ad un programma la massima +priorità di esecuzione abbia risultati significativi in termini di +prestazioni. + +La politica tradizionale di scheduling di Unix (che tratteremo in +\secref{sec:proc_sched_stand}) è sempre stata basata su delle priorità +dinamiche, che assicurassaro che tutti i processi, anche i meno importanti, +potessero ricevere un po' di tempo di CPU. + +Lo standard POSIX però per tenere conto dei sistemi real-time,\footnote{per + sistema real-time si intende un sistema in grado di eseguire operazioni in + tempo reale; 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 in che devono essere +eseguiti in un determinato momento non debbano aspettare la conclusione di un +altri processi che non hanno questa necessità, ha introdotto il concetto di +\textsl{priorità assoluta}, chimata anche \textsl{priorità statica}, in +contrapposizione con la normale priorità dinamica. + +Il concetto di prorità assoluta dice che quando due processi si contendono +l'esecuzione, vince sempre quello con la priorità assoluta più alta, anche, +grazie al \textit{prehemptive scheduling}, se l'altro è in esecuzione. +Ovviamente questo avviene solo per i processi che sono pronti per essere +eseguiti (cioè nello stato \textit{runnable}\footnote{lo stato di un processo + è riportato nel campo \texttt{STAT} dell'output del comando \cmd{ps}, + abbiamo già visto che lo stato di \textit{zombie} è indicato con \texttt{Z}, + gli stati \textit{runnable}, \textit{sleep} e di I/O (\textit{uninteruttible + sleep}) sono invece indicati con \texttt{R}, \texttt{S} e \texttt{D}.}), +la priorità assoluta viene invece ignorata per quelli che sono bloccati su una +richiesta di I/O o in stato di \textit{sleep}. + +Questa viene in genere indicata con un numero + + + + +\subsection{Il meccanismo di \textit{scheduling} standard} +\label{sec:proc_sched_stand} + +In Linux tutti i processi hanno sostanzialmente la stessa priorità; benché sia +possibile specificare una priorità assoluta secondo lo standard POSIX +(argomento che tratteremo più avanti) l'uso comune segue quello che è il +meccanismo tradizionale con cui i sistemi -- 2.30.2 From af29167e31ac4adf834095368a716b489d8f0b8e Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 24 Feb 2002 00:23:36 +0000 Subject: [PATCH 05/16] Aggiunte le sezioni da fare --- signal.tex | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/signal.tex b/signal.tex index 40be93c..6f45f11 100644 --- a/signal.tex +++ b/signal.tex @@ -267,9 +267,8 @@ segnale. Per alcuni segnali (\macro{SIGKILL} e \macro{SIGSTOP}) questa azione una delle tre possibilità seguenti: \begin{itemize*} -\item \textsl{ignorare} il segnale. -\item \textsl{catturare} il segnale, ed utilizzare il manipolatore - specificato. +\item ignorare il segnale. +\item catturare il segnale, ed utilizzare il manipolatore specificato. \item accettare l'azione di default per quel segnale. \end{itemize*} @@ -763,15 +762,8 @@ indicizzate per numero di segnale, per cui una chiamata del tipo di \code{char -\section{La gestione dei segnali} -\label{sec:sig_handlers} - -I segnali sono il primo e più classico esempio di eventi asincroni, cioè di -eventi che possono accadere in un qualunque momento durante l'esecuzione di un -programma. Dato che la loro gestione non è sotto il controllo del programma -essa non può essere effettuata all'interno del normale flusso di esecuzione, -per cui tutto quello che si può fare è di specificare (al kernel, installando -un manipolatore) quale azione andrà intrapresa quando essi si verificano. +\section{Le funzioni di base dei segnali} +\label{sec:sig_base_func} In questa sezione vedremo allora come si gestiscono i segnali, esaminando le funzioni che si usano per effettuare la gestione dei segnali ed analizzando le @@ -832,18 +824,41 @@ installare l'azione di di default\footnote{si ricordi per +\subsection{La funzione \func{sigpending}} +\label{sec:sig_sigpending} + + + + +\subsection{Le funzioni \func{kill} e \func{raise}} +\label{sec:sig_kill_raise} + +\subsection{Le funzioni \func{alarm} e \func{pause}} +\label{sec:sig_kill_raise} + + + + +\section{La programmazione con i segnali} +\label{sec:sig_use} + +I segnali sono il primo e più classico esempio di eventi asincroni, cioè di +eventi che possono accadere in un qualunque momento durante l'esecuzione di un +programma. Dato che la loro gestione non è sotto il controllo del programma +essa non può essere effettuata all'interno del normale flusso di esecuzione, +per cui tutto quello che si può fare è di specificare (al kernel, installando +un manipolatore) quale azione andrà intrapresa quando essi si verificano. \subsection{Funzioni rientranti e default dei segnali} \label{sec:sig_reentrant} +\subsection{La funzione \func{sigaction}} +\label{sec:sig_sigaction} -\subsection{La funzione \func{sigpending}} -\label{sec:sig_sigpending} - %%% Local Variables: %%% mode: latex -- 2.30.2 From ac5b743a5b952950626aa675bf6f4f51b030d309 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Mon, 25 Feb 2002 00:15:24 +0000 Subject: [PATCH 06/16] Quattro chiacchiere su fork, exec e segnali --- prochand.tex | 11 +++---- signal.tex | 85 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/prochand.tex b/prochand.tex index 4c16fe2..c7f441d 100644 --- a/prochand.tex +++ b/prochand.tex @@ -591,8 +591,8 @@ comune dopo l'esecuzione di una \func{fork} \item la directory di lavoro e la directory radice (vedi \secref{sec:file_work_dir} e \secref{sec:file_chroot}). \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}). +\item la maschera dei segnali bloccati (vedi \secref{sec:sig_sigpending}) e le + azioni installate (vedi \secref{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}). @@ -608,7 +608,8 @@ le differenze fra padre e figlio dopo la \func{fork} invece sono: 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 gli allarmi ed i segnali pendenti (vedi \secref{sec:sig_gen_beha}), che + per il figlio vengono cancellati. \end{itemize*} @@ -1199,7 +1200,7 @@ la lista completa \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}). + \secref{sec:sig_sigpending}). \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}). @@ -1210,7 +1211,7 @@ 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}). +\secref{sec:sig_gen_beha}). La gestione dei file aperti dipende dal valore che ha il flag di \textit{close-on-exec} (trattato in \secref{sec:file_fcntl}) per ciascun file diff --git a/signal.tex b/signal.tex index 6f45f11..38c3d58 100644 --- a/signal.tex +++ b/signal.tex @@ -73,7 +73,7 @@ semantiche) che vengono chiamate rispettivamente semantica \textsl{affidabile} \textit{unreliable}). Nella semantica \textsl{inaffidabile} (quella implementata dalle prime -versioni di unix) la routine di gestione del segnale specificata dall'utente +versioni di Unix) la routine di gestione del segnale specificata dall'utente non resta attiva una volta che è stata eseguita; è perciò compito dell'utente stesso ripetere l'installazione della stessa all'interno della routine di gestione, in tutti i casi in cui si vuole che il manipolatore esterno resti @@ -762,18 +762,74 @@ indicizzate per numero di segnale, per cui una chiamata del tipo di \code{char -\section{Le funzioni di base dei segnali} -\label{sec:sig_base_func} +\section{La gestione dei segnali} +\label{sec:sig_management} + +I segnali sono il primo e più classico esempio di eventi asincroni, cioè di +eventi che possono accadere in un qualunque momento durante l'esecuzione di un +programma. Dato che la loro gestione non è sotto il controllo del programma +essa non può essere effettuata all'interno del normale flusso di esecuzione. + +In questa sezione vedremo come si effettua gestione dei segnali, a partire dal +comportamento del sistema, passando per le varie funzioni relative ai segnali, +affrontando inoltre le varie promblematiche di programmazione che si devono +tenere presenti quando si ha a che fare con essi. + + +\subsection{Il comportamento generale del sistema.} +\label{sec:sig_gen_beha} + +Abbiamo già trattato in \secref{sec:sig_intro} le modalità con cui il sistema +gestisce l'interazione fra segnali e processi, ci resta da esaminare però il +comportamento delle system call; in particolare due di esse, \func{fork} ed +\func{exec}, dovranno essere prese esplicitamente in considerazione, data la +loro stretta relazione con la creazione di nuovi processi. + +Come accennato in \secref{sec:proc_fork} quando viene creato un nuovo processo +con \func{fork} esso eredita dal padre sia le azioni che sono state settate +per i singoli segnali, che la maschera dei segnali bloccati (tratteremo +quest'ultimo argomento in \ref{sec:sig_sigpending}). Invece tutti i segnali +pendenti e gli allarmi vengono cancellati; essi infatti devono essere +recapitati solo al padre, al figlio dovranno arrivare solo i segnali dovuti +alle sue azioni. + +Quando si mette in esecuzione un nuovo programma con \func{exec} (si ricordi +quanto detto in \secref{sec:prog_exec}) tutti i segnali per i quali è stato +installato un manipolatore vengono resettati a \macro{SIG\_DFL}. Non ha più +senso infatti fare riferimento a funzioni definite nel programma originario, +che non sono nemmeno presenti nello spazio di indirizzi del nuovo programma. + +Si noti che questo vale solo per le azioni per le quali è stato installato un +manipolatore; viene mantenuto invece ogni eventuale settaggio dell'azione a +\macro{SIG\_IGN}. Questo permette ad esempio alla shell di settare ad +\macro{SIG\_IGN} le risposte per \macro{SIGINT} e \macro{SIGQUIT} per i +programmi eseguiti in background, che altrimenti sarebbero interrotti da una +successiva pressione di \texttt{C-c} o \texttt{C-y}. + +Per quanto riguarda tutte le altre system call esse vengono tradizionalmente +classificate, proprio in base al loro comportamento nei confronti dei segnali, +in lente (\textit{slow}) e veloci (\textit{fast}). La gran parte appartiene a +quest'ultima categoria che non è influenzata dall'arrivo di un segnale. In tal +caso un eventuale manipolatore viene sempre eseguito dopo che la system call è +stata completata. Esse sono dette \textit{fast} proprio in quanto attendere la +loro esecuzione per eseguire un manipolatore non comporta nessun +inconveniente. + +Esistono però dei casi (ad esempio le funzioni di I/O che si bloccano in +attesa di dati in ingresso) in cui tutto questo non è possibile proprio perché +renderebbe impossibile una risposta pronta al segnale (per questo sono +chiamate \textit{slow}), pertanto un eventuale manipolatore sarà eseguito +prima che la system call sia ritornata. + +In quest'ultimo caso si pone il problema di cosa fare una volta che il +manipolatori ritorni. La scelta -In questa sezione vedremo allora come si gestiscono i segnali, esaminando le -funzioni che si usano per effettuare la gestione dei segnali ed analizzando le -problematiche relative alla gestione di eventi asincroni di questo tipo. \subsection{La funzione \func{signal}} \label{sec:sig_signal} -L'interfaccia più semplice alla manipolazione dei segnali è costituita dalla +L'interfaccia più semplice per la gestione dei segnali è costituita dalla funzione \func{signal} che è definita fin dallo standard ANSI C. Quest'ultimo però non considera sistemi multitasking, per cui la definizione è tanto vaga da essere del tutto inutile in un sistema Unix; è questo il motivo per cui @@ -824,7 +880,9 @@ installare l'azione di di default\footnote{si ricordi per -\subsection{La funzione \func{sigpending}} + + +\subsection{Le funzioni \func{sigprocmask} e \func{sigpending}} \label{sec:sig_sigpending} @@ -839,17 +897,6 @@ installare l'azione di di default\footnote{si ricordi per -\section{La programmazione con i segnali} -\label{sec:sig_use} - -I segnali sono il primo e più classico esempio di eventi asincroni, cioè di -eventi che possono accadere in un qualunque momento durante l'esecuzione di un -programma. Dato che la loro gestione non è sotto il controllo del programma -essa non può essere effettuata all'interno del normale flusso di esecuzione, -per cui tutto quello che si può fare è di specificare (al kernel, installando -un manipolatore) quale azione andrà intrapresa quando essi si verificano. - - \subsection{Funzioni rientranti e default dei segnali} \label{sec:sig_reentrant} -- 2.30.2 From fb4a1ece9397b5063ff0920df17389aba882d328 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Mon, 25 Feb 2002 16:53:03 +0000 Subject: [PATCH 07/16] Aggiunta la shell di Alessio per generare le figure. --- geneimg.sh | 8 ++++++++ signal.tex | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 geneimg.sh diff --git a/geneimg.sh b/geneimg.sh new file mode 100644 index 0000000..b648322 --- /dev/null +++ b/geneimg.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# Contributo di A. Frusciante +for i in img/*dia +do +j=`basename $i .dia` +dia -e img/$j.eps $i +epstopdf img/$j.eps --outfile=img/$j.pdf +done diff --git a/signal.tex b/signal.tex index 38c3d58..ce3ee76 100644 --- a/signal.tex +++ b/signal.tex @@ -892,7 +892,7 @@ installare l'azione di di default\footnote{si ricordi per \label{sec:sig_kill_raise} \subsection{Le funzioni \func{alarm} e \func{pause}} -\label{sec:sig_kill_raise} +\label{sec:sig_alarm_pause} -- 2.30.2 From 432c8645b755cad22c7b9d4b291ea09432eb7f0f Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Mon, 25 Feb 2002 18:45:29 +0000 Subject: [PATCH 08/16] slow system call e segnali --- signal.tex | 73 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/signal.tex b/signal.tex index ce3ee76..cb163ba 100644 --- a/signal.tex +++ b/signal.tex @@ -767,13 +767,14 @@ indicizzate per numero di segnale, per cui una chiamata del tipo di \code{char I segnali sono il primo e più classico esempio di eventi asincroni, cioè di eventi che possono accadere in un qualunque momento durante l'esecuzione di un -programma. Dato che la loro gestione non è sotto il controllo del programma -essa non può essere effettuata all'interno del normale flusso di esecuzione. +programma. Per questa loro caratteristica la loro gestione non può essere +effettuata all'interno del normale flusso di esecuzione dello stesso, ma è +delegata appunto agli eventuali manipolatori che si sono installati. -In questa sezione vedremo come si effettua gestione dei segnali, a partire dal -comportamento del sistema, passando per le varie funzioni relative ai segnali, -affrontando inoltre le varie promblematiche di programmazione che si devono -tenere presenti quando si ha a che fare con essi. +In questa sezione vedremo come si effettua gestione dei segnali, a partire +dalla loro interazione con le system call, passando per le varie funzioni che +permettono di installare i manipolatori e controllare le reazioni di un +processo alla loro occorrenza. \subsection{Il comportamento generale del sistema.} @@ -808,21 +809,32 @@ successiva pressione di \texttt{C-c} o \texttt{C-y}. Per quanto riguarda tutte le altre system call esse vengono tradizionalmente classificate, proprio in base al loro comportamento nei confronti dei segnali, -in lente (\textit{slow}) e veloci (\textit{fast}). La gran parte appartiene a -quest'ultima categoria che non è influenzata dall'arrivo di un segnale. In tal -caso un eventuale manipolatore viene sempre eseguito dopo che la system call è -stata completata. Esse sono dette \textit{fast} proprio in quanto attendere la -loro esecuzione per eseguire un manipolatore non comporta nessun -inconveniente. - -Esistono però dei casi (ad esempio le funzioni di I/O che si bloccano in -attesa di dati in ingresso) in cui tutto questo non è possibile proprio perché -renderebbe impossibile una risposta pronta al segnale (per questo sono -chiamate \textit{slow}), pertanto un eventuale manipolatore sarà eseguito -prima che la system call sia ritornata. +in \textsl{lente} (\textit{slow}) e \textsl{veloci} (\textit{fast}). La gran +parte appartiene a quest'ultima categoria che non è influenzata dall'arrivo di +un segnale. In tal caso un eventuale manipolatore viene sempre eseguito dopo +che la system call è stata completata. Esse sono dette \textsl{veloci} proprio +in quanto la loro esecuzione è sostanzialmente immediata e attendere per +eseguire un manipolatore non comporta nessun inconveniente. + +Esistono però dei casi in cui questo non è possibile perché renderebbe +impossibile una risposta pronta al segnale. In generale questo avviene tutte +le volte che si ha a che fare con system call che possono bloccarsi +indenfinitamente, che per questo vengono chiamate \textsl{lente}. Un elenco +dei casi in cui si presenta questa situazione è il seguente: +\begin{itemize*} +\item lettura da file che possono bloccarsi in attesa di dati non ancora + presenti (come per certi dispositivi, la rete o le pipe). +\item scrittura sugli stessi file, nel caso in cui dati non possano essere + accettati immediatamente. +\item apertura di un file di dipositivo che richiede operazioni non immediate + per una una risposta. +\item operazioni eseguite con \func{ioctl} che non è detto possano essere + eseguite immediatamente. +\end{itemize*} -In quest'ultimo caso si pone il problema di cosa fare una volta che il -manipolatori ritorni. La scelta +In questo caso si pone il problema di cosa fare una volta che il manipolatore +sia ritornato. La scelta originaria dei primi Unix era quella di far ritornare +la system call con un errore di \macro{EINTR}, @@ -880,11 +892,8 @@ installare l'azione di di default\footnote{si ricordi per - - -\subsection{Le funzioni \func{sigprocmask} e \func{sigpending}} -\label{sec:sig_sigpending} - +\subsection{Funzioni rientranti e default dei segnali} +\label{sec:sig_reentrant} @@ -895,10 +904,15 @@ installare l'azione di di default\footnote{si ricordi per \label{sec:sig_alarm_pause} +\section{Il controllo dei segnali} +\label{sec:sig_control} -\subsection{Funzioni rientranti e default dei segnali} -\label{sec:sig_reentrant} + + +\subsection{Le funzioni \func{sigprocmask} e \func{sigpending}} +\label{sec:sig_sigpending} + \subsection{La funzione \func{sigaction}} @@ -907,6 +921,11 @@ installare l'azione di di default\footnote{si ricordi per +, affrontando inoltre le varie problematiche di programmazione che si devono +tenere presenti quando si ha a che fare con essi. + + + %%% Local Variables: %%% mode: latex %%% TeX-master: "gapil" -- 2.30.2 From 2bb235e9ba8e8d532251205d30b82455dc119894 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Wed, 27 Feb 2002 11:47:52 +0000 Subject: [PATCH 09/16] Correzioni multiple da Daniele Masini --- ChangeLog | 5 ++ intro.tex | 76 +++++++++++----------- pref.tex | 10 +-- process.tex | 173 +++++++++++++++++++++++++++------------------------ prochand.tex | 24 +++++-- 5 files changed, 159 insertions(+), 129 deletions(-) diff --git a/ChangeLog b/ChangeLog index e343f4c..f1a6a7b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2002-02-27 Simone Piccardi + + * pref.tex, intro.tex, process.tex, prochand.tex: Correzioni + multiple da Daniele Masini. + 2001-02-27 Simone Piccardi * network.tex: completata (si fa per dire) la parte introduttiva. diff --git a/intro.tex b/intro.tex index 5e9ade0..b03d1e3 100644 --- a/intro.tex +++ b/intro.tex @@ -6,7 +6,7 @@ cui fornire una base di comprensione mirata a sottolineare le peculiarità del sistema che sono più rilevanti per quello che riguarda la programmazione. -Dopo una introduzione sulle caratteristiche principali di un sistema di tipo +Dopo un'introduzione sulle caratteristiche principali di un sistema di tipo unix passeremo ad illustrare alcuni dei concetti base dell'architettura di GNU/Linux (che sono comunque comuni a tutti i sistemi \textit{unix-like}) ed introdurremo alcuni degli standard principali a cui viene fatto riferimento. @@ -79,7 +79,7 @@ Uno dei concetti fondamentali su cui si basa l'architettura dei sistemi unix quello della distinzione fra il cosiddetto \textit{user space}, che contraddistingue l'ambiente in cui vengono eseguiti i programmi, e il \textit{kernel space}, che è l'ambiente in cui viene eseguito il kernel. Ogni -programma vede se stesso come se avesse la piena disponibilità della CPU e +programma vede sé stesso come se avesse la piena disponibilità della CPU e della memoria ed è, salvo i meccanismi di comunicazione previsti dall'architettura, completamente ignaro del fatto che altri programmi possono essere messi in esecuzione dal kernel. @@ -138,9 +138,9 @@ si aspetta da un sistema operativo. Come accennato le interfacce con cui i programmi possono accedere all'hardware vanno sotto il nome di chiamate al sistema (le cosiddette \textit{system call}), si tratta di un insieme di funzioni, che un programma può chiamare, -per le quali viene generata una interruzione processo ed il controllo passa -dal programma al kernel. Sarà poi quest'ultimo che (oltre a compiere una serie -di operazioni interne come la gestione del multitasking e l'allocazione della +per le quali viene generata un'interruzione processo ed il controllo passa dal +programma al kernel. Sarà poi quest'ultimo che (oltre a compiere una serie di +operazioni interne come la gestione del multitasking e l'allocazione della memoria) eseguirà la funzione richiesta in \textit{kernel space} restituendo i risultati al chiamante. @@ -193,7 +193,7 @@ danneggiarsi a vicenda o danneggiare il sistema. Ad ogni utente è dato un nome \textit{username}, che è quello che viene richiesto all'ingresso nel sistema dalla procedura di \textit{login}. Questa -procedura si incarica di verificare la identità dell'utente, in genere +procedura si incarica di verificare l'identità dell'utente, in genere attraverso la richiesta di una parola d'ordine, anche se sono possibili meccanismi diversi\footnote{Ad esempio usando la libreria PAM (\textit{Pluggable Autentication Methods}) è possibile astrarre @@ -202,7 +202,7 @@ meccanismi diversi\footnote{Ad esempio usando la libreria PAM Eseguita la procedura di riconoscimento in genere il sistema manda in esecuzione un programma di interfaccia (che può essere la \textit{shell} su -terminale o una interfaccia grafica) che mette a disposizione dell'utente un +terminale o un'interfaccia grafica) che mette a disposizione dell'utente un meccanismo con cui questo può impartire comandi o eseguire altri programmi. Ogni utente appartiene anche ad almeno un gruppo (il cosiddetto @@ -285,14 +285,13 @@ usare le varie estensioni al linguaggio e al preprocessore da esso supportate. \label{sec:intro_posix} Uno standard più attinente al sistema nel suo complesso (e che concerne sia il -kernel che le librerie e` lo standard POSIX. Esso prende origine dallo -standard ANSI C, che contiene come sottoinsieme, prevedendo ulteriori capacità -per le funzioni in esso definite, ed aggiungendone di nuove. Le estensioni -principali sono +kernel che le librerie è lo standard POSIX. Esso prende origine dallo standard +ANSI C, che contiene come sottoinsieme, prevedendo ulteriori capacità per le +funzioni in esso definite, ed aggiungendone di nuove. In realtà POSIX è una famiglia di standard diversi, il cui nome, suggerito da Richard Stallman, sta per \textit{Portable Operating System Interface}, ma la -X finale denuncia la sua stretta relazione con i sistemi unix. Esso nasce dal +X finale denuncia la sua stretta relazione con i sistemi Unix. Esso nasce dal lavoro dell'IEEE (\textit{Institute of Electrical and Electronics Engeneers}) che ne produsse una prima versione, nota come IEEE 1003.1-1988, mirante a standardizzare l'interfaccia con il sistema operativo. @@ -302,12 +301,12 @@ libreria, e in seguito sono stati prodotti anche altri standard per la shell e le utility di sistema (1003.2), per le estensioni realtime e per i thread (1003.1d e 1003.1c) e vari altri. -Benché lo standard POSIX sia basato sui sistemi unix esso definisce comunque -una interfaccia e non fa riferimento ad una specifica implementazione (ad -esempio esiste anche una implementazione di questo standard pure sotto Windows -NT). Lo standard si è evoluto nel tempo ed una versione più aggiornata (quella -che viene normalmente denominata POSIX.1) è stata rilasciata come standard -internazionale con la sigla ISO/IEC 9945-1:1996. +Benché lo standard POSIX sia basato sui sistemi Unix esso definisce comunque +un'interfaccia e non fa riferimento ad una specifica implementazione (ad +esempio esiste un'implementazione di questo standard anche sotto Windows NT). +Lo standard si è evoluto nel tempo ed una versione più aggiornata (quella che +viene normalmente denominata POSIX.1) è stata rilasciata come standard +internazionale con la sigla ISO/IEC 9945-1:1996. Le \acr{glibc} implementano tutte le funzioni definite nello standard POSIX.1, e Linux; @@ -317,14 +316,14 @@ e Linux; \label{sec:intro_xopen} Il consorzio X/Open nacque nel 1984 come consorzio di venditori di sistemi -unix per giungere ad una armonizzazione delle varie implementazioni. Per far +unix per giungere ad un'armonizzazione delle varie implementazioni. Per far questo iniziò a pubblicare una serie di documentazioni e specifiche sotto il nome di \textit{X/Open Portability Guide} (a cui di norma si fa riferimento con l'abbreviazione XPGn). Nel 1989 produsse una terza versione di questa guida particolarmente -voluminosa (la \textit{X/Open Portability Guide, Issue 3}), contenente una -ulteriore standardizzazione dell'interfaccia sistema unix, che venne presa +voluminosa (la \textit{X/Open Portability Guide, Issue 3}), contenente +un'ulteriore standardizzazione dell'interfaccia sistema unix, che venne presa come riferimento da vari produttori. Questo standard, detto anche XPG3 dal nome della suddetta guida, è sempre @@ -396,10 +395,11 @@ marchio depositato, sviluppandone una serie di versioni diverse; nel 1983 la versione supportata ufficialmente venne rilasciata al pubblico con il nome di Unix System V. Negli anni successivi l'AT/T proseguì lo sviluppo rilasciando varie versioni con aggiunte e integrazioni; nel 1989 un accordo fra vari -venditori (AT/T, Sun, HP, e altro) portò ad una versione che provvedeva una -unificazione dell interfacce comprendente Xenix e BSD, la System V release 4. +venditori (AT/T, Sun, HP, e altro) portò ad una versione che provvedeva +un'unificazione dell interfacce comprendente Xenix e BSD, la System V release +4. -La interfaccia di questa ultima release è descritta in un documento dal titolo +L'interfaccia di questa ultima release è descritta in un documento dal titolo \textit{System V Interface Description}, o SVID; spesso però si riferimento a questo standard con il nome della sua implementazione, usando la sigla SVr4. @@ -436,8 +436,8 @@ nessuna funzione non riconosciuta dalle specifiche standard ISO per il C. Per attivare le varie opzioni è possibile definire le macro di preprocessore, che controllano le funzionalità che le \acr{glibc} possono mettere a -disposizione questo può essere fatto attraverso l'opzione \cmd{-D} del -compilatore, ma è buona norma inserire gli opportuni \texttt{\#define} nei +disposizione: questo può essere fatto attraverso l'opzione \cmd{-D} del +compilatore, ma è buona norma inserire gli opportuni \code{\#define} nei propri header file. Le macro disponibili per i vari standard sono le seguenti: @@ -480,21 +480,21 @@ Le macro disponibili per i vari standard sono le seguenti: disponibili in BSD e SVID. Se il valore della macro è posto a 500 questo include anche le nuove definizioni introdotte con la \textit{Single Unix Specification, version 2}, cioè Unix98. -\item[\macro{\_XOPEN\_SOURCE\_EXTENDED}] questa macro si attivano le ulteriori - funzionalità necessarie a esse conformi al rilascio del marchio - \textit{X/Open Unix} -\item[\macro{\_ISOC99\_SOURCE}] questa macro si attivano le +\item[\macro{\_XOPEN\_SOURCE\_EXTENDED}] definendo questa macro si attivano le + ulteriori funzionalità necessarie ad essere conformi al rilascio del marchio + \textit{X/Open Unix}. +\item[\macro{\_ISOC99\_SOURCE}] definendo questa macro si attivano le funzionalità previste per la revisione delle librerie standard del C denominato ISO C99. Dato che lo standard non è ancora adottato in maniera - ampia queste non sona abilitate automaticamente, ma le \acr{glibc} ha già - una implementazione completa che può essere attivata definendo questa macro. -\item[\macro{\_LARGEFILE\_SOURCE}] questa macro si attivano le + ampia queste non sono abilitate automaticamente, ma le \acr{glibc} hanno già + un'implementazione completa che può essere attivata definendo questa macro. +\item[\macro{\_LARGEFILE\_SOURCE}] definendo questa macro si attivano le funzionalità per il supporto dei file di grandi dimensioni (il \textit{Large - File Support} o LFS) con indici e dimensioni a 64 bit. -\item[\macro{\_GNU\_SOURCE}] questa macro si attivano tutte le funzionalità - disponibili: ISO C89, ISO C99, POSIX.1, POSIX.2, BSD, SVID, X/Open, LFS più - le estensioni specifiche GNU. Nel caso in cui BSD e POSIX confliggono viene - data la precedenza a POSIX. + File Support} o LFS) con indici e dimensioni a 64 bit. +\item[\macro{\_GNU\_SOURCE}] definendo questa macro si attivano tutte le + funzionalità disponibili: ISO C89, ISO C99, POSIX.1, POSIX.2, BSD, SVID, + X/Open, LFS più le estensioni specifiche GNU. Nel caso in cui BSD e POSIX + confliggano viene data la precedenza a POSIX. \end{basedescript} In particolare è da sottolineare che le \acr{glibc} supportano alcune diff --git a/pref.tex b/pref.tex index 60410b1..31adf17 100644 --- a/pref.tex +++ b/pref.tex @@ -9,10 +9,10 @@ possibilit senza la presenza di un valido manuale che sia altrettanto liberamente disponibile. -E, come per il software libero, è anche in questo caso è di fondamentale -importanza la libertà di accedere ai sorgenti (e non solo al risultato -finale, sia questo una stampa o un file formattato) e la libertà di -modificarli per apportarvi migliorie, aggiornamenti, etc. +E, come per il software libero, anche in questo caso è di fondamentale +importanza la libertà di accedere ai sorgenti (e non solo al risultato finale, +sia questo una stampa o un file formattato) e la libertà di modificarli per +apportarvi migliorie, aggiornamenti, etc. Per questo la Free Software Foundation ha approntato una licenza apposita per la documentazione, che tiene conto delle differenze che restano fra un testo e @@ -64,7 +64,7 @@ linguaggio, e di quanto necessario per scrivere, compilare ed eseguire un programma. Infine, dato che lo scopo del progetto è la produzione di un libro, si è -scelto di usare LaTex come "ambiente di sviluppo" del medesimo, sia per +scelto di usare \LaTeX\ come "ambiente di sviluppo" del medesimo, sia per l'impareggiabile qualità tipografica ottenibile, che per la congruenza dello strumento, tanto sul piano pratico, quanto su quello filosofico. diff --git a/process.tex b/process.tex index a574eb3..935f34e 100644 --- a/process.tex +++ b/process.tex @@ -1,26 +1,26 @@ \chapter{L'interfaccia base con i processi} \label{cha:process_interface} -Come accennato nell'introduzione il processo è l'unità di base con cui un -sistema unix-like alloca ed utilizza le risorse. Questo capitolo tratterà -l'interfaccia base fra il sistema e i processi, come vengono passati i -parametri, come viene gestita e allocata la memoria, come un processo può +Come accennato nell'introduzione il \textsl{processo} è l'unità di base con +cui un sistema unix-like alloca ed utilizza le risorse. Questo capitolo +tratterà l'interfaccia base fra il sistema e i processi, come vengono passati +i parametri, come viene gestita e allocata la memoria, come un processo può richiedere servizi al sistema e cosa deve fare quando ha finito la sua esecuzione. Nella sezione finale accenneremo ad alcune problematiche generiche di programmazione. In genere un programma viene eseguito quando un processo lo fa partire -eseguendo una funzione della famiglia \func{exec}; torneremo su questo e -sulla creazione e gestione dei processi nel prossimo capitolo. In questo +eseguendo una funzione della famiglia \func{exec}; torneremo su questo e sulla +creazione e gestione dei processi nel prossimo capitolo. In questo affronteremo l'avvio e il funzionamento di un singolo processo partendo dal -punto di vista del programma che viene messo in esecuzione. +punto di vista del programma che viene messo in esecuzione. \section{Esecuzione e conclusione di un programma} Uno dei concetti base di Unix è che un processo esegue sempre uno ed un solo programma: si possono avere più processi che eseguono lo stesso programma ma -ciascun processo vedrà la sua copia del codice (in realtà il kernel fa si che +ciascun processo vedrà la sua copia del codice (in realtà il kernel fa sì che tutte le parti uguali siano condivise), avrà un suo spazio di indirizzi, variabili proprie e sarà eseguito in maniera completamente indipendente da tutti gli altri\footnote{questo non è del tutto vero nel caso di un programma @@ -31,7 +31,7 @@ tutti gli altri\footnote{questo non \subsection{La funzione \func{main}} \label{sec:proc_main} -Quando un programma viene lanciato il kernel esegue una opportuna routine di +Quando un programma viene lanciato il kernel esegue un'opportuna routine di avvio, usando il programma \cmd{ld-linux.so}. Questo programma prima carica le librerie condivise che servono al programma, poi effettua il link dinamico del codice e alla fine lo esegue. Infatti, a meno di non aver specificato il @@ -43,7 +43,7 @@ page di \cmd{ld.so}. Il sistema fa partire qualunque programma chiamando la funzione \func{main}; sta al programmatore chiamare così la funzione principale del programma da cui -si suppone iniziale l'esecuzione; in ogni caso senza questa funzione lo stesso +si suppone iniziare l'esecuzione; in ogni caso senza questa funzione lo stesso linker darebbe luogo ad errori. Lo standard ISO C specifica che la funzione \func{main} può non avere @@ -92,7 +92,7 @@ della funzione \func{main} senza ritornare esplicitamente si ha un valore di uscita indefinito, è pertanto consigliabile di concludere sempre in maniera esplicita detta funzione. -Una altra convenzione riserva i valori da 128 a 256 per usi speciali: ad +Un'altra convenzione riserva i valori da 128 a 256 per usi speciali: ad esempio 128 viene usato per indicare l'incapacità di eseguire un altro programma in un sottoprocesso. Benché questa convenzione non sia universalmente seguita è una buona idea tenerne conto. @@ -114,9 +114,9 @@ valori di tipo \type{int} 0 e 1. \subsection{Le funzioni \func{exit} e \func{\_exit}} \label{sec:proc_exit} -Come accennato le funzioni usate per effettuare una uscita ``normale'' da un +Come accennato le funzioni usate per effettuare un'uscita ``normale'' da un programma sono due, la prima è la funzione \func{exit} che è definita dallo -standard ANSI C; ed il cui prototipo è: +standard ANSI C ed il cui prototipo è: \begin{prototype}{stdlib.h}{void exit(int status)} Causa la conclusione ordinaria del programma restituendo il valore \var{status} al processo padre. @@ -145,7 +145,7 @@ non vengono salvati e le eventuali funzioni registrate con \func{atexit} e La funzione chiude tutti i file descriptor appartenenti al processo (si tenga presente che questo non comporta il salvataggio dei dati bufferizzati degli -stream), fa si che ogni figlio del processo sia ereditato da \cmd{init} (vedi +stream), fa sì che ogni figlio del processo sia ereditato da \cmd{init} (vedi \secref{cha:process_handling}), manda un segnale \macro{SIGCHLD} al processo padre (vedi \secref{sec:sig_job_control}) ed infine ritorna lo stato di uscita specificato in \param{status} che può essere raccolto usando la funzione @@ -155,11 +155,11 @@ specificato in \param{status} che pu \subsection{Le funzioni \func{atexit} e \func{on\_exit}} \label{sec:proc_atexit} -Una esigenza comune che si incontra nella programmazione è quella di dover +Un'esigenza comune che si incontra nella programmazione è quella di dover effettuare una serie di operazioni di pulizia (ad esempio salvare dei dati, ripristinare dei settaggi, eliminare dei file temporanei, ecc.) prima della -conclusione di un programma. In genere queste operazioni vengono fatte in una -apposita sezione del programma, ma quando si realizza una libreria diventa +conclusione di un programma. In genere queste operazioni vengono fatte in +un'apposita sezione del programma, ma quando si realizza una libreria diventa antipatico dover richiedere una chiamata esplicita ad una funzione di pulizia al programmatore che la utilizza. @@ -182,7 +182,7 @@ funzione di pulizia da chiamare all'uscita, che non deve prendere argomenti e non deve ritornare niente (deve essere essere cioè definita come \code{void function(void)}). -Una estensione di \func{atexit} è la funzione \func{on\_exit}, che le +Un'estensione di \func{atexit} è la funzione \func{on\_exit}, che le \acr{glibc} includono per compatibilità con SunOS, ma che non è detto sia definita su altri sistemi; il suo prototipo è: \begin{prototype}{stdlib.h} @@ -253,7 +253,7 @@ esecuzione, e le varie funzioni utilizzabili per la sua gestione. Ci sono vari modi in cui i vari sistemi organizzano la memoria (ed i dettagli di basso livello dipendono spesso in maniera diretta dall'architettura dell'hardware), ma quello più tipico, usato dai sistemi unix-like come Linux è -la cosiddetta \textsl{memoria virtuale}m che consiste nell'assegnare ad ogni +la cosiddetta \textsl{memoria virtuale} che consiste nell'assegnare ad ogni processo uno spazio virtuale di indirizzamento lineare, in cui gli indirizzi vanno da zero ad un qualche valore massimo\footnote{nel caso di Linux fino al kernel 2.2 detto massimo era, per macchine a 32bit, di 2Gb, con il kernel @@ -278,12 +278,12 @@ diverse pagine di memoria virtuale appartenenti a processi diversi (come accade in genere per le pagine che contengono il codice delle librerie condivise). Ad esempio il codice della funzione \func{printf} starà su una sola pagina di memoria reale che farà da supporto a tutte le pagine di memoria -virtuale di tutti i processi hanno detta funzione nel loro codice. +virtuale di tutti i processi che hanno detta funzione nel loro codice. La corrispondenza fra le pagine della memoria virtuale e quelle della memoria fisica della macchina viene gestita in maniera trasparente dall'hardware di gestione della memoria (la \textit{Memory Management Unit} del processore). -Poiché in genere quest'ultima è solo una piccola frazione della memoria +Poiché in genere la memoria fisica è solo una piccola frazione della memoria virtuale, è necessario un meccanismo che permetta di trasferire le pagine che servono dal supporto su cui si trovano in memoria, eliminando quelle che non servono. Questo meccanismo è detto \textit{paging}, ed è uno dei compiti @@ -291,7 +291,7 @@ principali del kernel. Quando un processo cerca di accedere ad una pagina che non è nella memoria reale, avviene quello che viene chiamato un \textit{page fault}; l'hardware di -gestione della memoria genera una interruzione e passa il controllo al kernel +gestione della memoria genera un'interruzione e passa il controllo al kernel il quale sospende il processo e si incarica di mettere in RAM la pagina richiesta (effettuando tutte le operazioni necessarie per reperire lo spazio necessario), per poi restituire il controllo al processo. @@ -317,8 +317,8 @@ una parte di essi tentativo di accedere ad un indirizzo non allocato è un tipico errore che si commette quando si è manipolato male un puntatore e genera quello che viene chiamato un \textit{segmentation fault}. Se si tenta cioè di leggere o -scrivere da un indirizzo per il quale non esiste una associazione della pagina -virtuale il kernel risponde al relativo \textit{page fault}, mandando un +scrivere da un indirizzo per il quale non esiste un'associazione della pagina +virtuale, il kernel risponde al relativo \textit{page fault} mandando un segnale \macro{SIGSEGV} al processo, che normalmente ne causa la terminazione immediata. @@ -354,15 +354,16 @@ programma C viene suddiviso nei seguenti segmenti: specificati. La seconda parte è il segmento dei dati non inizializzati, che contiene le - variabili il cui valore è stato non è assegnato esplicitamente. Ad esempio - se si definisce: + variabili il cui valore non è stato assegnato esplicitamente. Ad esempio se + si definisce: \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} int vect[100]; \end{lstlisting} - questo valore sarà immagazzinato in questo segmento. Anch'esso viene - allocato all'avvio, e tutte le variabili vengono inizializzate a - zero (ed i puntatori a \macro{NULL}). - + questo vettore sarà immagazzinato in questo segmento. Anch'esso viene + allocato all'avvio, e tutte le variabili vengono inizializzate a zero (ed i + puntatori a \macro{NULL}).\footnote{si ricordi che questo vale solo per le + variabili che vanno nel segmento dati, e non è affatto vero in generale.} + Storicamente questo segmento viene chiamato BBS (da \textit{block started by symbol}). La sua dimensione è fissa. @@ -476,8 +477,8 @@ multipli di 8 byte. In genere su usano le funzioni \func{malloc} e \func{calloc} per allocare dinamicamente la memoria necessaria al programma, e siccome i puntatori ritornati sono di tipo generico non è necessario effettuare un cast per -assegnarli a puntatori al tipo di variabile per la quale si effettua la -allocazione. +assegnarli a puntatori al tipo di variabile per la quale si effettua +l'allocazione. La memoria allocata dinamicamente deve essere esplicitamente rilasciata usando \func{free}\footnote{le glibc provvedono anche una funzione \func{cfree} @@ -504,7 +505,7 @@ un blocco della dimensione voluta, copiandoci automaticamente il contenuto; lo spazio aggiunto non viene inizializzato. Si deve sempre avere ben presente il fatto che il blocco di memoria restituito -da \func{realloc} può non essere una estensione di quello che gli si è passato +da \func{realloc} può non essere un'estensione di quello che gli si è passato in ingresso; per questo si dovrà \emph{sempre} eseguire la riassegnazione di \var{ptr} al valore di ritorno della funzione, e reinizializzare o provvedere ad un adeguato aggiornamento di tutti gli altri puntatori all'interno del @@ -517,13 +518,13 @@ assegnare sempre a \macro{NULL} ogni puntatore liberato con \func{free}, dato che, quando il parametro è un puntatore nullo, \func{free} non esegue nessuna operazione. -Le \acr{glibc} hanno una implementazione delle routine di allocazione che è +Le \acr{glibc} hanno un'implementazione delle routine di allocazione che è controllabile dall'utente attraverso alcune variabili di ambiente, in particolare diventa possibile tracciare questo tipo di errori usando la variabile \macro{MALLOC\_CHECK\_} che quando viene definita mette in uso una -versione meno efficiente delle funzioni, che però è più tollerante nei -confronti di piccoli errori come quello di chiamate doppie a \func{free}; in -particolare: +versione meno efficiente delle funzioni suddette, che però è più tollerante +nei confronti di piccoli errori come quello di chiamate doppie a \func{free}. +In particolare: \begin{itemize*} \item se la variabile è posta a zero gli errori vengono ignorati. \item se è posta ad 1 viene stampato un avviso sullo \textit{standard error} @@ -539,26 +540,27 @@ non pi Un caso tipico che illustra il problema è quello in cui l'allocazione di una variabile viene fatta da una subroutine per un uso locale, ma la memoria non -viene liberata; la funzione esce e la memoria resta allocata. Chiamate -ripetute alla stessa subroutine continueranno ad allocarne ancora, causando a -lungo andare un esaurimento della memoria disponibile e l'impossibilità di -proseguire il programma. Il problema è che l'esaurimento che può avvenire in -qualunque momento, e senza nessuna relazione con la subroutine che contiene -l'errore, per questo motivo è sempre complesso trovare un \textit{memory - leak}. +viene liberata; la funzione esce e la memoria resta allocata (fino alla +terminazione del processo). Chiamate ripetute alla stessa subroutine +continueranno ad allocarne ancora, causando a lungo andare un esaurimento +della memoria disponibile e l'impossibilità di proseguire il programma. Il +problema è che l'esaurimento che può avvenire in qualunque momento, e senza +nessuna relazione con la subroutine che contiene l'errore, per questo motivo è +sempre complesso trovare un \textit{memory leak}. Per ovviare a questi problemi l'implementazione delle routine di allocazione delle \acr{glibc} mette a disposizione una serie di funzionalità (su cui torneremo in \secref{sec:xxx_advanced}) che permettono di tracciare le allocazioni e le disallocazione, e definisce anche una serie di possibili -\textsl{ganci} che permettono di sostituire alle funzioni di libreria una -propria versione (che può essere più o meno specializzata per il debugging). +\textit{hook} (\textsl{ganci}) che permettono di sostituire alle funzioni di +libreria una propria versione (che può essere più o meno specializzata per il +debugging). \subsection{La funzione \func{alloca}} \label{sec:proc_mem_alloca} -Una alternativa possibile all'uso di \func{malloc}, che non soffre dei di +Una possibile alternativa all'uso di \func{malloc}, che non soffre dei problemi di memory leak descritti in precedenza, è la funzione \func{alloca}, che invece di allocare la memoria nello heap usa il segmento di stack della funzione corrente. La sintassi è identica a quella di \func{malloc}, il suo @@ -585,7 +587,7 @@ Un altro vantaggio \func{malloc} e non viene sprecato spazio, infatti non è necessario gestire un pool di memoria da riservare e si evitano così anche i problemi di frammentazione di quest'ultimo, che comportano inefficienze sia -nella allocazione della memoria che nella esecuzione della allocazione. +nell'allocazione della memoria che nell'esecuzione dell'allocazione. Gli svantaggi sono che questa funzione non è disponibile su tutti gli Unix, e non è inserita né nello standard POSIX né in SUSv3 (ma è presente in BSD), il @@ -660,17 +662,18 @@ vuole che questo meccanismo si attivi. In generale i motivi per cui si possono avere di queste necessità sono due: \begin{itemize} \item \textsl{La velocità}. Il processo della paginazione è trasparente solo - se il programma in esecuzione se non è sensibile al tempo che occorre a - riportare la pagina in memoria; per questo motivi processi critici che hanno - esigenze di tempo reale o tolleranze critiche nella risposte (ad esempio + se il programma in esecuzione non è sensibile al tempo che occorre a + riportare la pagina in memoria; per questo motivo processi critici che hanno + esigenze di tempo reale o tolleranze critiche nelle risposte (ad esempio processi che trattano campionamenti sonori) possono non essere in grado di sopportare le variazioni della velocità di accesso dovuta alla paginazione. - + In certi casi poi un programmatore può conoscere meglio dell'algoritmo di allocazione delle pagine le esigenze specifiche del suo programma e decidere quali pagine di memoria è opportuno che restino in memoria per un aumento delle prestazioni. In genere queste sono esigenze particolari e richiedono - anche un aumento delle priorità in esecuzione (vedi \secref{sec:xxx_xxx}). + anche un aumento delle priorità in esecuzione del processo (vedi + \secref{sec:proc_real_time}). \item \textsl{La sicurezza}. Se si hanno password o chiavi segrete in chiaro in memoria queste possono essere portate su disco dal meccanismo della @@ -697,14 +700,15 @@ memoria bloccata non la sblocca. Chiaramente la terminazione del processo comporta anche la fine dell'uso della sua memoria virtuale, e quindi anche di tutti i suoi \textit{memory lock}. -I \textit{memory lock} non sono ereditati dai processi figli\footnote{ma - siccome Linux usa il copy on write gli indirizzi virtuali del figlio sono - mantenuti sullo stesso segmento di RAM del padre, quindi fintanto che un - figlio non scrive su un segmento, può usufruire dei memory lock del padre}. -Siccome la presenza di un \textit{memory lock} riduce la memoria disponibile -al sistema, con un impatto su tutti gli altri processi, solo l'amministratore -ha la capacità di bloccare una pagina. Ogni processo può però sbloccare le sue -pagine. +I \textit{memory lock} non sono ereditati dai processi figli.\footnote{ma + siccome Linux usa il \textit{copy on write} (vedi \secref{sec:proc_fork}) + gli indirizzi virtuali del figlio sono mantenuti sullo stesso segmento di + RAM del padre, quindi fintanto che un figlio non scrive su un segmento, può + usufruire del memory lock del padre.} Siccome la presenza di un +\textit{memory lock} riduce la memoria disponibile al sistema, con un impatto +su tutti gli altri processi, solo l'amministratore ha la capacità di bloccare +una pagina. Ogni processo può però sbloccare le pagine relative alla propria +memoria. Il sistema pone dei limiti all'ammontare di memoria di un processo che può essere bloccata e al totale di memoria fisica che può dedicare a questo, lo @@ -776,10 +780,16 @@ esempio limitandosi a tutte le pagine allocate a partire da un certo momento. In ogni caso un processo real-time che deve entrare in una sezione critica deve provvedere a riservare memoria sufficiente prima dell'ingresso, per -scongiurare in partenza un eventuale page fault causato dal meccanismo di copy -on write. In genere questo si fa chiamando una funzione che ha allocato una -quantità sufficiente ampia di variabili automatiche, in modo che esse vengano -mappate in RAM dallo stack, e poi ci scrive sopra. +scongiurare in partenza un eventuale page fault causato dal meccanismo di +\textit{copy on write}. Infatti se nella sezione critica si va ad utilizzare +memoria che non è ancora stata riportata in RAM si potrebbe avere un page +fault durante l'esecuzione della stessa, con conseguente rallentamento +(probabilmente inaccettabile) dei tempi di esecuzione. + +In genere si ovvia a questa problematica chiamando una funzione che ha +allocato una quantità sufficientemente ampia di variabili automatiche, in modo +che esse vengano mappate in RAM dallo stack, dopo di che, per essere sicuri +che esse siano state effettivamente portate in memoria, ci si scrive sopra. @@ -856,7 +866,7 @@ riconoscendo le possibili opzioni segnalate con \var{optstring}. Questa funzione prende come argomenti le due variabili \var{argc} e \var{argv} passate a \func{main} ed una stringa che indica quali sono le opzioni valide; la funzione effettua la scansione della lista degli argomenti ricercando ogni -stringa che comincia con \cmd{-} e ritorna ogni volta che trova una opzione +stringa che comincia con \cmd{-} e ritorna ogni volta che trova un'opzione valida. La stringa \var{optstring} indica quali sono le opzioni riconosciute ed è @@ -931,13 +941,13 @@ comando. Anzitutto si può notare che si è anzitutto (\texttt{\small 1}) disabilitata la stampa di messaggi di errore per opzioni non riconosciute, per poi passare al ciclo per la verifica delle opzioni (\texttt{\small 2-27}); per ciascuna delle -opzioni possibili si è poi provveduto ad una opportuna azione, ad esempio per +opzioni possibili si è poi provveduto ad un'azione opportuna, ad esempio per le tre opzioni che prevedono un parametro si è effettuata la decodifica del medesimo (il cui indirizzo è contenuto nella variabile \var{optarg}) avvalorando la relativa variabile (\texttt{\small 12-14}, \texttt{\small 15-17} e \texttt{\small 18-20}). Completato il ciclo troveremo in -\var{optind} l'indice in \var{argv[]} del primo degli argomenti a linea di -comando restanti. +\var{optind} l'indice in \var{argv[]} del primo degli argomenti rimanenti +nella linea di comando. Normalmente \func{getopt} compie una permutazione degli elementi di \var{argv} così che alla fine della scansione gli elementi che non sono opzioni sono @@ -1042,7 +1052,7 @@ anche altre: per una lista pi \macro{BROWSER} & $\bullet$ & $\bullet$ & $\bullet$ & Browser di default\\ \hline \end{tabular} - \caption{Variabile di ambiente più comuni definite da vari standard} + \caption{Variabili di ambiente più comuni definite da vari standard.} \label{tab:proc_env_var} \end{table} @@ -1135,7 +1145,7 @@ invece esiste il suo valore sar seguendo il comportamento di BSD4.4; dato che questo può dar luogo a perdite di memoria e non rispetta lo standard. Il comportamento è stato modificato a partire dalle 2.1.2, eliminando anche, sempre in conformità a SUSv2, - l'attributo \type{const} dal prototipo.} \func{string} alla lista delle + l'attributo \type{const} dal prototipo.} \param{string} alla lista delle variabili di ambiente; pertanto ogni cambiamento alla stringa in questione si riflette automaticamente sull'ambiente, e quindi si deve evitare di passare a questa funzione una variabile automatica (per evitare i problemi esposti in @@ -1143,13 +1153,16 @@ questa funzione una variabile automatica (per evitare i problemi esposti in Si tenga infine presente che se si passa a \func{putenv} solo il nome di una variabile (cioè \param{string} è nella forma \texttt{NAME} e non contiene un -\texttt{=}) allora questa viene cancellata dall'ambiente. Infine se la chiamata -di \func{putenv} comporta la necessità di allocare una nuova versione del -vettore \var{environ} questo sarà allocato, ma la versione corrente sarà -deallocata solo se anch'essa è risultante da una allocazione fatta in -precedenza da un'altra \func{putenv}, il vettore originale (in genere piazzato -al di sopra dello stack, vedi \figref{fig:proc_mem_layout}), o la memoria -associata alle variabili di ambiente eliminate non viene comunque liberata. +\texttt{=}) allora questa viene cancellata dall'ambiente. Infine se la +chiamata di \func{putenv} comporta la necessità di allocare una nuova versione +del vettore \var{environ} questo sarà allocato, ma la versione corrente sarà +deallocata solo se anch'essa è risultante da un'allocazione fatta in +precedenza da un'altra \func{putenv}. Questo perché il vettore delle variabili +di ambiente iniziale, creato dalla chiamata ad \func{exec} (vedi +\secref{sec:proc_exec}) è piazzato al di sopra dello stack, (vedi +\figref{fig:proc_mem_layout}) e non nello heap e non può essere deallocato. +Inoltre la memoria associata alle variabili di ambiente eliminate non viene +liberata. \section{Problematiche di programmazione generica} @@ -1246,7 +1259,7 @@ inoltre che l'ultimo degli argomenti fissi sia di tipo automaticamente a \type{double} ed i \type{char} e gli \type{short} ad \type{int}. Un tipo \textit{self-promoting} è un tipo che verrebbe promosso a se stesso.} il che esclude array, puntatori a funzioni e interi di tipo -\type{char} o \type{short} (con segno o meno). Una ulteriore restrizione di +\type{char} o \type{short} (con segno o meno). Un'ulteriore restrizione di alcuni compilatori è di non dichiarare l'ultimo parametro fisso come \type{register}. @@ -1321,7 +1334,7 @@ motivo \macro{va\_list} direttamente ad un altra variabile dello stesso tipo. Per risolvere questo problema lo standard ISO C99\footnote{alcuni sistemi che non hanno questa macro provvedono al suo posto \macro{\_\_va\_copy} che era il nome proposto - in una bozza dello standard} ha previsto una ulteriore macro che permette di + in una bozza dello standard} ha previsto un'ulteriore macro che permette di eseguire la copia di un puntatore alla lista degli argomenti: \begin{prototype}{stdarg.h}{void va\_copy(va\_list dest, va\_list src)} Copia l'attuale valore \param{src} del puntatore alla lista degli argomenti diff --git a/prochand.tex b/prochand.tex index c7f441d..dcad876 100644 --- a/prochand.tex +++ b/prochand.tex @@ -290,12 +290,19 @@ 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 della creazione di - un nuovo processo.} pertanto padre e figlio vedono variabili diverse. +padre. Si tenga presente però che la memoria è copiata, non condivisa, pertanto +padre e figlio vedono variabili diverse. + +Per quanto riguarda la gestione della memoria in generale il segmento di +testo, che è identico, è condiviso e tenuto in read-only per il padre e per i +figli. Per gli altri segmenti Linux utilizza la tecnica del \textit{copy on + write}\index{copy on write}; questa tecnica comporta che una pagina di +memoria viene effettivamente copiata per il nuovo processo solo quando ci +viene effettuata sopra una scrittura (e si ha quindi una reale differenza fra +padre e figlio). In questo modo si rende molto più efficiente il meccanismo +della creazione di un nuovo processo, non essendo più necessaria la copia di +tutto lo spazio degli indirizzi virtuali del padre, ma solo delle pagine di +memoria che sono state modificate, e solo al momento della modifica stessa. La differenza che si ha nei due processi è che nel processo padre il valore di ritorno della funzione \func{fork} è il \acr{pid} del processo figlio, mentre @@ -1875,6 +1882,7 @@ Questa viene in genere indicata con un numero + \subsection{Il meccanismo di \textit{scheduling} standard} \label{sec:proc_sched_stand} @@ -1883,6 +1891,10 @@ possibile specificare una priorit (argomento che tratteremo più avanti) l'uso comune segue quello che è il meccanismo tradizionale con cui i sistemi +\subsection{Il meccanismo di \textit{scheduling real-time}} +\label{sec:proc_real_time} + +Per settare le -- 2.30.2 From d4c16890a0197a513327a43fbed7d95c48bee8cb Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Thu, 28 Feb 2002 23:18:59 +0000 Subject: [PATCH 10/16] Tre righe tanto per fare scena --- signal.tex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/signal.tex b/signal.tex index cb163ba..aafb4ec 100644 --- a/signal.tex +++ b/signal.tex @@ -834,7 +834,12 @@ dei casi in cui si presenta questa situazione In questo caso si pone il problema di cosa fare una volta che il manipolatore sia ritornato. La scelta originaria dei primi Unix era quella di far ritornare -la system call con un errore di \macro{EINTR}, +anche la system call restituendo l'errore di \macro{EINTR}. Questa è a +tutt'oggi una scelta corrente, ma comporta che i programmi che usano dei +manipolatori controllino lo stato di uscita delle funzioni per ripeterne la +chiamata qualora l'errore fosse questo. + +Dato che dimenticarsi di richiamare una funzione interrotta è un errore comune -- 2.30.2 From 9aeb46d93d970f26f1939d3853e4a9e62b2eb5b9 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 1 Mar 2002 18:47:21 +0000 Subject: [PATCH 11/16] Integrate una altra serie di correzioni e riscritte parti coi suggerimenti di Alessio e Daniele. Messa pure una sezione ringraziamenti, e aggiornato il ChangeLog, per dare il relativo credito ai contributors ... --- ChangeLog | 14 +++ fileintro.tex | 38 ++++---- gapil.tex | 1 + ipprot.tex | 11 ++- process.tex | 47 ++++++---- prochand.tex | 227 +++++++++++++++++++++++---------------------- ringraziamenti.tex | 18 ++++ 7 files changed, 202 insertions(+), 154 deletions(-) create mode 100644 ringraziamenti.tex diff --git a/ChangeLog b/ChangeLog index f1a6a7b..8228743 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2002-03-01 Simone Piccardi + + * prochand.tex: correzioni varie da D. Masini. + + * process.tex: Spiegazione delle calling convention per le + pulitura dello stack da parte delle funzioni, contributo di + D. Masini, chiarimenti sui memory leak secondo quanto suggerito da + A. Frusciante. + +2002-02-28 Simone Piccardi + + * prochand.tex: un altro blocco di correzioni suggerite da Daniele + Masini. + 2002-02-27 Simone Piccardi * pref.tex, intro.tex, process.tex, prochand.tex: Correzioni diff --git a/fileintro.tex b/fileintro.tex index 40d8e25..15e2fd8 100644 --- a/fileintro.tex +++ b/fileintro.tex @@ -90,10 +90,11 @@ risoluzione del nome (\textit{file name resolution} o \textit{pathname resolution}). La risoluzione viene fatta esaminando il \textit{pathname} da sinistra a destra e localizzando ogni nome nella directory indicata dal nome precedente usando \file{/} come separatore\footnote{nel caso di nome vuoto, il - costrutto \file{//} viene considerato equivalente a \file{/}.}: ovviamente -perché il procedimento funzioni occorre che i nomi indicati come directory + costrutto \file{//} viene considerato equivalente a \file{/}.}: ovviamente, +perché il procedimento funzioni, occorre che i nomi indicati come directory esistano e siano effettivamente directory, inoltre i permessi (si veda -\secref{sec:file_access_control}) devono consentire l'accesso. +\secref{sec:file_access_control}) devono consentire l'accesso all'intero +\textit{pathname}. Se il \textit{pathname} comincia per \file{/} la ricerca parte dalla directory radice del processo; questa, a meno di un \func{chroot} (su cui torneremo in @@ -105,16 +106,16 @@ parte dalla directory corrente (su cui torneremo in relativo}\index{pathname relativo}. I nomi \file{.} e \file{..} hanno un significato speciale e vengono inseriti -in ogni directory, il primo fa riferimento alla directory corrente e il +in ogni directory: il primo fa riferimento alla directory corrente e il secondo alla directory \textsl{genitrice} (o \textit{parent directory}) cioè la directory che contiene il riferimento alla directory corrente; nel caso -questa sia la directory radice allora il riferimento è a se stessa. +questa sia la directory radice, allora il riferimento è a se stessa. \subsection{I tipi di file} \label{sec:file_file_types} -Come detto in precedenza in Unix esistono vari tipi di file, in Linux questi +Come detto in precedenza in Unix esistono vari tipi di file; in Linux questi sono implementati come oggetti del \textit{Virtual File System} (vedi \secref{sec:file_vfs_work}) e sono presenti in tutti i filesystem unix-like utilizzabili con Linux. L'elenco dei vari tipi di file definiti dal @@ -143,10 +144,10 @@ dati) in base al loro contenuto, o tipo di accesso. un file che identifica una periferica ad accesso sequenziale \\ \textit{block device} & \textsl{dispositivo a blocchi} & un file che identifica una periferica ad accesso diretto \\ - \textit{fifo} & \textsl{``tubo''} & + \textit{fifo} & \textsl{``coda''} & un file speciale che identifica una linea di comunicazione software (unidirezionale) \\ - \textit{socket} & \textsl{``spina''} & + \textit{socket} & \textsl{``presa''} & un file speciale che identifica una linea di comunicazione software (bidirezionale) \\ \hline @@ -160,27 +161,28 @@ VMS o Windows) un flusso continuo di byte. Non esiste cioè differenza per come vengono visti dal sistema file di diverso contenuto o formato (come nel caso di quella fra file di testo e binari che c'è in Windows) né c'è una strutturazione a record -per il cosiddetto ``accesso diretto'' come nel caso del VMS\footnote{con i +per il cosiddetto ``accesso diretto'' come nel caso del VMS.\footnote{con i kernel della serie 2.4 è disponibile una forma di accesso diretto ai dischi (il \textit{raw access}) attraverso dei device file appositi, che però non - ha nulla a che fare con questo}. + ha nulla a che fare con questo.} Una seconda differenza è nel formato dei file ASCII; in Unix la fine riga è -codificata in maniera diversa da Windows o Mac, in particolare il fine -riga è il carattere \texttt{LF} (o \verb|\n|) al posto del \texttt{CR} -(\verb|\r|) del Mac e del \texttt{CR LF} di Windows. Questo può causare alcuni +codificata in maniera diversa da Windows o Mac, in particolare il fine riga è +il carattere \texttt{LF} (o \verb|\n|) al posto del \texttt{CR} (\verb|\r|) +del Mac e del \texttt{CR LF} di Windows.\footnote{per questo esistono in Linux + dei programmi come \cmd{unix2dos} e \cmd{dos2unix} che effettuano una + conversione fra questi due formati di testo.} Questo può causare alcuni problemi qualora nei programmi si facciano assunzioni sul terminatore della riga. Si ricordi infine che in ambiente Unix non esistono tipizzazioni dei file di dati e che non c'è nessun supporto del sistema per le estensioni come parte -del filesystem. Ciò non ostante molti programmi adottano delle convenzioni per +del filesystem. Ciò nonostante molti programmi adottano delle convenzioni per i nomi dei file, ad esempio il codice C normalmente si mette in file con l'estensione \file{.c}, ma questa è, per quanto usata ed accettata in maniera universale, solo una convenzione. - \subsection{Le due interfacce ai file} \label{sec:file_io_api} @@ -203,21 +205,21 @@ rappresentati da numeri interi (cio L'interfaccia è definita nell'header \file{unistd.h}. La seconda interfaccia è quella che il manuale della \acr{glibc} chiama degli -\textit{stream}\index{stream}, essa provvede funzioni più evolute e un accesso +\textit{stream}\index{stream}. Essa provvede funzioni più evolute e un accesso bufferizzato (controllato dalla implementazione fatta dalle \acr{glibc}), la tratteremo in dettaglio nel \capref{cha:files_std_interface}. Questa è l'interfaccia standard specificata dall'ANSI C e perciò si trova anche su tutti i sistemi non Unix. Gli \textit{stream} sono oggetti complessi e sono rappresentati da puntatori ad un opportuna struttura definita dalle -librerie del C, si accede ad essi sempre in maniera indiretta utilizzando il +librerie del C; si accede ad essi sempre in maniera indiretta utilizzando il tipo \type{FILE *}. L'interfaccia è definita nell'header \type{stdio.h}. Entrambe le interfacce possono essere usate per l'accesso ai file come agli altri oggetti del VFS (pipe, socket, device, sui quali torneremo in dettaglio a tempo opportuno), ma per poter accedere alle operazioni di controllo su un qualunque tipo di oggetto del VFS occorre usare l'interfaccia standard di Unix -coi \textit{file descriptor}. Allo stesso modo devono essere usati i +con i \textit{file descriptor}. Allo stesso modo devono essere usati i \textit{file descriptor} se si vuole ricorrere a modalità speciali di I/O come il polling o il non-bloccante (vedi \capref{cha:file_advanced}). diff --git a/gapil.tex b/gapil.tex index 25211c9..36fc3c7 100644 --- a/gapil.tex +++ b/gapil.tex @@ -125,6 +125,7 @@ \include{ipprot} \include{tcpprot} \include{errors} +\include{ringraziamenti} \include{fdl} % at the end put the bibliography diff --git a/ipprot.tex b/ipprot.tex index 4618a30..11954fa 100644 --- a/ipprot.tex +++ b/ipprot.tex @@ -752,11 +752,12 @@ prima di avere un indirizzo globale. \end{table} Ci sono due tipi di indirizzi, \textit{link-local} e \textit{site-local}. Il -primo è usato per un singolo link; la struttura è mostrata in \curtab, questi -indirizzi iniziano sempre per \texttt{FE80} e vengono in genere usati per la -configurazione automatica dell'indirizzo al bootstrap e per la ricerca dei -vicini (vedi \ref{sec:IP_ipv6_autoconf}); un pacchetto che abbia tale -indirizzo come sorgente o destinazione non deve venire ritrasmesso dai router. +primo è usato per un singolo link; la struttura è mostrata in +\tabref{tab:IP_ipv6_linklocal}, questi indirizzi iniziano sempre per +\texttt{FE80} e vengono in genere usati per la configurazione automatica +dell'indirizzo al bootstrap e per la ricerca dei vicini (vedi +\ref{sec:IP_ipv6_autoconf}); un pacchetto che abbia tale indirizzo come +sorgente o destinazione non deve venire ritrasmesso dai router. Un indirizzo \textit{site-local} invece è usato per l'indirizzamento all'interno di un sito che non necessita di un prefisso globale; la struttura diff --git a/process.tex b/process.tex index 935f34e..483fbb9 100644 --- a/process.tex +++ b/process.tex @@ -380,7 +380,10 @@ programma C viene suddiviso nei seguenti segmenti: del chiamante (tipo il contenuto di alcuni registri della CPU). Poi la funzione chiamata alloca qui lo spazio per le sue variabili locali: in questo modo le funzioni possono essere chiamate ricorsivamente. Al ritorno - della funzione lo spazio è automaticamente rilasciato. + della funzione lo spazio è automaticamente rilasciato. Al ritorno della + funzione lo spazio è automaticamente ripulito. La pulizia in C e C++ viene + fatta dal chiamante.\footnote{a meno che non sia stato specificato + l'utilizzo di una calling convention diversa da quella standard.} La dimensione di questo segmento aumenta seguendo la crescita dello stack del programma, ma non viene ridotta quando quest'ultimo si restringe. @@ -538,15 +541,18 @@ routine di allocazione non più utilizzata, quello che in inglese viene chiamato \textit{memory-leak}, (cioè \textsl{perdita di memoria}). -Un caso tipico che illustra il problema è quello in cui l'allocazione di una -variabile viene fatta da una subroutine per un uso locale, ma la memoria non -viene liberata; la funzione esce e la memoria resta allocata (fino alla -terminazione del processo). Chiamate ripetute alla stessa subroutine -continueranno ad allocarne ancora, causando a lungo andare un esaurimento -della memoria disponibile e l'impossibilità di proseguire il programma. Il -problema è che l'esaurimento che può avvenire in qualunque momento, e senza -nessuna relazione con la subroutine che contiene l'errore, per questo motivo è -sempre complesso trovare un \textit{memory leak}. +Un caso tipico che illustra il problema è quello in cui in una subroutine si +alloca della memoria per uso locale senza liberarla prima di uscire. La +memoria resta così allocata fino alla terminazione del processo. Chiamate +ripetute alla stessa subroutine continueranno ad effettuare altre allocazioni, +causando a lungo andare un esaurimento della memoria disponibile (e la +probabile l'impossibilità di proseguire l'esecuzione programma). + +Il problema è che l'esaurimento della memoria può avvenire in qualunque +momento, in corrispondenza ad una qualunque chiamata di \func{malloc}, che può +essere in una sezione del codice che non ha alcuna relazione con la subroutine +che contiene l'errore. Per questo motivo è sempre molto difficile trovare un +\textit{memory leak}. Per ovviare a questi problemi l'implementazione delle routine di allocazione delle \acr{glibc} mette a disposizione una serie di funzionalità (su cui @@ -1173,7 +1179,7 @@ problematiche generali che possono emergere nella programmazione e di quali precauzioni o accorgimenti occorre prendere per risolverle. Queste problematiche non sono specifiche di sistemi unix-like o multitasking, ma avendo trattato in questo capitolo il comportamento dei processi visti come -entità a se stanti, le riportiamo qui. +entità a sé stanti, le riportiamo qui. \subsection{Il passaggio delle variabili e dei valori di ritorno} @@ -1258,8 +1264,8 @@ inoltre che l'ultimo degli argomenti fissi sia di tipo per compatibilità; ad esempio i tipi \type{float} vengono convertiti automaticamente a \type{double} ed i \type{char} e gli \type{short} ad \type{int}. Un tipo \textit{self-promoting} è un tipo che verrebbe promosso - a se stesso.} il che esclude array, puntatori a funzioni e interi di tipo -\type{char} o \type{short} (con segno o meno). Un'ulteriore restrizione di + a sé stesso.} il che esclude array, puntatori a funzioni e interi di tipo +\type{char} o \type{short} (con segno o meno). Una restrizione ulteriore di alcuni compilatori è di non dichiarare l'ultimo parametro fisso come \type{register}. @@ -1284,7 +1290,7 @@ in generale potrebbero essere stati effettivamente forniti, e nella esecuzione delle \macro{va\_arg} ci si può fermare in qualunque momento ed i restanti argomenti saranno ignorati; se invece si richiedono più argomenti di quelli forniti si -otterranno dei valori indefiniti. Nel caso del \cmd{gcc} poi l'uso della macro +otterranno dei valori indefiniti. Nel caso del \cmd{gcc} l'uso della macro \macro{va\_end} è inutile, ma si consiglia di usarlo ugualmente per compatibilità. @@ -1334,7 +1340,7 @@ motivo \macro{va\_list} direttamente ad un altra variabile dello stesso tipo. Per risolvere questo problema lo standard ISO C99\footnote{alcuni sistemi che non hanno questa macro provvedono al suo posto \macro{\_\_va\_copy} che era il nome proposto - in una bozza dello standard} ha previsto un'ulteriore macro che permette di + in una bozza dello standard} ha previsto una macro ulteriore che permette di eseguire la copia di un puntatore alla lista degli argomenti: \begin{prototype}{stdarg.h}{void va\_copy(va\_list dest, va\_list src)} Copia l'attuale valore \param{src} del puntatore alla lista degli argomenti @@ -1395,10 +1401,12 @@ dinamicamente con una delle funzioni della famiglia \func{malloc}. \label{sec:proc_longjmp} Il controllo del flusso di un programma in genere viene effettuato con le -varie istruzioni del linguaggio C, la più bistrattata delle quali è il -\code{goto}, ampiamente deprecato in favore di costrutti più puliti; esiste -però un caso in l'uso di questa istruzione porta all'implementazione più -efficiente, quello dell'uscita in caso di errore. +varie istruzioni del linguaggio C; fra queste la più bistrattata è il +\code{goto}, che viene deprecato in favore dei costrutti della programmazione +strutturata, che rendono il codice più leggibile e mantenibile . Esiste però +un caso in cui l'uso di questa istruzione porta all'implementazione più +efficiente e chiara anche dal punto di vista della struttura del programma, +quello dell'uscita in caso di errore. Il C però non consente di effettuare un salto ad una label definita in un'altra funzione, per cui se l'errore avviene in funzioni profondamente @@ -1406,7 +1414,6 @@ annidate occorre usare quello che viene chiamato un salto \textsl{non-locale}; questo viene fatto usando salvando il contesto dello stack nel punto in cui si vuole tornare in caso di errore, e ripristinandolo quando l'occorrenza capita. - La funzione che permette di salvare il contesto dello stack è \func{setjmp}, il cui prototipo è: diff --git a/prochand.tex b/prochand.tex index dcad876..38a9b2f 100644 --- a/prochand.tex +++ b/prochand.tex @@ -14,13 +14,12 @@ finale introdurremo alcune problematiche generiche della programmazione in ambiente multitasking. - \section{Introduzione} \label{sec:proc_gen} -Inizieremo con una 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 +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, dando una panoramica sull'uso delle principali funzioni di gestione. @@ -37,9 +36,9 @@ numero unico, il cosiddetto \textit{process identifier} o, pi \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 +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 voluto: questo è ad esempio +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. @@ -107,10 +106,10 @@ Dato che tutti i processi attivi nel sistema sono comunque generati da 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 +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 \secref{sec:file_organization}); in \curfig\ si è mostrato -il risultato del comando \cmd{pstree} che permette di mostrare questa +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. @@ -162,7 +161,7 @@ figlio sono affrontate in dettaglio in \secref{sec:proc_fork}). Se si vuole che il processo padre si fermi fino alla conclusione del processo figlio questo deve essere specificato subito dopo la \func{fork} chiamando la funzione \func{wait} o la funzione \func{waitpid} (si veda -\secref{sec:proc_wait}); queste funzioni restituiscono anche una informazione +\secref{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 @@ -182,8 +181,8 @@ coi processi che 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 +corrente; questo fa sì che l'immagine precedente venga completamente +cancellata. Questo significa che quando il nuovo programma esce, anche il processo termina, e non si può tornare alla precedente immagine. Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto @@ -207,17 +206,18 @@ programmi. \subsection{Gli identificatori dei processi} \label{sec:proc_pid} -Come accennato nell'introduzione ogni processo viene identificato dal sistema +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 \acr{glibc} il tipo usato è \type{int}). +intero con segno (nel caso di Linux e delle \acr{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\footnote{FIXME: verificare, non sono - sicuro}. Per questo motivo processo il processo di avvio (\cmd{init}) ha -sempre il \acr{pid} uguale a uno. + sicuro}. Per questo motivo, come visto in \secref{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 @@ -273,9 +273,9 @@ prototipo della funzione \funcdecl{pid\_t fork(void)} Crea un nuovo processo. - \bodydesc{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; - \var{errno} può assumere i valori: + \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 processo (per allocare la tabella delle pagine e le strutture del task) o @@ -286,12 +286,12 @@ prototipo della funzione \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 +il processo figlio continuano ad essere eseguiti normalmente all'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. Si tenga presente però che la memoria è copiata, non condivisa, pertanto -padre e figlio vedono variabili diverse. +padre. Si tenga presente però che la memoria è copiata, non condivisa, +pertanto padre e figlio vedono variabili diverse. Per quanto riguarda la gestione della memoria in generale il segmento di testo, che è identico, è condiviso e tenuto in read-only per il padre e per i @@ -392,7 +392,7 @@ operazione che viene chiamata \textit{spawn}. Nei sistemi unix-like scelto di mantenere questa separazione, dato che, come per la prima modalità d'uso, esistono numerosi scenari in cui si può usare una \func{fork} senza aver bisogno di eseguire una \func{exec}. Inoltre, anche nel caso della -seconda modalità di uso, avere le due funzioni separate permette al figlio di +seconda modalità d'uso, avere le due funzioni separate permette al figlio di cambiare gli attributi del processo (maschera dei segnali, redirezione dell'output, \textit{user id}) prima della \func{exec}, rendendo così relativamente facile intervenire sulle le modalità di esecuzione del nuovo @@ -401,11 +401,14 @@ programma. In \curfig\ si è riportato il corpo del codice del programma di esempio \cmd{forktest}, che ci permette di illustrare molte caratteristiche dell'uso della funzione \func{fork}. Il programma permette di creare un numero di figli -specificato a linea di comando, e prende anche alcune opzioni per indicare +specificato da linea di comando, e prende anche alcune opzioni per indicare degli eventuali tempi di attesa in secondi (eseguiti tramite la funzione \func{sleep}) per il padre ed il figlio (con \cmd{forktest -h} si ottiene la descrizione delle opzioni); il codice completo, compresa la parte che gestisce -le opzioni a riga di comando, è disponibile nel file \file{ForkTest.c}. +le opzioni a riga di comando, è disponibile nel file \file{ForkTest.c}, +distribuito insieme agli altri sorgenti degli esempi su +\href{http://firenze.linux.it/~piccardi/gapil_source.tgz} +{\texttt{http://firenze.linux.it/\~~\hspace{-2.0mm}piccardi/gapil\_source.tgz}}. Decifrato il numero di figli da creare, il ciclo principale del programma (\texttt{\small 24--40}) esegue in successione la creazione dei processi figli @@ -441,8 +444,8 @@ Go to next child \end{verbatim} %$ \normalsize -Esaminiamo questo risultato: una prima conclusione che si può trarre è non si -può dire quale processo fra il padre ed il figlio venga eseguito per +Esaminiamo questo risultato: una prima conclusione che si può trarre è che non +si può dire quale processo fra il padre ed il figlio venga eseguito per primo\footnote{a partire dal kernel 2.5.2-pre10 è stato introdotto il nuovo scheduler di Ingo Molnar che esegue sempre per primo il figlio; per mantenere la portabilità è opportuno non fare comunque affidamento su questo @@ -463,7 +466,7 @@ cui il processo padre ha eseguito pi figli venisse messo in esecuzione. Pertanto non si può fare nessuna assunzione sulla sequenza di esecuzione delle -istruzioni del codice fra padre e figli, nè sull'ordine in cui questi potranno +istruzioni del codice fra padre e figli, né sull'ordine in cui questi potranno essere messi in esecuzione. Se è necessaria una qualche forma di precedenza occorrerà provvedere ad espliciti meccanismi di sincronizzazione, pena il rischio di incorrere nelle cosiddette \textit{race condition} \index{race @@ -472,9 +475,9 @@ rischio di incorrere nelle cosiddette \textit{race condition} \index{race Si noti inoltre che essendo i segmenti di memoria utilizzati dai singoli processi completamente separati, le modifiche delle variabili nei processi figli (come l'incremento di \var{i} in \texttt{\small 31}) sono visibili solo -a loro, e non hanno alcun effetto sul valore che le stesse variabili hanno nel -processo padre (ed in eventuali altri processi figli che eseguano lo stesso -codice). +a loro (ogni processo vede solo la propria copia della memoria), e non hanno +alcun effetto sul valore che le stesse variabili hanno nel processo padre (ed +in eventuali altri processi figli che eseguano lo stesso codice). Un secondo aspetto molto importante nella creazione dei processi figli è quello dell'interazione dei vari processi con i file; per illustrarlo meglio @@ -687,7 +690,7 @@ eseguite alla chiusura di un processo 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 +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. @@ -715,7 +718,7 @@ terminato (si potrebbe avere cio 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 +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 possiamo parlare di un padre \textsl{adottivo}) @@ -932,7 +935,7 @@ per leggerne lo stato di chiusura (ed evitare la presenza di \textit{zombie}), per questo la modalità più usata per chiamare queste funzioni è quella di utilizzarle all'interno di un \textit{signal handler} (torneremo sui segnali e su come gestire \macro{SIGCHLD} in \secref{sec:sig_sigwait_xxx}). In questo -caso infatti, dato che il segnale è generato dalla terminazione un figlio, +caso infatti, dato che il segnale è generato dalla terminazione di un figlio, avremo la certezza che la chiamata a \func{wait} non si bloccherà. \begin{table}[!htb] @@ -986,12 +989,12 @@ anomala), uno per indicare se 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}). +\file{} ed elencate in \tabref{tab:proc_status_macro} (si tenga +presente che queste macro prendono come parametro la variabile di tipo +\type{int} puntata da \var{status}). Si tenga conto che nel caso di conclusione anomala il valore restituito da -\macro{WTERMSIG} può essere controllato contro le costanti definite in +\macro{WTERMSIG} può essere confrontato con le costanti definite in \file{signal.h} ed elencate in \tabref{tab:sig_signal_list}, e stampato usando le apposite funzioni trattate in \secref{sec:sig_strsignal}. @@ -999,7 +1002,7 @@ le apposite funzioni trattate in \secref{sec:sig_strsignal}. \subsection{Le funzioni \func{wait3} e \func{wait4}} \label{sec:proc_wait4} -Linux, seguendo una estensione di BSD, supporta altre due funzioni per la +Linux, seguendo un'estensione di BSD, supporta altre due funzioni per la lettura dello stato di terminazione di un processo \func{wait3} e \func{wait4}, analoghe alle precedenti ma che prevedono un ulteriore parametro attraverso il quale il kernel può restituire al padre informazioni @@ -1164,11 +1167,12 @@ 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}. +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 \macro{EACCESS}), la ricerca viene +proseguita nelle eventuali ulteriori directory indicate in \var{PATH}; solo se +non viene trovato nessun altro file viene finalmente restituito +\macro{EACCESS}. Le altre quattro funzioni si limitano invece a cercare di eseguire il file indicato dal parametro \var{path}, che viene interpretato come il @@ -1177,7 +1181,7 @@ indicato dal parametro \var{path}, che viene interpretato come il \begin{figure}[htb] \centering \includegraphics[width=13cm]{img/exec_rel} - \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}} + \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}.} \label{fig:proc_exec_relat} \end{figure} @@ -1213,12 +1217,11 @@ la lista completa \var{tms\_cutime}, \var{tms\_ustime} (vedi \secref{sec:xxx_xxx}). \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_gen_beha}). +Inoltre 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_gen_beha}). La gestione dei file aperti dipende dal valore che ha il flag di \textit{close-on-exec} (trattato in \secref{sec:file_fcntl}) per ciascun file @@ -1227,7 +1230,7 @@ restano aperti. Questo significa che il comportamento di default 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 +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 \secref{sec:file_dir_read}) che effettua da sola il settaggio del flag di \textit{close-on-exec} sulle directory che apre, in @@ -1356,13 +1359,13 @@ 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). +\textit{supplementary group id} dei gruppi dei quali 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}). Questi identificatori normalmente sono identici ai corrispondenti del gruppo -\textsl{reale} tranne nel caso in cui, come accennato in +\textit{real} tranne nel caso in cui, come accennato 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 @@ -1401,10 +1404,10 @@ 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 +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 + 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 @@ -1473,7 +1476,7 @@ 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 prendiamo quello con cui +Come esempio per chiarire l'uso di queste funzioni prendiamo 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ò essere lasciato aperto in scrittura a qualunque utente, che potrebbe @@ -1484,7 +1487,7 @@ 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 +Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato, la situazione degli identificatori è la seguente: \begin{eqnarray*} \label{eq:1} @@ -1493,7 +1496,7 @@ situazione degli identificatori \textit{saved group id} &=& \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 +programma può accedere a \file{/var/log/utmp} in scrittura ed aggiornarlo. A questo punto il programma può eseguire una \code{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à @@ -1509,9 +1512,9 @@ 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 \code{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: +\acr{utmp}, ottenuto ad esempio con una precedente \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: \begin{eqnarray*} \label{eq:3} \textit{real group id} &=& \textrm{\acr{gid} (invariato)} \\ @@ -1580,7 +1583,7 @@ Lo stesso problema di propagazione dei privilegi ad eventuali processi figli si porrebbe per i \textit{saved id}: queste funzioni derivano da un'implementazione che non ne prevede la presenza, e quindi non è possibile usarle per correggere la situazione come nel caso precedente. Per questo -motivo in Linux tutte le volte che vengono usata per modificare uno degli +motivo in Linux tutte le volte che vengono usate per modificare uno degli identificatori ad un valore diverso dal \textit{real id} precedente, il \textit{saved id} viene sempre settato al valore dell'\textit{effective id}. @@ -1616,7 +1619,7 @@ il settaggio di tutti gli identificatori. \subsection{Le funzioni \func{setresuid} e \func{setresgid}} \label{sec:proc_setresuid} -Queste due funzioni sono una estensione introdotta in Linux dal kernel 2.1.44, +Queste due funzioni sono un'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: \begin{functions} @@ -1663,7 +1666,7 @@ prototipi sono: variabili di ritorno non sono validi.} \end{functions} -Anche queste funzioni sono una estensione specifica di Linux, e non richiedono +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 \textit{value result argument}). Si noti che queste funzioni sono le uniche in grado di leggere i @@ -1715,11 +1718,10 @@ coincide con uno dei \textit{real}, \textit{effective} o \textit{saved id}. \subsection{Le funzioni \func{setgroups} e \func{getgroups}} \label{sec:proc_setgroups} -Le ultime funzioni che esamineremo sono quelle sono quelle che permettono di -operare sui gruppi supplementari. Ogni processo può avere fino a -\macro{NGROUPS\_MAX} gruppi supplementari in aggiunta al gruppo primario, -questi vengono ereditati dal processo padre e possono essere cambiati con -queste funzioni. +Le ultime funzioni che esamineremo sono quelle che permettono di operare sui +gruppi supplementari. Ogni processo può avere fino a \macro{NGROUPS\_MAX} +gruppi supplementari 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 è \func{getgroups}; questa funzione è definita nello standard POSIX ed il suo prototipo è: @@ -1760,8 +1762,8 @@ ottenere tutti i gruppi a cui appartiene un utente; il suo prototipo \noindent la funzione esegue una scansione del database dei gruppi (si veda \secref{sec:sys_user_group}) e ritorna 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 -e passando indietro il numero dei gruppi trovati. +perché qualora il valore specificato sia troppo piccolo la funzione ritorna +-1, passando indietro il numero dei gruppi trovati. Per settare i gruppi supplementari di un processo ci sono due funzioni, che possono essere usate solo se si hanno i privilegi di amministratore. La prima @@ -1784,7 +1786,7 @@ delle due \end{functions} Se invece si vogliono settare i gruppi supplementari del processo a quelli di -un utente specifico si può usare \func{initgroups} il cui prototipo è: +un utente specifico, si può usare \func{initgroups} il cui prototipo è: \begin{functions} \headdecl{sys/types.h} \headdecl{grp.h} @@ -1800,9 +1802,9 @@ un utente specifico si pu \end{functions} La funzione esegue la scansione del database dei gruppi (usualmente -\file{/etc/groups}) cercando i gruppi di cui è membro \param{user} costruendo -una lista di gruppi supplementari a cui aggiunge \param{group}, che poi setta -usando \func{setgroups}. +\file{/etc/groups}) cercando i gruppi di cui è membro \param{user} e +costruendo una lista di gruppi supplementari a cui aggiunge \param{group}, che +poi setta 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 @@ -1817,8 +1819,8 @@ In questa sezione tratteremo pi lo \textit{scheduler}\footnote{che è la parte del kernel che si occupa di stabilire quale processo dovrà essere posto in esecuzione.} assegna la CPU ai vari processi attivi. In particolare prendremo in esame i vari meccanismi -con cui viene gestita l'assgnazione del tempo di CPU, ed illustreremo le varie -funzioni di gestione. +con cui viene gestita l'assegnazione del tempo di CPU, ed illustreremo le +varie funzioni di gestione. \subsection{I meccanismi di \textit{scheduling}} @@ -1829,16 +1831,20 @@ il tempo di CPU per l'esecuzione dei processi ed oggetto di numerose ricerche; in ogni caso essa dipende in maniera essenziale anche dal tipo di utilizzo che deve essere fatto del sistema. -La cosa è resa ancora più complicata dal fatto che con sistemi -multi-processore si introduce anche la complessità dovuta alla scelta di quale -sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni la - presenza di ampie cache può rendere poco efficiente trasferire l'esecuzione - di un processo da una CPU ad un'altra, per cui occorrono meccanismi per - determininare quale è la migliore scelta fra le diverse CPU.} Tutto questo -comunque appartiene alle sottigliezze dell'implementazione del kernel, e dal -punto di vista dei programmi che girano in user space di può pensare sempre -alla risorsa tempo di esecuzione, governata dagli stessi mecca, che nel caso -di più processori sarà a disposizione di più di un processo alla volta. +La cosa è resa ancora più complicata dal fatto che con le architetture +multi-processore si introduce anche la problematica dovuta alla scelta di +quale sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni + la presenza di ampie cache può rendere poco efficiente trasferire + l'esecuzione di un processo da una CPU ad un'altra, per cui occorrono + meccanismi per determininare quale è la migliore scelta fra le diverse CPU.} +Tutto questo comunque appartiene alle sottigliezze dell'implementazione del +kernel, e dal punto di vista dei programmi che girano in user space anche +quando si hanno più processori, e quindi potenzialmente anche dei processi che +sono eseguiti davvero in contemporanea, si può pensare alle politiche di +scheduling come concernenti la risorsa \textsl{tempo di esecuzione}, la cui +assegnazione sarà governata dagli stessi meccanismi di scelta di priorità, +solo che nel caso di più processori sarà a disposizione di più di un processo +alla volta. Si tenga presente inoltre che l'utilizzo della CPU è soltanto una delle risorse (insieme alla memoria e all'accesso alle periferiche) che sono @@ -1849,8 +1855,8 @@ prestazioni. La politica tradizionale di scheduling di Unix (che tratteremo in \secref{sec:proc_sched_stand}) è sempre stata basata su delle priorità -dinamiche, che assicurassaro che tutti i processi, anche i meno importanti, -potessero ricevere un po' di tempo di CPU. +dinamiche, che assicurassero che tutti i processi, anche i meno importanti, +potessero ricevere un po' di tempo di CPU. Lo standard POSIX però per tenere conto dei sistemi real-time,\footnote{per sistema real-time si intende un sistema in grado di eseguire operazioni in @@ -1859,8 +1865,8 @@ Lo standard POSIX per 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 in che devono essere -eseguiti in un determinato momento non debbano aspettare la conclusione di un + ritenuto accettabile.} in cui è vitale che i processi che devono essere +eseguiti in un determinato momento non debbano aspettare la conclusione di altri processi che non hanno questa necessità, ha introdotto il concetto di \textsl{priorità assoluta}, chimata anche \textsl{priorità statica}, in contrapposizione con la normale priorità dinamica. @@ -1882,7 +1888,6 @@ Questa viene in genere indicata con un numero - \subsection{Il meccanismo di \textit{scheduling} standard} \label{sec:proc_sched_stand} @@ -1917,7 +1922,7 @@ abbiamo affrontato la gestione dei processi. \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à di interruzione in una fase intermedia. @@ -1942,7 +1947,7 @@ processi. 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 +sono compiute nello stesso spazio di indirizzi del processo. Per questo, anche il solo accesso o l'assegnazione di una variabile possono non essere più operazioni atomiche (torneremo su questi aspetti in \secref{sec:sign_xxx}). @@ -1963,7 +1968,7 @@ condiviso, onde evitare problemi con le ottimizzazioni del codice. 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 una operazione che viene eseguita da un processo in più +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. @@ -2010,26 +2015,26 @@ eseguire in maniera atomica le operazioni necessarie. 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 thread di esecuzione senza che questo comporti nessun problema nella -esecuzione della stessa. La problematica è comune nella programmazione +un altro thread di esecuzione senza che questo comporti nessun problema +nell'esecuzione della stessa. La problematica è comune nella programmazione multi-thread, ma si hanno gli stessi problemi quando si vogliono chiamare delle funzioni all'interno dei manipolatori dei segnali. Fintanto che una funzione opera soltanto con le variabili locali è rientrante; -queste infatti vengono tutte le volte allocate nello stack, e un'altra -invocazione non fa altro che allocarne un'altra copia. Una funzione può non -essere rientrante quando opera su memoria che non è nello stack. Ad esempio -una funzione non è mai rientrante se usa una variabile globale o statica. - -Nel caso invece la funzione operi su un oggetto allocato dinamicamente la cosa -viene a dipendere da come avvengono le operazioni; se l'oggetto è creato ogni -volta e ritornato indietro la funzione può essere rientrante, se invece esso -viene individuato dalla funzione stessa due chiamate alla stessa funzione -potranno interferire quando entrambe faranno riferimento allo stesso oggetto. -Allo stesso modo una funzione può non essere rientrante se usa e modifica un -oggetto che le viene fornito dal chiamante: due chiamate possono interferire -se viene passato lo stesso oggetto; in tutti questi casi occorre molta cura da -parte del programmatore. +queste infatti vengono allocate nello stack, e un'altra invocazione non fa +altro che allocarne un'altra copia. Una funzione può non essere rientrante +quando opera su memoria che non è nello stack. Ad esempio una funzione non è +mai rientrante se usa una variabile globale o statica. + +Nel caso invece la funzione operi su un oggetto allocato dinamicamente, la +cosa viene a dipendere da come avvengono le operazioni: se l'oggetto è creato +ogni volta e ritornato indietro la funzione può essere rientrante, se invece +esso viene individuato dalla funzione stessa, due chiamate alla stessa +funzione potranno interferire quando entrambe faranno riferimento allo stesso +oggetto. Allo stesso modo una funzione può non essere rientrante se usa e +modifica un oggetto che le viene fornito dal chiamante: due chiamate possono +interferire se viene passato lo stesso oggetto; in tutti questi casi occorre +molta cura da parte del programmatore. In genere le funzioni di libreria non sono rientranti, molte di esse ad esempio utilizzano variabili statiche, le \acr{glibc} però mettono a diff --git a/ringraziamenti.tex b/ringraziamenti.tex new file mode 100644 index 0000000..2965001 --- /dev/null +++ b/ringraziamenti.tex @@ -0,0 +1,18 @@ +\chapter{Ringraziamenti} +\label{cha:ringraziamenti} + +Desidero ringraziare tutti coloro che a vario titolo e a più riprese mi hanno +aiutato ed han contribuito a migliorare in molteplici aspetti la qualità di +GaPiL. In ordine rigorosamente alfabetico desidero citare: +\begin{description} +\item[Alessio Frusciante] per l'apprezzamento, le molteplici correzioni ed i + suggerimenti per rendere più chiara l'esposizione. +\item[Daniele Masini] per la rilettura puntuale, le innumerevoli correzioni, i + consigli sull'esposizione ed il contributo relativo alle calling convention + dei linguaggi. +\end{description} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "gapil" +%%% End: -- 2.30.2 From b1ebfb4b1e0eaaaa50b20e510cf4f394ed5ea62c Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 1 Mar 2002 23:24:25 +0000 Subject: [PATCH 12/16] Qualche altra riga --- signal.tex | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/signal.tex b/signal.tex index aafb4ec..5f00b4d 100644 --- a/signal.tex +++ b/signal.tex @@ -830,6 +830,11 @@ dei casi in cui si presenta questa situazione per una una risposta. \item operazioni eseguite con \func{ioctl} che non è detto possano essere eseguite immediatamente. +\item le funzioni di intercomunicazione che si bloccano in attesa di risposte + da altri processi. +\item la funzione \func{pause} (usata appunto per attendere l'arrivo di un + segnale). +\item la funzione \func{wait} (se nessun processo figlio è ancora terminato). \end{itemize*} In questo caso si pone il problema di cosa fare una volta che il manipolatore @@ -839,8 +844,23 @@ tutt'oggi una scelta corrente, ma comporta che i programmi che usano dei manipolatori controllino lo stato di uscita delle funzioni per ripeterne la chiamata qualora l'errore fosse questo. -Dato che dimenticarsi di richiamare una funzione interrotta è un errore comune +Dimenticarsi di richiamare una system call interrotta da un segnale è un +errore comune, tanto che le \acr{glibc} provvedono una macro +\code{TEMP\_FAILURE\_RETRY(expr)} che esegue l'operazione automaticamente, +ripetendo l'esecuzione dell'espressione \var{expr} fintanto che il risultato +non è diverso dall'uscita con un errore \macro{EINTR}. +La soluzione è comunque poco elegante e BSD ha scelto un approccio molto +diverso, che è quello di fare ripartire automaticamente la system call invece +di farla fallire. In questo caso ovviamente non c'è da preoccuparsi di +controllare il codice di errore; si perde però la possibilità di eseguire +azioni specifiche all'occorrenza di questa particolare condizione. + +Linux e le \acr{glibc} consentono di utilizzare entrambi gli approcci, +attraverso una opportuna opzione di \func{sigaction} (vedi +\secref{sec:sig_sigaction}). È da chiarire comunque che nel caso di +interruzione nel mezzo di un trasferimento parziale di dati, le system call +ritornano sempre indicando i byte trasferiti. \subsection{La funzione \func{signal}} @@ -896,7 +916,6 @@ installare l'azione di di default\footnote{si ricordi per intercettati}. - \subsection{Funzioni rientranti e default dei segnali} \label{sec:sig_reentrant} -- 2.30.2 From cf7518466ba15ab1759e5bc312ab99798b8cf2ac Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sat, 2 Mar 2002 16:33:27 +0000 Subject: [PATCH 13/16] Un po' di priorita` dei processi e chiusa la questione dell'assegnazione progressiva dei pid dei processi (Thanks Algol!). --- ChangeLog | 5 ++ prochand.tex | 138 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 90 insertions(+), 53 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8228743..26d9895 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2002-03-02 Simone Piccardi + + * prochand.tex: Aggiunte info sui limiti superiore ed inferiore + dei pid verificate da A. Frusciante. + 2002-03-01 Simone Piccardi * prochand.tex: correzioni varie da D. Masini. diff --git a/prochand.tex b/prochand.tex index 38a9b2f..4f72ab5 100644 --- a/prochand.tex +++ b/prochand.tex @@ -103,15 +103,15 @@ init-+-keventd 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 un'organizzazione gerarchica ad -albero, in maniera analoga a come i file sono organizzati in un albero di -directory (si veda \secref{sec:file_organization}); in \curfig\ 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. + 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}, etc.).} 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 +\secref{sec:file_organization}); in \curfig\ 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 @@ -213,11 +213,15 @@ intero con segno (nel caso di Linux e delle \acr{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\footnote{FIXME: verificare, non sono - sicuro}. Per questo motivo, come visto in \secref{sec:proc_hierarchy}, il -processo di avvio (\cmd{init}) ha sempre il \acr{pid} uguale a uno. +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 32767. +Oltre questo valore l'assegnazione riparte dal numero più basso disponibile a +partire da un minimo di 300,\footnote{questi valori sono definiti dalla macro + \macro{PID\_MAX} in \file{threads.h} e direttamente in \file{fork.c} nei + sorgenti del kernel.} che serve a riservare i \acr{pid} più bassi ai processi +eseguiti dal direttamente dal kernel. Per questo motivo, come visto in +\secref{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 @@ -846,7 +850,7 @@ segnale termina il processo o chiama una funzione di gestione. processo figlio termina. Se un figlio è già terminato la funzione ritorna immediatamente. -Al ritorno lo stato di termininazione del processo viene salvato nella +Al ritorno lo stato di terminazione del processo viene salvato nella variabile puntata da \var{status} e tutte le informazioni relative al processo (vedi \secref{sec:proc_termination}) vengono rilasciate. Nel caso un processo abbia più figli il valore di ritorno permette di @@ -1818,7 +1822,7 @@ quando si definisce \macro{\_POSIX\_SOURCE} o si compila con il flag In questa sezione tratteremo più approfonditamente i meccanismi con il quale lo \textit{scheduler}\footnote{che è la parte del kernel che si occupa di stabilire quale processo dovrà essere posto in esecuzione.} assegna la CPU -ai vari processi attivi. In particolare prendremo in esame i vari meccanismi +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. @@ -1836,15 +1840,14 @@ multi-processore si introduce anche la problematica dovuta alla scelta di quale sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni la presenza di ampie cache può rendere poco efficiente trasferire l'esecuzione di un processo da una CPU ad un'altra, per cui occorrono - meccanismi per determininare quale è la migliore scelta fra le diverse CPU.} + meccanismi per determinare quale è la migliore scelta fra le diverse CPU.} Tutto questo comunque appartiene alle sottigliezze dell'implementazione del -kernel, e dal punto di vista dei programmi che girano in user space anche -quando si hanno più processori, e quindi potenzialmente anche dei processi che -sono eseguiti davvero in contemporanea, si può pensare alle politiche di -scheduling come concernenti la risorsa \textsl{tempo di esecuzione}, la cui -assegnazione sarà governata dagli stessi meccanismi di scelta di priorità, -solo che nel caso di più processori sarà a disposizione di più di un processo -alla volta. +kernel, e dal punto di vista dei programmi che girano in user space, anche +quando si hanno più processori (e dei processi che sono eseguiti davvero in +contemporanea), si può pensare alle politiche di scheduling come concernenti +la risorsa \textsl{tempo di esecuzione}, la cui assegnazione sarà governata +dagli stessi meccanismi di scelta di priorità, solo che nel caso di più +processori sarà a disposizione di più di un processo alla volta. Si tenga presente inoltre che l'utilizzo della CPU è soltanto una delle risorse (insieme alla memoria e all'accesso alle periferiche) che sono @@ -1853,27 +1856,30 @@ importante. Per questo non priorità di esecuzione abbia risultati significativi in termini di prestazioni. -La politica tradizionale di scheduling di Unix (che tratteremo in -\secref{sec:proc_sched_stand}) è sempre stata basata su delle priorità -dinamiche, che assicurassero che tutti i processi, anche i meno importanti, -potessero ricevere un po' di tempo di CPU. - -Lo standard POSIX però per tenere conto dei sistemi real-time,\footnote{per - sistema real-time si intende un sistema in grado di eseguire operazioni in - tempo reale; 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 processi che non hanno questa necessità, ha introdotto il concetto di -\textsl{priorità assoluta}, chimata anche \textsl{priorità statica}, in -contrapposizione con la normale priorità dinamica. - -Il concetto di prorità assoluta dice che quando due processi si contendono -l'esecuzione, vince sempre quello con la priorità assoluta più alta, anche, -grazie al \textit{prehemptive scheduling}, se l'altro è in esecuzione. +Il meccanismo tradizionale di scheduling di Unix (che tratteremo in +\secref{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 tempo reale; 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, anche +quando l'altro è in esecuzione (grazie al \textit{prehemptive scheduling}). Ovviamente questo avviene solo per i processi che sono pronti per essere eseguiti (cioè nello stato \textit{runnable}\footnote{lo stato di un processo è riportato nel campo \texttt{STAT} dell'output del comando \cmd{ps}, @@ -1881,20 +1887,35 @@ eseguiti (cio gli stati \textit{runnable}, \textit{sleep} e di I/O (\textit{uninteruttible sleep}) sono invece indicati con \texttt{R}, \texttt{S} e \texttt{D}.}), la priorità assoluta viene invece ignorata per quelli che sono bloccati su una -richiesta di I/O o in stato di \textit{sleep}. - -Questa viene in genere indicata con un numero - +richiesta di I/O o in stato di \textit{sleep}. 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 +\secref{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 in base ad una priorità dinamica che è +calcolata indipendentemente. È 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} -In Linux tutti i processi hanno sostanzialmente la stessa priorità; benché sia -possibile specificare una priorità assoluta secondo lo standard POSIX -(argomento che tratteremo più avanti) l'uso comune segue quello che è il -meccanismo tradizionale con cui i sistemi +A meno che non si abbiano specifiche esigenze, l'unico meccanismo di +scheduling con il quale si avrà a che fare è quello tradizionale che prevede +solo priorità dinamiche, ed è di questo che di norma ci si dovrà preoccupare +nella programmazione. + +Come accennato in Linux tutti i processi ordinari hanno la stessa priorità +assoluta. Quello che determina quale, fra tutti i processi in attesa di +esecuzione, sarà eseguito per primo, è la priorità dinamica, che è chiamata +così proprio perché viene varia nel corso dell'esecuzione di un processo. + + + \subsection{Il meccanismo di \textit{scheduling real-time}} \label{sec:proc_real_time} @@ -1902,6 +1923,17 @@ meccanismo tradizionale con cui i sistemi Per settare le +\footnote{a meno che non si siano installate le patch di RTLinux o RTAI, con i + quali è possibile ottenere un sistema effettivamente hard real-time.} + +in realtà non si tratta di un vero hard real-time, in quanto + la presenza di eventuali interrupt o di page fault può sempre interrompere + l'esecuzione di un processo, a meno di non installare le estensioni di + RTLinux o RTAI, il normale kernel non è real-time. + + + + \section{Problematiche di programmazione multitasking} \label{sec:proc_multi_prog} -- 2.30.2 From 12f3d0def93dbdf672e012934826df53c44a79a6 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 3 Mar 2002 10:29:39 +0000 Subject: [PATCH 14/16] Finita signal, iniziate kill e raise --- signal.tex | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/signal.tex b/signal.tex index 5f00b4d..aad92a9 100644 --- a/signal.tex +++ b/signal.tex @@ -885,7 +885,7 @@ comportamento, pur mantenendone immutato il prototipo\footnote{in realt \end{prototype} In questa definizione si è usato il tipo \type{sighandler\_t} che è una -estensione GNU definita dalle \acr{glibc} che permette di riscrivere il +estensione GNU, definita dalle \acr{glibc}, che permette di riscrivere il prototipo in una forma più leggibile dell'originario: \begin{verbatim} void (*signal(int signum, void (*handler)(int)))int) @@ -915,20 +915,92 @@ installare l'azione di di default\footnote{si ricordi per \macro{SIGKILL} e \macro{SIGSTOP} non possono essere ignorati né intercettati}. +La funzione \func{signal} originale (e quella attuale in System V) era +conforme alla semantica inaffidabile e resettava l'azione di default a +\macro{SIG\_DEF}; Linux fino alle \acr{libc4} e le \acr{libc5} seguiva la +stessa semantica; al contrario con l'utilizzo delle \acr{glibc2}, Linux, come +BSD, non resetta il manipolatore e blocca il segnale durante la chiamata. La +versione originale della funzione, il cui uso è deprecato, può essere +utilizzata chiamando \func{sysv\_signal}. -\subsection{Funzioni rientranti e default dei segnali} -\label{sec:sig_reentrant} - +È da tenere presente che seguendo lo standard POSIX, il comportamento di un +processo che ignora i segnali \macro{SIGFPE}, \macro{SIGILL}, o +\macro{SIGSEGV} (qualora non originino da una \func{kill} o una \func{raise}) +è indefinito. Un manipolatore che ritorna da questi segnali può dare luogo ad +un ciclo infinito. \subsection{Le funzioni \func{kill} e \func{raise}} \label{sec:sig_kill_raise} +Come accennato in \secref{sec:sig_types}, un segnale può essere generato +``artificialmente'' attraverso l'uso delle funzioni \func{kill} e +\func{raise}, i cui prototipi sono: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{signal.h} + \funcdecl{int kill(pid\_t pid, int sig)} invia il segnale \param{sig} al + processo specificato con \param{pid}. + \funcdecl{int raise(int sig)} invia il segnale \param{sig} al processo + corrente. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\macro{EINVAL}] Si è specificato un numero di segnale invalido. + \item[\macro{EPERM}] Il processo non ha il permesso di inviare il segnale + alla destinazione specificata. + \item[\macro{ESRCH}] Il \acr{pid} o il process group indicati non + esistono. Gli zombie (vedi \ref{sec:proc_termination}) sono considerati come + processi esistenti. + \end{errlist}} +\end{functions} + +La funzione \code{raise(sig)} è sostanzialmente equivalente ad una +\code{kill(getpid(), sig)}. Il valore di \param{sig} specifica il segnale che +si vuole inviare e può essere specificato con una delle macro definite in +\ref{sec:sig_classification}. + +Lo standard POSIX poi prevede che il valore 0 sia usato per specificare il +segnale nullo. Se le funzioni vengono chiamate con questo valore non viene +inviato nessun segnale, ma viene eseguito il controllo degli errori, in tal +caso si otterrà un errore \macro{EPERM} se non si hanno i permessi necessari +ed un errore \macro{ESRCH} se il processo specificato non esiste. Si tenga +conto però che il sistema ricicla i \acr{pid}, così come visto in +\secref{sec:proc_pid}, per cui l'esistenza di un processo non significa che +esso sia realmente quello a cui si intendeva mandare il segnale. + +Per poter effettuare l'invio del segnale ad un altro processo, si devono +possedere i privilegi di amministratore, oppure il \textit{real user id} o +l'\textit{effective user id} del chiamante devono corrispondere al +\textit{real user id} o al \textit{aved user id} della destinazione. Nel caso +del segnale \macro{SIGCONT} entrambi i processi devono appartenere alla stessa +sessione. + +Il valore dell'argomento \param{pid} specifica la destinazione a cui inviare +il segnale e può assumere i seguenti significati: +\begin{basedescript}{\desclabelwidth{2cm}\desclabelstyle{\nextlinelabel}} +\item[$\texttt{pid}>0$] il segnale è mandato al processo con il \acr{pid} + indicato. +\item[$\texttt{pid}=0$] il segnale è mandato ad ogni processo del + \textit{process group} del chiamante. +\item[$\texttt{pid}=-1$] il segnale è mandato ad ogni processo (eccetto + \cmd{init}). +\item[$\texttt{pid}<-1$] il segnale è mandato ad ogni processo del process + group $|\code{pid}|$. +\end{basedescript} + + \subsection{Le funzioni \func{alarm} e \func{pause}} \label{sec:sig_alarm_pause} -\section{Il controllo dei segnali} +\subsection{Funzioni rientranti e default dei segnali} +\label{sec:sig_reentrant} + + + +\section{Gestione avanzata} \label{sec:sig_control} -- 2.30.2 From 7090500d79c488db306ed0c065b90bb0c0505430 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 3 Mar 2002 23:05:11 +0000 Subject: [PATCH 15/16] Correzioni per le footnote e scritta altra roba sui segnali (finite kill e raise, iniziate alarm e setitimer). --- elemtcp.tex | 55 +++++++------- filedir.tex | 40 +++++----- filestd.tex | 16 ++-- fileunix.tex | 37 +++++---- intro.tex | 8 +- network.tex | 4 +- process.tex | 16 ++-- prochand.tex | 18 ++--- signal.tex | 206 +++++++++++++++++++++++++++++++++++++++++---------- simpltcp.tex | 12 +-- socket.tex | 12 +-- system.tex | 89 +++++++++++----------- 12 files changed, 321 insertions(+), 192 deletions(-) diff --git a/elemtcp.tex b/elemtcp.tex index ce44d1d..7e75f92 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -44,7 +44,7 @@ creazione di una connessione \func{connect}, attraverso un procedimento che viene chiamato \textsl{apertura attiva}, dall'inglese \textit{active open}. La chiamata di \func{connect} blocca il processo e causa l'invio da parte del client di un - segmento SYN\footnote{Si ricordi che il segmento è l'unità elementare di + segmento SYN,\footnote{Si ricordi che il segmento è l'unità elementare di dati trasmessa dal protocollo TCP al livello superiore; tutti i segmenti hanno un header che contiene le informazioni che servono allo \textit{stack TCP} (così viene di solito chiamata la parte del kernel che @@ -52,7 +52,7 @@ creazione di una connessione ci sono una serie di flag usati per gestire la connessione, come SYN, ACK, URG, FIN, alcuni di essi, come SYN (che sta per \textit{syncronize}) corrispondono a funzioni particolari del protocollo e danno il nome al - segmento, (per maggiori dettagli vedere \capref{cha:tcp_protocol})}, in + segmento, (per maggiori dettagli vedere \capref{cha:tcp_protocol}).} in sostanza viene inviato al server un pacchetto IP che contiene solo gli header IP e TCP (con il numero di sequenza iniziale e il flag SYN) e le opzioni di TCP. @@ -128,20 +128,20 @@ regolare la connessione. Normalmente vengono usate le seguenti opzioni: \textsl{finestra annunciata} (\textit{advertized window}) con la quale ciascun capo della comunicazione dichiara quanto spazio disponibile ha in memoria per i dati. Questo è un numero a 16 bit dell'header, che così può - indicare un massimo di 65535 byte (anche se Linux usa come massimo 32767 - per evitare problemi con alcuni stack bacati che usano l'aritmetica con - segno per implementare lo stack TCP); ma alcuni tipi di connessione come - quelle ad alta velocità (sopra i 45Mbits/sec) e quelle che hanno grandi - ritardi nel cammino dei pacchetti (come i satelliti) richiedono una finestra - più grande per poter ottenere il massimo dalla trasmissione, per questo - esiste questa opzione che indica un fattore di scala da applicare al valore - della finestra annunciata\footnote{essendo una nuova opzione per garantire - la compatibilità con delle vecchie implementazioni del protocollo la - procedura che la attiva prevede come negoziazione che l'altro capo della - connessione riconosca esplicitamente l'opzione inserendola anche lui nel - suo SYN di risposta dell'apertura della connessione} per la connessione - corrente (espresso come numero di bit cui shiftare a sinistra il valore - della finestra annunciata inserito nel pacchetto). + indicare un massimo di 65535 byte (anche se Linux usa come massimo 32767 per + evitare problemi con alcuni stack bacati che usano l'aritmetica con segno + per implementare lo stack TCP); ma alcuni tipi di connessione come quelle ad + alta velocità (sopra i 45Mbits/sec) e quelle che hanno grandi ritardi nel + cammino dei pacchetti (come i satelliti) richiedono una finestra più grande + per poter ottenere il massimo dalla trasmissione, per questo esiste questa + opzione che indica un fattore di scala da applicare al valore della finestra + annunciata\footnote{essendo una nuova opzione per garantire la compatibilità + con delle vecchie implementazioni del protocollo la procedura che la + attiva prevede come negoziazione che l'altro capo della connessione + riconosca esplicitamente l'opzione inserendola anche lui nel suo SYN di + risposta dell'apertura della connessione.} per la connessione corrente + (espresso come numero di bit cui shiftare a sinistra il valore della + finestra annunciata inserito nel pacchetto). \item \textit{timestamp option}, è anche questa una nuova opzione necessaria per le connessioni ad alta velocità per evitare possibili corruzioni di dati @@ -662,7 +662,7 @@ per il server\footnote{un'eccezione a tutto ci In questo caso viene fatta assegnare dal kernel una porta effimera che poi viene registrata presso il \textit{portmapper}; quest'ultimo è un altro demone che deve essere contattato dai client per ottenere la porta effimera - su cui si trova il server} che in genere viene identificato dalla porta su + su cui si trova il server.} che in genere viene identificato dalla porta su cui risponde. Con \func{bind} si può assegnare un IP specifico ad un socket, purché questo @@ -877,11 +877,11 @@ dette code. Stevens riporta che BSD ha sempre applicato un fattore di 1.5 al valore, e provvede una tabella con i risultati ottenuti con vari kernel, compreso Linux 2.0, che mostrano le differenze fra diverse implementazioni. -In Linux il significato di questo valore è cambiato a partire dal kernel -2.2 per prevenire l'attacco chiamato \textit{syn flood}. Questo si basa +In Linux il significato di questo valore è cambiato a partire dal kernel 2.2 +per prevenire l'attacco chiamato \textit{syn flood}. Questo si basa sull'emissione da parte dell'attaccante di un grande numero di pacchetti SYN indirizzati verso una porta forgiati con indirizzo IP fasullo\footnote{con la - tecnica che viene detta \textit{ip spoofing}} così che i SYN$+$ACK vanno + tecnica che viene detta \textit{ip spoofing}.} così che i SYN$+$ACK vanno perduti e la coda delle connessioni incomplete viene saturata, impedendo di fatto ulteriori connessioni. @@ -996,14 +996,13 @@ Se la funzione ha successo restituisce il descrittore di un nuovo socket creato dal kernel (detto \textit{connected socket}) a cui viene associata la prima connessione completa (estratta dalla relativa coda, vedi \secref{sec:TCPel_func_listen}) che il client TCP ha effettuato verso il -socket \var{sockfd}. Quest'ultimo (detto \textit{listening socket}) è -quello creato all'inizio e messo in ascolto con \func{listen}, e non viene -toccato dalla funzione. -Se non ci sono connessioni pendenti da accettare la funzione mette in attesa -il processo\footnote{a meno che non si sia settato il socket per essere - non-bloccante, nel qual caso ritorna con l'errore \macro{EAGAIN}, - torneremo su questa modalità di operazione in \secref{sec:xxx_sock_noblock}} -fintanto che non ne arriva una. +socket \var{sockfd}. Quest'ultimo (detto \textit{listening socket}) è quello +creato all'inizio e messo in ascolto con \func{listen}, e non viene toccato +dalla funzione. Se non ci sono connessioni pendenti da accettare la funzione +mette in attesa il processo\footnote{a meno che non si sia settato il socket + per essere non-bloccante, nel qual caso ritorna con l'errore \macro{EAGAIN}. + Torneremo su questa modalità di operazione in + \secref{sec:xxx_sock_noblock}.} fintanto che non ne arriva una. Il meccanismo di funzionamento di \func{accept} è essenziale per capire il funzionamento di un server: in generale infatti c'è sempre un solo socket in diff --git a/filedir.tex b/filedir.tex index efeb41c..d7cfa6f 100644 --- a/filedir.tex +++ b/filedir.tex @@ -151,7 +151,7 @@ singola system call. Si ricordi infine che il file non viene eliminato dal disco fintanto che tutti i riferimenti ad esso sono stati cancellati, solo quando il \textit{link count} mantenuto nell'inode diventa zero lo spazio occupato viene rimosso. A -questo però si aggiunge una altra condizione, e cioè che non ci siano processi +questo però si aggiunge un'altra condizione, e cioè che non ci siano processi che abbiano detto file aperto. Questa proprietà viene spesso usata per essere sicuri di non lasciare file @@ -185,9 +185,9 @@ directory \end{prototype} Per cambiare nome ad un file o a una directory (che devono comunque essere -nello stesso filesystem) si usa invece la funzione \func{rename}\footnote{la +nello stesso filesystem) si usa invece la funzione \func{rename},\footnote{la funzione è definita dallo standard ANSI C solo per i file, POSIX estende la - funzione anche alle directory}, il cui prototipo è: + funzione anche alle directory.} il cui prototipo è: \begin{prototype}{stdio.h} {int rename(const char *oldpath, const char *newpath)} @@ -248,7 +248,7 @@ eseguita. In ogni caso se \var{newpath} esiste e l'operazione fallisce per un qualche motivo (come un crash del kernel), \func{rename} garantisce di lasciare -presente una istanza di \var{newpath}. Tuttavia nella sovrascrittura potrà +presente un'istanza di \var{newpath}. Tuttavia nella sovrascrittura potrà esistere una finestra in cui sia \var{oldpath} che \var{newpath} fanno riferimento allo stesso file. @@ -387,13 +387,13 @@ stringa con un carattere nullo e la tronca alla dimensione specificata da Un caso comune che si può avere con i link simbolici è la creazione dei cosiddetti \textit{loop}. La situazione è illustrata in \curfig, che riporta la struttura della directory \file{/boot}. Come si vede si è creato al suo -interno un link simbolico che punta di nuovo a \file{/boot}\footnote{Questo +interno un link simbolico che punta di nuovo a \file{/boot}.\footnote{Questo tipo di loop è stato effettuato per poter permettere a \cmd{grub} (un bootloader in grado di leggere direttamente da vari filesystem il file da lanciare come sistema operativo) di vedere i file in questa directory con lo stesso path con cui verrebbero visti dal sistema operativo, anche se essi si trovano, come è solito, su una partizione separata (e che \cmd{grub} - vedrebbe come radice).}. + vedrebbe come radice).} Questo può causare problemi per tutti quei programmi che effettuano la scansione di una directory senza tener conto dei link simbolici, ad esempio se @@ -422,7 +422,7 @@ quanto aprendo in scrittura \file{temporaneo} verr $ cat temporaneo cat: temporaneo: No such file or directory \end{verbatim}%$ -con un errore che può sembrare sbagliato, dato che una ispezione con \cmd{ls} +con un errore che può sembrare sbagliato, dato che un'ispezione con \cmd{ls} ci mostrerebbe invece l'esistenza di \file{temporaneo}. @@ -597,7 +597,7 @@ Per accedere al contenuto delle directory si usano i cosiddetti \capref{cha:files_std_interface}); la funzione \func{opendir} apre uno di questi stream e la funzione \func{readdir} legge il contenuto della directory, i cui elementi sono le \textit{directory entry} (da distinguersi da quelle -della cache di cui parlavamo in \secref{sec:file_vfs}) in una opportuna +della cache di cui parlavamo in \secref{sec:file_vfs}) in un'opportuna struttura \var{struct dirent}. (NdA Il resto va scritto!!! É noioso e lo farò più avanti). @@ -644,8 +644,8 @@ apposita funzione di libreria, \func{getcwd}, il cui prototipo Il buffer deve essere sufficientemente lungo da poter contenere il pathname completo più lo zero di terminazione della stringa. Qualora esso ecceda le dimensioni specificate con \var{size} la funzione restituisce un errore. Si -può anche specificare un puntatore nullo come \var{buffer}\footnote{questa è - una estensione allo standard POSIX.1, supportata da Linux}, nel qual caso la +può anche specificare un puntatore nullo come \var{buffer},\footnote{questa è + un'estensione allo standard POSIX.1, supportata da Linux.} nel qual caso la stringa sarà allocata automaticamente per una dimensione pari a \var{size} qualora questa sia diversa da zero, o della lunghezza esatta del pathname altrimenti. In questo caso ci si deve ricordare di disallocare la stringa una @@ -998,7 +998,7 @@ Il primo valore dell'elenco di \secref{tab:file_mode_flags} binaria che permette di estrarre i bit nei quali viene memorizzato il tipo di file, i valori successivi sono le costanti corrispondenti ai singoli bit, e possono essere usati per effettuare la selezione sul tipo di file voluto, con -una opportuna combinazione. +un'opportuna combinazione. \begin{table}[htb] \centering @@ -1182,7 +1182,7 @@ essere capiti se si tiene conto di quanto gi directory sono file (che contengono una lista di nomi) che il sistema tratta in maniera del tutto analoga a tutti gli altri. -Per questo motivo tutte le volte che compiremo una operazione su un file che +Per questo motivo tutte le volte che compiremo un'operazione su un file che comporta una modifica del nome contenuto nella directory, andremo anche a scrivere sulla directory che lo contiene cambiandone il tempo di modifica. Un esempio di questo può essere la cancellazione di un file, invece leggere o @@ -1343,7 +1343,7 @@ Esistono varie estensioni a questo modello,\footnote{come le \textit{Access Control List} che possono essere aggiunte al filesystem standard con opportune patch, e sono presenti in filesystem non ancora inclusi nel kernel ufficiale come \textsl{xfs}, o meccanismi di controllo ancora più - sofisticati come il \textit{mandatory access control} di SE-Linux} ma nella + sofisticati come il \textit{mandatory access control} di SE-Linux.} ma nella maggior parte dei casi il meccanismo standard è più che sufficiente a soffisfare tutte le necessità più comuni. I tre permessi di base associati ad ogni file sono: @@ -1601,9 +1601,9 @@ Le attuali implementazioni di memoria virtuale e filesystem rendono sostanzialmente inutile questo procedimento. Benché ormai non venga più utilizzato per i file, lo \textsl{sticky bit} ha -invece assunto un uso importante per le directory\footnote{lo \textsl{sticky - bit} per le directory è una estensione non definita nello standard POSIX, - Linux però la supporta, così come BSD e SVR4.}; in questo caso se il bit è +invece assunto un uso importante per le directory;\footnote{lo \textsl{sticky + bit} per le directory è un'estensione non definita nello standard POSIX, + Linux però la supporta, così come BSD e SVR4.} in questo caso se il bit è settato un file potrà essere rimosso dalla directory soltanto se l'utente ha il permesso di scrittura su di essa ed inoltre è vera una delle seguenti condizioni: @@ -1818,7 +1818,7 @@ misura di sicurezza, volta ad scongiurare l'abuso dei bit \acr{suid} e \acr{sgid}; essa consiste nel cancellare automaticamente questi bit qualora un processo che non appartenga all'amministratore scriva su un file. In questo modo anche se un utente malizioso scopre un file \acr{suid} su cui può -scrivere, una eventuale modifica comporterà la perdita di ogni ulteriore +scrivere, un'eventuale modifica comporterà la perdita di ogni ulteriore privilegio. \subsection{La funzione \func{umask}} @@ -1839,7 +1839,7 @@ funzione \func{umask}, il cui prototipo Questa maschera è una caratteristica di ogni processo\footnote{è infatti contenuta nel campo \var{umask} di \var{fs\_struct}, vedi - \figref{fig:proc_task_struct}} e viene utilizzata per impedire che alcuni + \figref{fig:proc_task_struct}.} e viene utilizzata per impedire che alcuni permessi possano essere assegnati ai nuovi file in sede di creazione. I bit indicati nella maschera vengono infatti esclusi quando un nuovo file viene creato. @@ -1901,10 +1901,10 @@ in link simbolico si deve usare la funzione \func{lchown}.\footnote{fino alla versione 2.1.81 in Linux \func{chown} non seguiva i link simbolici, da allora questo comportamento è stato assegnato alla funzione \func{lchown}, introdotta per l'occasione, ed è stata creata una nuova system call per - \func{chown} che seguisse i link simbolici} La funzione \func{fchown} opera + \func{chown} che seguisse i link simbolici.} La funzione \func{fchown} opera su un file aperto, essa è mutuata da BSD, ma non è nello standard POSIX. Un'altra estensione rispetto allo standard POSIX è che specificando -1 come -valore per \var{owner} e \var{group} i valori restano immutati. +valore per \var{owner} e \var{group} i valori restano immutati. Quando queste funzioni sono chiamate con successo da un processo senza i privilegi di root entrambi i bit \acr{suid} e \acr{sgid} vengono diff --git a/filestd.tex b/filestd.tex index d46f965..5e6cdec 100644 --- a/filestd.tex +++ b/filestd.tex @@ -209,9 +209,9 @@ corrente in uno stream. \label{sec:file_fopen} Le funzioni che si possono usare per aprire uno stream sono solo tre: -\func{fopen}, \func{fdopen} e \func{freopen}\footnote{\func{fopen} e +\func{fopen}, \func{fdopen} e \func{freopen},\footnote{\func{fopen} e \func{freopen} fanno parte dello standard ANSI C, \func{fdopen} è parte - dello standard POSIX.1.}, i loro prototipi sono: + dello standard POSIX.1.} i loro prototipi sono: \begin{functions} \headdecl{stdio.h} \funcdecl{FILE *fopen(const char *path, const char *mode)} @@ -322,7 +322,7 @@ processo (si veda \secref{sec:file_umask}). In caso di file aperti in lettura e scrittura occorre ricordarsi che c'è di messo una bufferizzazione; per questo motivo lo standard ANSI C -richiede che ci sia una operazione di posizionamento fra una operazione +richiede che ci sia un'operazione di posizionamento fra un'operazione di output ed una di input o viceversa (eccetto il caso in cui l'input ha incontrato la fine del file), altrimenti una lettura può ritornare anche il risultato di scritture precedenti l'ultima effettuata. @@ -332,7 +332,7 @@ una scrittura una delle funzioni \func{fflush}, \func{fseek}, \func{fsetpos} o \func{rewind} prima di eseguire una rilettura; viceversa nel caso in cui si voglia fare una scrittura subito dopo aver eseguito una lettura occorre prima usare una delle funzioni \func{fseek}, \func{fsetpos} o \func{rewind}. Anche -una operazione nominalmente nulla come \code{fseek(file, 0, SEEK\_CUR)} è +un'operazione nominalmente nulla come \code{fseek(file, 0, SEEK\_CUR)} è sufficiente a garantire la sincronizzazione. Una volta aperto lo stream, si può cambiare la modalità di bufferizzazione @@ -1470,9 +1470,9 @@ e \func{setlinebuf}, i loro prototipi sono: \noindent tutte queste funzioni sono realizzate con opportune chiamate a \func{setvbuf} e sono definite solo per compatibilità con le vecchie librerie BSD. Infine le \acr{glibc} provvedono le funzioni non standard\footnote{anche - queste sono originarie di Solaris} \func{\_\_flbf} e \func{\_\_fbufsize} che -permettono di leggere le proprietà di bufferizzazione di uno stream; i cui -prototipi sono: + queste funzioni sono originarie di Solaris.} \func{\_\_flbf} e +\func{\_\_fbufsize} che permettono di leggere le proprietà di bufferizzazione +di uno stream; i cui prototipi sono: \begin{functions} \headdecl{stdio\_ext.h} @@ -1497,7 +1497,7 @@ scelta, si pu \end{prototype} \noindent anche di questa funzione esiste una analoga \func{fflush\_unlocked}\footnote{accessibile definendo \macro{\_BSD\_SOURCE} o - \macro{\_SVID\_SOURCE} o \macro{\_GNU\_SOURCE}} che non effettua il blocco + \macro{\_SVID\_SOURCE} o \macro{\_GNU\_SOURCE}.} che non effettua il blocco dello stream. Se \param{stream} è \macro{NULL} lo scarico dei dati è forzato per tutti gli diff --git a/fileunix.tex b/fileunix.tex index e99db64..cacce23 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -1,4 +1,4 @@ -\chapter{I file: l'interfaccia standard unix} +\chapter{I file: l'interfaccia standard Unix} \label{cha:file_unix_interface} Esamineremo in questo capitolo la prima delle due interfacce di programmazione @@ -68,7 +68,7 @@ file, fra cui: campo \var{f\_pos}). \item un puntatore all'inode\footnote{nel kernel 2.4.x si è in realtà passati ad un puntatore ad una struttura \var{dentry} che punta a sua volta - all'inode passando per la nuova struttura del VFS} del file. + all'inode passando per la nuova struttura del VFS.} del file. %\item un puntatore alla tabella delle funzioni \footnote{la struttura % \var{f\_op} descritta in \secref{sec:file_vfs_work}} che si possono usare % sul file. @@ -153,7 +153,7 @@ restano i limiti imposti dall'amministratore (vedi \secref{sec:sys_limits}). \section{Le funzioni base} \label{sec:file_base_func} -L'interfaccia standard unix per l'input/output sui file è basata su cinque +L'interfaccia standard Unix per l'input/output sui file è basata su cinque funzioni fondamentali: \func{open}, \func{read}, \func{write}, \func{lseek} e \func{close}, usate rispettivamente per aprire, leggere, scrivere, spostarsi e chiudere un file. @@ -245,7 +245,7 @@ sempre il file descriptor con il valore pi zero. Se il file è un terminale o una fifo il flag verrà ignorato, negli altri casi il comportamento non è specificato. \\ \macro{O\_NOFOLLOW} & se \var{pathname} è un link simbolico la chiamata - fallisce. Questa è una estensione BSD aggiunta in Linux dal kernel 2.1.126. + fallisce. Questa è un'estensione BSD aggiunta in Linux dal kernel 2.1.126. Nelle versioni precedenti i link simbolici sono sempre seguiti, e questa opzione è ignorata. \\ \macro{O\_DIRECTORY} & se \var{pathname} non è una directory la chiamata @@ -303,7 +303,7 @@ sempre il file descriptor con il valore pi \footnotetext[5]{l'opzione origina da SVr4, dove però causava il ritorno da una \func{read} con un valore nullo e non con un errore, questo introduce - una ambiguità, dato che come vedremo in \secref{sec:file_read} il ritorno di + un'ambiguità, dato che come vedremo in \secref{sec:file_read} il ritorno di zero da parte di \func{read} ha il significato di una end-of-file.} Questa caratteristica permette di prevedere qual'è il valore del file @@ -450,7 +450,7 @@ La nuova posizione sommato al riferimento dato da \param{whence}; quest'ultimo può assumere i seguenti valori\footnote{per compatibilità con alcune vecchie notazioni questi valori possono essere rimpiazzati rispettivamente con 0, 1 e 2 o con - \macro{L\_SET}, \macro{L\_INCR} e \macro{L\_XTND}}: + \macro{L\_SET}, \macro{L\_INCR} e \macro{L\_XTND}.}: \begin{basedescript}{\desclabelwidth{2.0cm}} \item[\macro{SEEK\_SET}] si fa riferimento all'inizio del file: il valore di \var{offset} è la nuova posizione. @@ -480,9 +480,9 @@ essersi spostata, ma noi scriveremo alla posizione settata in precedenza. Non tutti i file supportano la capacità di eseguire una \func{lseek}, in questo caso la funzione ritorna l'errore \macro{EPIPE}. Questo, oltre che per i tre casi citati nel prototipo, vale anche per tutti quei dispositivi che non -supportano questa funzione, come ad esempio per le \acr{tty}\footnote{altri +supportano questa funzione, come ad esempio per le \acr{tty}.\footnote{altri sistemi, usando \macro{SEEK\_SET}, in questo caso ritornano il numero di - caratteri che vi sono stati scritti}. Lo standard POSIX però non specifica + caratteri che vi sono stati scritti.} Lo standard POSIX però non specifica niente al proposito. Infine alcuni device, ad esempio \file{/dev/null}, non causano un errore ma restituiscono un valore indefinito. @@ -563,7 +563,7 @@ Nella seconda versione delle \textit{Single Unix Specification}\footnote{questa funzione, e l'analoga \func{pwrite} sono state aggiunte nel kernel 2.1.60, il supporto nelle \acr{glibc}, compresa l'emulazione per i vecchi kernel che non hanno la system call, è stato - aggiunto con la versione 2.1} (quello che viene chiamato normalmente Unix98, + aggiunto con la versione 2.1.} (quello che viene chiamato normalmente Unix98, vedi \secref{sec:intro_opengroup}) è stata introdotta la definizione di un'altra funzione di lettura, \func{pread}, che diventa accessibile con la definizione: @@ -636,7 +636,7 @@ i file ordinari il numero di byte scritti da \var{count}, a meno di un errore. Negli altri casi si ha lo stesso comportamento di \func{read}. -Anche per \func{write} lo standard Unix98 definisce una analoga \func{pwrite} +Anche per \func{write} lo standard Unix98 definisce un'analoga \func{pwrite} per scrivere alla posizione indicata senza modificare la posizione corrente nel file, il suo prototipo è: \begin{prototype}{unistd.h} @@ -731,7 +731,7 @@ corrente nel file varier Si noti inoltre che anche i flag di stato del file (quelli settati dall'argomento \param{flag} di \func{open}) essendo tenuti nella voce della \textit{file table}\footnote{per la precisione nel campo \var{f\_flags} di - \var{file}}, vengono in questo caso condivisi. Ai file però sono associati + \var{file}.}, vengono in questo caso condivisi. Ai file però sono associati anche altri flag, dei quali l'unico usato al momento è \macro{FD\_CLOEXEC}, detti \textit{file descriptor flags}. Questi ultimi sono tenuti invece in \var{file\_struct}, e perciò sono specifici di ciascun processo e non vengono @@ -765,14 +765,13 @@ file sar corrente settata con la \func{lseek} che non corrisponde più alla fine del file, e la successiva \func{write} sovrascriverà i dati del secondo processo. -Il problema è che usare due system call in successione non è una operazione +Il problema è che usare due system call in successione non è un'operazione atomica; il problema è stato risolto introducendo la modalità \macro{O\_APPEND}. In questo caso infatti, come abbiamo descritto in precedenza, è il kernel che aggiorna automaticamente la posizione alla fine del file prima di effettuare la scrittura, e poi estende il file. Tutto questo avviene all'interno di una singola system call (la \func{write}) che non -essendo interrompibile da un altro processo costituisce una operazione -atomica. +essendo interrompibile da un altro processo costituisce un'operazione atomica. Un altro caso tipico in cui è necessaria l'atomicità è quello in cui si vuole creare un file di lock, bloccandosi se il file esiste. In questo caso la @@ -798,10 +797,10 @@ secondo tempo rispetto al momento della esecuzione della \func{write}. Per questo motivo, quando è necessaria una sincronizzazione dei dati, il sistema mette a disposizione delle funzioni che provvedono a forzare lo -scarico dei dati dai buffer del kernel\footnote{come già accennato neanche +scarico dei dati dai buffer del kernel.\footnote{come già accennato neanche questo da la garanzia assoluta che i dati siano integri dopo la chiamata, l'hardware dei dischi è in genere dotato di un suo meccanismo interno che - può ritardare ulteriormente la scrittura effettiva.}. La prima di queste + può ritardare ulteriormente la scrittura effettiva.} La prima di queste funzioni è \func{sync} il cui prototipo è: \begin{prototype}{unistd.h}{int sync(void)} @@ -847,10 +846,10 @@ di \var{fstat} come i tempi del file). Si tenga presente che questo non comporta la sincronizzazione della directory che contiene il file (e scrittura della relativa voce su -disco) che deve essere effettuata esplicitamente\footnote{in realtà per +disco) che deve essere effettuata esplicitamente.\footnote{in realtà per il filesystem \acr{ext2}, quando lo si monta con l'opzione \cmd{sync}, il kernel provvede anche alla sincronizzazione automatica delle voci - delle directory.}. + delle directory.} \subsection{La funzioni \func{dup} e \func{dup2}} @@ -1026,7 +1025,7 @@ avanti quando affronteremo le problematiche ad esse relative. Per determinare le modalità di accesso inoltre è necessario estrarre i bit di accesso (ottenuti con il comando \macro{F\_GETFL}); infatti la definizione corrente non assegna bit separati a \macro{O\_RDONLY}, \macro{O\_WRONLY} e -\macro{O\_RDWR}\footnote{posti rispettivamente ai valori 0, 1 e 2}, per cui il +\macro{O\_RDWR},\footnote{posti rispettivamente ai valori 0, 1 e 2.} per cui il valore si ottiene eseguendo un AND binario del valore di ritorno di \func{fcntl} con la maschera \macro{O\_ACCMODE} anch'essa definita in \file{fcntl.h}. diff --git a/intro.tex b/intro.tex index b03d1e3..e8d8a1f 100644 --- a/intro.tex +++ b/intro.tex @@ -195,10 +195,10 @@ Ad ogni utente richiesto all'ingresso nel sistema dalla procedura di \textit{login}. Questa procedura si incarica di verificare l'identità dell'utente, in genere attraverso la richiesta di una parola d'ordine, anche se sono possibili -meccanismi diversi\footnote{Ad esempio usando la libreria PAM +meccanismi diversi.\footnote{Ad esempio usando la libreria PAM (\textit{Pluggable Autentication Methods}) è possibile astrarre completamente i meccanismi di autenticazione e sostituire ad esempio l'uso - delle password con meccanismi di identificazione biometrica}. + delle password con meccanismi di identificazione biometrica.} Eseguita la procedura di riconoscimento in genere il sistema manda in esecuzione un programma di interfaccia (che può essere la \textit{shell} su @@ -229,8 +229,8 @@ Infine in ogni unix \acr{uid} è zero. Esso identifica l'amministratore del sistema, che deve essere in grado di fare qualunque operazione; per l'utente \textit{root} infatti i meccanismi di controllo descritti in precedenza sono -disattivati\footnote{i controlli infatti vengono sempre eseguiti da un codice - del tipo \texttt{if (uid) \{ ... \}}}. +disattivati.\footnote{i controlli infatti vengono sempre eseguiti da un codice + del tipo \texttt{if (uid) \{ ... \}}} \section{Gli standard di unix e GNU/Linux} diff --git a/network.tex b/network.tex index 12d5c88..b1f825f 100644 --- a/network.tex +++ b/network.tex @@ -529,8 +529,8 @@ Quando un pacchetto IP viene inviato su una interfaccia di rete e le sue dimensioni eccedono la MTU viene eseguita la cosiddetta \textit{frammentazione}, i pacchetti cioè vengono spezzati (sia da IPv4 che da IPv6, anche se i pacchetti frammentati sono gestiti con modalità -diverse\footnote{il primo usa un flag nell'header, il secondo una opportuna - opzione, si veda \secref{cha:ip_protocol}}), in blocchi più piccoli che +diverse,\footnote{il primo usa un flag nell'header, il secondo una opportuna + opzione, si veda \secref{cha:ip_protocol}.}) in blocchi più piccoli che possono essere trasmessi attraverso l'interfaccia. \begin{table}[!htb] diff --git a/process.tex b/process.tex index 483fbb9..554f4d6 100644 --- a/process.tex +++ b/process.tex @@ -23,9 +23,9 @@ programma: si possono avere pi ciascun processo vedrà la sua copia del codice (in realtà il kernel fa sì che tutte le parti uguali siano condivise), avrà un suo spazio di indirizzi, variabili proprie e sarà eseguito in maniera completamente indipendente da -tutti gli altri\footnote{questo non è del tutto vero nel caso di un programma +tutti gli altri.\footnote{questo non è del tutto vero nel caso di un programma \textit{multi-thread}, ma sulla gestione dei \textit{thread} in Linux - torneremo più avanti}. + torneremo più avanti.} \subsection{La funzione \func{main}} @@ -255,9 +255,9 @@ di basso livello dipendono spesso in maniera diretta dall'architettura dell'hardware), ma quello più tipico, usato dai sistemi unix-like come Linux è la cosiddetta \textsl{memoria virtuale} che consiste nell'assegnare ad ogni processo uno spazio virtuale di indirizzamento lineare, in cui gli indirizzi -vanno da zero ad un qualche valore massimo\footnote{nel caso di Linux fino al +vanno da zero ad un qualche valore massimo.\footnote{nel caso di Linux fino al kernel 2.2 detto massimo era, per macchine a 32bit, di 2Gb, con il kernel - 2.4 ed il supporto per la \textit{high-memory} il limite è stato esteso}. + 2.4 ed il supporto per la \textit{high-memory} il limite è stato esteso.} Come accennato in \capref{cha:intro_unix} questo spazio di indirizzi è virtuale e non corrisponde all'effettiva posizione dei dati nella RAM del @@ -495,13 +495,13 @@ La funzione \func{realloc} si usa invece per cambiare (in genere aumentare) la dimensione di un'area di memoria precedentemente allocata, la funzione vuole in ingresso il puntatore restituito dalla precedente chiamata ad una \func{malloc} (se è passato un valore \macro{NULL} allora la funzione si -comporta come \func{malloc}\footnote{questo è vero per Linux e +comporta come \func{malloc},\footnote{questo è vero per Linux e l'implementazione secondo lo standard ANSI C, ma non è vero per alcune vecchie implementazioni, inoltre alcune versioni delle librerie del C consentivano di usare \func{realloc} anche per un puntatore liberato con \func{free} purché non ci fossero state nel frattempo altre chiamate a funzioni di allocazione, questa funzionalità è totalmente deprecata e non è - consentita sotto Linux.}), ad esempio quando si deve far crescere la + consentita sotto Linux.}) ad esempio quando si deve far crescere la dimensione di un vettore. In questo caso se è disponibile dello spazio adiacente al precedente la funzione lo utilizza, altrimenti rialloca altrove un blocco della dimensione voluta, copiandoci automaticamente il contenuto; lo @@ -1340,8 +1340,8 @@ motivo \macro{va\_list} direttamente ad un altra variabile dello stesso tipo. Per risolvere questo problema lo standard ISO C99\footnote{alcuni sistemi che non hanno questa macro provvedono al suo posto \macro{\_\_va\_copy} che era il nome proposto - in una bozza dello standard} ha previsto una macro ulteriore che permette di -eseguire la copia di un puntatore alla lista degli argomenti: + in una bozza dello standard.} ha previsto una macro ulteriore che permette +di eseguire la copia di un puntatore alla lista degli argomenti: \begin{prototype}{stdarg.h}{void va\_copy(va\_list dest, va\_list src)} Copia l'attuale valore \param{src} del puntatore alla lista degli argomenti su \param{dest}. diff --git a/prochand.tex b/prochand.tex index 4f72ab5..baced8d 100644 --- a/prochand.tex +++ b/prochand.tex @@ -453,7 +453,7 @@ si pu primo\footnote{a partire dal kernel 2.5.2-pre10 è stato introdotto il nuovo scheduler di Ingo Molnar che esegue sempre per primo il figlio; per mantenere la portabilità è opportuno non fare comunque affidamento su questo - comportamento} dopo la chiamata a \func{fork}; dall'esempio si può notare + comportamento.} dopo la chiamata a \func{fork}; dall'esempio si può notare infatti come nei primi due cicli sia stato eseguito per primo il padre (con la stampa del \acr{pid} del nuovo processo) per poi passare all'esecuzione del figlio (completata con i due avvisi di esecuzione ed uscita), e tornare @@ -682,7 +682,7 @@ eseguite alla chiusura di un processo \item ad ogni processo figlio viene assegnato un nuovo padre (in genere \cmd{init}). \item viene inviato il segnale \macro{SIGCHLD} al processo padre (vedi - \secref{sec:sig_xxx}). + \secref{sec:sig_sigchld}). \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 (vedi \secref{sec:sess_xxx}). @@ -799,7 +799,7 @@ 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 +\func{wait}, vedi \secref{sec:sig_sigchld} 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. @@ -964,9 +964,9 @@ avremo la certezza che la chiamata a \func{wait} non si bloccher \macro{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{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}.\\ + sia in Linux che in altri Unix.}\\ \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}. \\ @@ -1208,7 +1208,7 @@ la lista completa \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 (vedi \secref{sec:sig_xxx}). +\item il tempo restante ad un allarme (vedi \secref{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 @@ -1286,7 +1286,7 @@ problematiche connesse ad una gestione accorta dei privilegi. 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 + 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 @@ -1881,11 +1881,11 @@ Il concetto di priorit l'esecuzione, vince sempre quello con la priorità assoluta più alta, anche quando l'altro è in esecuzione (grazie al \textit{prehemptive scheduling}). Ovviamente questo avviene solo per i processi che sono pronti per essere -eseguiti (cioè nello stato \textit{runnable}\footnote{lo stato di un processo +eseguiti (cioè nello stato \textit{runnable},\footnote{lo stato di un processo è riportato nel campo \texttt{STAT} dell'output del comando \cmd{ps}, abbiamo già visto che lo stato di \textit{zombie} è indicato con \texttt{Z}, gli stati \textit{runnable}, \textit{sleep} e di I/O (\textit{uninteruttible - sleep}) sono invece indicati con \texttt{R}, \texttt{S} e \texttt{D}.}), + sleep}) sono invece indicati con \texttt{R}, \texttt{S} e \texttt{D}.}) la priorità assoluta viene invece ignorata per quelli che sono bloccati su una richiesta di I/O o in stato di \textit{sleep}. La priorità assoluta viene in genere indicata con un numero intero, ed un valore più alto comporta una diff --git a/signal.tex b/signal.tex index aad92a9..90885ff 100644 --- a/signal.tex +++ b/signal.tex @@ -45,8 +45,8 @@ il seguente: \item una richiesta dell'utente di terminare o fermare il programma. In genere si realizza attraverso un segnale mandato dalla shell in corrispondenza della pressione di tasti del terminale come \code{C-c} o - \code{C-z}\footnote{indichiamo con \code{C-x} la pressione simultanea al - tasto \code{x} del tasto control (ctrl in molte tastiere)}. + \code{C-z}.\footnote{indichiamo con \code{C-x} la pressione simultanea al + tasto \code{x} del tasto control (ctrl in molte tastiere).} \item l'esecuzione di una \func{kill} o di una \func{raise} da parte del processo stesso o di un'altro (solo nel caso della \func{kill}). \end{itemize*} @@ -873,7 +873,7 @@ da essere del tutto inutile in un sistema Unix; ogni implementazione successiva ne ha modificato e ridefinito il comportamento, pur mantenendone immutato il prototipo\footnote{in realtà alcune vecchie implementazioni (SVR4 e 4.3+BSD) usano parametri aggiuntivi - per definire il comportamento della funzione} che è: + per definire il comportamento della funzione.} che è: \begin{prototype}{signal.h} {sighandler\_t signal(int signum, sighandler\_t handler)} @@ -898,10 +898,10 @@ con il precedente prototipo si pu typedef void (* sighandler_t)(int) \end{verbatim} e cioè un puntatore ad una funzione \type{void} (cioè senza valore di ritorno) -e che prende un argomento di tipo \type{int}\footnote{si devono usare le +e che prende un argomento di tipo \type{int}.\footnote{si devono usare le parentesi intorno al nome della funzione per via delle precedenze degli operatori del C, senza di esse si sarebbe definita una funzione che ritorna - un puntatore a \type{void} e non un puntatore ad una funzione \type{void}}. + un puntatore a \type{void} e non un puntatore ad una funzione \type{void}.} La funzione \func{signal} quindi restituisce e prende come secondo argomento un puntatore a una funzione di questo tipo, che è appunto il manipolatore del segnale. @@ -911,19 +911,30 @@ direttamente con una delle costanti definite in \secref{sec:sig_standard}. Il manipolatore \param{handler} invece, oltre all'indirizzo della funzione da chiamare all'occorrenza del segnale, può assumere anche i due valori costanti \macro{SIG\_IGN} con cui si dice ignorare il segnale e \macro{SIG\_DFL} per -installare l'azione di di default\footnote{si ricordi però che i due segnali +installare l'azione di di default.\footnote{si ricordi però che i due segnali \macro{SIGKILL} e \macro{SIGSTOP} non possono essere ignorati né - intercettati}. - -La funzione \func{signal} originale (e quella attuale in System V) era -conforme alla semantica inaffidabile e resettava l'azione di default a -\macro{SIG\_DEF}; Linux fino alle \acr{libc4} e le \acr{libc5} seguiva la -stessa semantica; al contrario con l'utilizzo delle \acr{glibc2}, Linux, come -BSD, non resetta il manipolatore e blocca il segnale durante la chiamata. La -versione originale della funzione, il cui uso è deprecato, può essere -utilizzata chiamando \func{sysv\_signal}. - -È da tenere presente che seguendo lo standard POSIX, il comportamento di un + intercettati.} + +La funzione restituisce l'indirizzo dell'azione precedente, che può essere +salvato per poterlo ripristinare (con un'altra chiamata a \func{signal}) in un +secondo tempo. Si ricordi che se si setta come azione \macro{SIG\_IGN} (o si +setta un \macro{SIG\_DFL} per un segnale il cui default è di essere ignorato), +tutti i segnali pendenti saranno scartati, e non verranno mai notificati. + +L'uso di \func{signal} è soggetto a problemi di compatibilità, dato che essa +si comporta in maniera diversa per sistemi derivati da BSD o da System V. In +questi ultimi infatti la funzione è conforme al comportamento originale dei +primi Unix in cui il manipolatore viene disinstallato alla sua chiamata +secondo la semantica inaffidabile; Linux seguiva questa convenzione fino alle +\acr{libc5}. Al contrario BSD segue la semantica affidabile, non resettando il +manipolatore e bloccando il segnale durante l'esecuzione dello stesso. Con +l'utilizzo delle \acr{glibc2} anche Linux è passato a questo comportamento; +quello della versione originale della funzione, il cui uso è deprecato per i +motivi visti in \secref{sec:sig_semantics}, può essere ottenuto chiamando +\func{sysv\_signal}. In generale, per evitare questi problemi, tutti i nuovi +programmi dovrebbero usare \func{sigaction}. + +È da tenere presente che, seguendo lo standard POSIX, il comportamento di un processo che ignora i segnali \macro{SIGFPE}, \macro{SIGILL}, o \macro{SIGSEGV} (qualora non originino da una \func{kill} o una \func{raise}) è indefinito. Un manipolatore che ritorna da questi segnali può dare luogo ad @@ -934,16 +945,33 @@ un ciclo infinito. \label{sec:sig_kill_raise} Come accennato in \secref{sec:sig_types}, un segnale può essere generato -``artificialmente'' attraverso l'uso delle funzioni \func{kill} e -\func{raise}, i cui prototipi sono: +direttamente da un processo. L'invio di un sengale generico può essere +effettuato attraverso delle funzioni \func{kill} e \func{raise}. La prima +serve per inviare un segnale al processo corrente, ed il suo prototipo è: +\begin{prototype}{signal.h}{int raise(int sig)} + Invia il segnale \param{sig} al processo corrente. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, il solo errore restituito è \macro{EINVAL} qualora si sia + specificato un numero di segnale invalido.} +\end{prototype} + +Il valore di \param{sig} specifica il segnale che si vuole inviare e può +essere specificato con una delle macro definite in +\secref{sec:sig_classification}. In genere questa funzione viene usata per +riprodurre il comportamento di default di un segnale che sia stato +intercettato. In questo caso, una volta eseguite le operazioni volute, il +manipolatore potrà reinstallare l'azione di default, e attivarla con +\func{raise}. + +Se invece si vuole inviare un segnale ad un altro processo occorre utilizzare +la funzione \func{kill}; il suo prototipo è: \begin{functions} \headdecl{sys/types.h} \headdecl{signal.h} - \funcdecl{int kill(pid\_t pid, int sig)} invia il segnale \param{sig} al + \funcdecl{int kill(pid\_t pid, int sig)} Invia il segnale \param{sig} al processo specificato con \param{pid}. - \funcdecl{int raise(int sig)} invia il segnale \param{sig} al processo - corrente. - + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un errore, nel qual caso \var{errno} assumerà i valori: \begin{errlist} @@ -957,26 +985,19 @@ Come accennato in \secref{sec:sig_types}, un segnale pu \end{functions} La funzione \code{raise(sig)} è sostanzialmente equivalente ad una -\code{kill(getpid(), sig)}. Il valore di \param{sig} specifica il segnale che -si vuole inviare e può essere specificato con una delle macro definite in -\ref{sec:sig_classification}. +\code{kill(getpid(), sig)}. Siccome \func{raise} è definita nello standard ISO +C non esiste in alcune vecchie versioni di Unix, per cui in generale l'uso di +\func{kill} è più portabile. Lo standard POSIX poi prevede che il valore 0 sia usato per specificare il segnale nullo. Se le funzioni vengono chiamate con questo valore non viene inviato nessun segnale, ma viene eseguito il controllo degli errori, in tal caso si otterrà un errore \macro{EPERM} se non si hanno i permessi necessari ed un errore \macro{ESRCH} se il processo specificato non esiste. Si tenga -conto però che il sistema ricicla i \acr{pid}, così come visto in -\secref{sec:proc_pid}, per cui l'esistenza di un processo non significa che +conto però che il sistema ricicla i \acr{pid} (come accennato in +\secref{sec:proc_pid}) per cui l'esistenza di un processo non significa che esso sia realmente quello a cui si intendeva mandare il segnale. -Per poter effettuare l'invio del segnale ad un altro processo, si devono -possedere i privilegi di amministratore, oppure il \textit{real user id} o -l'\textit{effective user id} del chiamante devono corrispondere al -\textit{real user id} o al \textit{aved user id} della destinazione. Nel caso -del segnale \macro{SIGCONT} entrambi i processi devono appartenere alla stessa -sessione. - Il valore dell'argomento \param{pid} specifica la destinazione a cui inviare il segnale e può assumere i seguenti significati: \begin{basedescript}{\desclabelwidth{2cm}\desclabelstyle{\nextlinelabel}} @@ -990,13 +1011,117 @@ il segnale e pu group $|\code{pid}|$. \end{basedescript} +Solo l'amministratore può inviare un segnale ad un processo qualunque, in +tutti gli altri casi il \textit{real user id} o l'\textit{effective user id} +del processo chiamante devono corrispondere al \textit{real user id} o al +\textit{saved user id} della destinazione. Fa eccezione il caso in cui il +segnale inviato sia \macro{SIGCONT}, nel quale occorre che entrambi i processi +appartengano alla stessa sessione. Inoltre, dato il ruolo fondamentale che +riveste nel sistema (si ricordi quanto visto in \secref{sec:sig_termination}), +non è possibile inviare al processo 1 (cioè a \cmd{init}) segnali per i quali +esso non abbia un manipolatore installato. + +Infine, seguendo le specifiche POSIX 1003.1-2001, l'uso della chiamata +\code{kill(-1, sig)} comporta che il segnale sia inviato (con la solita +eccezione di \cmd{init}) a tutti i processi per i quali i permessi lo +consentano. Lo standard permette comunque alle varie implementazione di +escludere alcuni processi specifici: nel caso in questione Linux non invia il +segnale al processo che ha effettuato la chiamata. + + +\subsection{Le funzioni \func{alarm} e \func{abort}} +\label{sec:sig_alarm_abort} + +Un caso particolare di segnali generati a richiesta è quello che riguarda i +segnali di temporizzazione e e \macro{SIGABORT}, per i quali sono previste +funzioni specifiche che ne effettuino l'invio. La prima di queste è +\func{alarm} il cui prototipo è: +\begin{prototype}{unistd.h}{unsigned int alarm(unsigned int seconds)} + Predispone l'invio di \macro{SIGALARM} dopo \param{seconds} secondi. + + \bodydesc{La funzione restituisce il numero di secondi rimanenti ad un + precedente allarme, o zero se non c'erano allarmi pendenti.} +\end{prototype} -\subsection{Le funzioni \func{alarm} e \func{pause}} -\label{sec:sig_alarm_pause} +La funzione provvede un meccanismo che consente ad un processo di predisporre +un'interruzione nel futuro, (ad esempio per effettuare una qualche operazione +dopo un certo periodo di tempo), programmando l'emissione si un segnale di +\macro{SIGALARM} dopo il numero di secondi specificato da \param{seconds}. +Chiaramente la precisione è determinata da quella dell'orologio di sistema, e +sono sempre possibili ritardi in caso di un sistema eccessivamente carico. + +Se si specifica per \param{seconds} un valore nullo non verrà inviato nessun +segnale; siccome alla chiamata viene cancellato ogni precedente allarme, +questo può essere usato per cancellare una programmazione precedente. La +funzione inoltre ritorna il numero di secondi rimanenti all'invio dell'allarme +precedentemente programmato, in modo che sia eventualmente possibile +effettuare delle scelte in caso di necessità di più interruzioni. + +In \secref{sec:sys_unix_time} abbiamo visto che ad ogni processo sono +associati tre tempi diversi: \textit{clock time}, \textit{user time} e +\textit{system time}. Per poterli calcolare il kernel mantiene tre diversi +timer per ciascun processo: +\begin{itemize} +\item un \textit{real-time timer} che calcola il tempo reale trascorso (che + corrisponde al \textit{clock time}). La scadenza di questo timer provoca + l'emissione di \macro{SIGALARM}. +\item un \textit{virtual timer} che calcola il tempo di processore usato dal + processo in user space (che corrisponde all'\textit{user time}). La scadenza + di questo timer provoca l'emissione di \macro{SIGVTALRM}. +\item un \textit{profiling timer} che calcola la somma dei tempi di processore + utilizzati direttamente dal processo in user space, e dal kernel nelle + system call ad esso relative (che corrisponde a quello che in + \secref{sec:sys_unix_time} abbiamo chiamato \textit{CPU time}). La scadenza + di questo timer provoca l'emissione di \macro{SIGPROF}. +\end{itemize} + +Il timer usato da \func{alarm} è il \textit{clock time}, e corrisponde cioè al +tempo reale. Dato che \func{alarm} non consente di usare gli altri timer, e +non può specificare intervalli con precisione maggiore al secondo le +\acr{glibc} provvedono la funzione \func{setitimer} che permette un controllo +completo, a scapito di un uso molto più complesso. Il suo prototipo è: +\begin{prototype}{sys/time.h}{int setitimer(int which, const struct + itimerval *value, struct itimerval *ovalue)} + + Predispone l'invio di un segnale di allarme alla scadenza dell'intervallo + \param{value} sul timer specificato da \func{which}. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} può assumere i valori \macro{EINVAL} e + \macro{EFAULT}.} +\end{prototype} +Il valore di \param{which} permette di specificare quale dei tre timer usare; +i possibili valori sono riportati in \tabref{tab:sig_setitimer_values}. + +\begin{table}[htb] + \centering + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Valore} & \textbf{Timer} \\ + \hline + \hline + \macro{ITIMER\_REAL} & \textit{real-time timer}\\ + \macro{ITIMER\_VIRTUAL} & \textit{virtual timer}\\ + \macro{ITIMER\_PROF} & \textit{profiling timer}\\ + \hline + \end{tabular} + \caption{Valori dell'argomento \param{which} per la funzione + \func{setitimer}.} + \label{tab:sig_setitimer_values} +\end{table} + + +\subsection{Le funzioni \func{pause} e \func{sleep}} +\label{sec:sig_pause_sleep} + + + + + +\subsection{Le semantiche di \macro{SIGCHLD}} +\label{sec:sig_sigchld} -\subsection{Funzioni rientranti e default dei segnali} -\label{sec:sig_reentrant} @@ -1016,6 +1141,9 @@ il segnale e pu +\subsection{Funzioni rientranti e default dei segnali} +\label{sec:sig_reentrant} + , affrontando inoltre le varie problematiche di programmazione che si devono tenere presenti quando si ha a che fare con essi. diff --git a/simpltcp.tex b/simpltcp.tex index c119df0..f210f21 100644 --- a/simpltcp.tex +++ b/simpltcp.tex @@ -8,13 +8,13 @@ comunicazione in entrambe le direzioni. Inoltre prenderemo in esame, oltre al comportamento in condizioni normali, anche tutti i possibili scenari particolari (errori, sconnessione della rete, crash del client o del server durante la connessione) che possono avere luogo -durante l'impiego di una applicazione di rete. +durante l'impiego di un'applicazione di rete. \section{Il servizio \texttt{echo}} \label{sec:TCPsimp_echo} -L'applicazione scelta come esempio sarà una implementazione elementare, ma +L'applicazione scelta come esempio sarà un'implementazione elementare, ma completa, del servizio \texttt{echo}. Il servizio \texttt{echo} è uno dei servizi standard solitamente provvisti direttamente dal superserver \cmd{inetd}, ed è definito dall'RFC~862. Come dice il nome il servizio deve @@ -35,10 +35,10 @@ risponde alle richieste di un client; tutto quello che cambia nel caso si una applicazione più complessa è la elaborazione dell'input del client da parte del server nel fornire le risposte in uscita. -Partiremo da una implementazione elementare che dovrà essere rimaneggiata di +Partiremo da un'implementazione elementare che dovrà essere rimaneggiata di volta in volta per poter tenere conto di tutte le evenienze che si possono -manifestare nella vita reale di una applicazione di rete, fino ad arrivare ad -una implementazione completa. +manifestare nella vita reale di un'applicazione di rete, fino ad arrivare ad +un'implementazione completa. \subsection{La struttura del server} \label{sec:TCPsimp_server_main} @@ -262,7 +262,7 @@ il client esce. Benché il codice dell'esempio precedente sia molto ridotto, esso ci permetterà di considerare in dettaglio tutte le problematiche che si possono incontrare -nello scrivere una applicazione di rete. Infatti attraverso l'esame delle sue +nello scrivere un'applicazione di rete. Infatti attraverso l'esame delle sue modalità di funzionamento normali, all'avvio e alla terminazione, e di quello che avviene nelle varie situazioni limite, da una parte potremo approfondire la comprensione del protocollo TCP/IP e dall'altra ricavare le indicazioni diff --git a/socket.tex b/socket.tex index 36f1f3e..619c1dd 100644 --- a/socket.tex +++ b/socket.tex @@ -8,7 +8,7 @@ operativi. Dopo una breve panoramica sulle caratteristiche di questa interfaccia vedremo come creare un socket e come collegarlo allo specifico protocollo di rete che -utilizzerà per la comunicazione. Per evitare una introduzione puramente teorica +utilizzerà per la comunicazione. Per evitare un'introduzione puramente teorica concluderemo il capitolo con un primo esempio di applicazione. \section{Una panoramica} @@ -22,9 +22,9 @@ con essi. \label{sec:sock_socket_def} Il \textit{socket}\footnote{una traduzione letterale potrebbe essere - \textsl{manicotto}, ma essendo universalmente noti come socket utilizzeremo - sempre la parola inglese} è uno dei principali meccanismi di comunicazione -fra programmi utilizzato in ambito unix. Il socket costituisce in sostanza un + \textsl{presa}, ma essendo universalmente noti come socket utilizzeremo + sempre la parola inglese.} è uno dei principali meccanismi di comunicazione +fra programmi utilizzato in ambito Unix. Il socket costituisce in sostanza un canale di comunicazione fra due processi su cui si possono leggere e scrivere dati analogo a quello di una pipe ma a differenza di questa e degli altri meccanismi esaminati nel capitolo \capref{cha:IPC} i socket non sono limitati @@ -430,7 +430,7 @@ problema e le relative soluzioni). \subsection{La struttura degli indirizzi IPv6} \label{sec:sock_sa_ipv6} -Essendo IPv6 una estensione di IPv4 i socket di tipo \macro{PF\_INET6} sono +Essendo IPv6 un'estensione di IPv4 i socket di tipo \macro{PF\_INET6} sono sostanzialmente identici ai precedenti; la parte in cui si trovano praticamente tutte le differenze è quella della struttura degli indirizzi. La struttura degli indirizzi è definita ancora in \file{netinet/in.h}. @@ -912,7 +912,7 @@ Il primo passo (\texttt{\small 14--18}) socket in tutte le chiamate successive. Nel caso la chiamata fallisca si stampa un errore con la relativa routine e si esce. -Il passo seguente (\texttt{\small 19--27}) è quello di costruire una apposita +Il passo seguente (\texttt{\small 19--27}) è quello di costruire un'apposita struttura \type{sockaddr\_in} in cui sarà inserito l'indirizzo del server ed il numero della porta del servizio. Il primo passo è inizializzare tutto a zero, per poi inserire il tipo di protocollo e la porta (usando per diff --git a/system.tex b/system.tex index 067dd59..062b90d 100644 --- a/system.tex +++ b/system.tex @@ -49,7 +49,7 @@ La prima funzionalit contengono le costanti necessarie definite come macro di preprocessore, per la seconda invece sono ovviamente necessarie delle funzioni. La situazione è complicata dal fatto che ci sono molti casi in cui alcuni di questi limiti -sono fissi in una implementazione mentre possono variare in un altra. Tutto +sono fissi in un'implementazione mentre possono variare in un altra. Tutto questo crea una ambiguità che non è sempre possibile risolvere in maniera chiara; in generale quello che succede è che quando i limiti del sistema sono fissi essi vengono definiti come macro di preprocessore nel file @@ -476,7 +476,7 @@ suo prototipo \subsection{La funzione \func{uname}} \label{sec:sys_uname} -Una altra funzione che si può utilizzare per raccogliere informazioni sia +Un'altra funzione che si può utilizzare per raccogliere informazioni sia riguardo al sistema che al computer su cui esso sta girando è \func{uname}, il suo prototipo è: \begin{prototype}{sys/utsname.h}{int uname(struct utsname *info)} @@ -636,7 +636,7 @@ Come accennato in \secref{sec:file_organization} per poter accedere ai file occorre prima rendere disponibile al sistema il filesystem su cui essi sono memorizzati; l'operazione di attivazione del filesystem è chiamata \textsl{montaggio}, per far questo in Linux\footnote{la funzione è specifica - di Linux e non è portabile} si usa la funzione \func{mount} il cui prototipo + di Linux e non è portabile.} si usa la funzione \func{mount} il cui prototipo è: \begin{prototype}{sys/mount.h} {mount(const char *source, const char *target, const char *filesystemtype, @@ -780,13 +780,13 @@ Una volta che non si voglia pi \macro{ENAMETOOLONG}, \macro{ENOENT} o \macro{ELOOP}.} \end{prototype} \noindent la funzione prende il nome della directory su cui il filesystem è -montato e non il file o il dispositivo che è stato montato\footnote{questo è +montato e non il file o il dispositivo che è stato montato,\footnote{questo è vero a partire dal kernel 2.3.99-pre7, prima esistevano due chiamate separate e la funzione poteva essere usata anche specificando il file di - dispositivo.}, in quanto con il kernel 2.4.x è possibile montare lo stesso + dispositivo.} in quanto con il kernel 2.4.x è possibile montare lo stesso dispositivo in più punti. Nel caso più di un filesystem sia stato montato sullo stesso \textit{mount point} viene smontato quello che è stato montato -per ultimo. +per ultimo. Si tenga presente che la funzione fallisce quando il filesystem è \textsl{occupato}, questo avviene quando ci sono ancora file aperti sul @@ -811,8 +811,8 @@ seconda del tipo di filesystem alcune (o tutte) possono essere superate, evitando l'errore di \macro{EBUSY}. In tutti i casi prima dello smontaggio viene eseguita una sincronizzazione dei dati. -Altre due funzioni specifiche di Linux\footnote{esse si trovano anche su BSD, - ma con una struttura diversa}, utili per ottenere in maniera diretta +Altre due funzioni specifiche di Linux,\footnote{esse si trovano anche su BSD, + ma con una struttura diversa.} utili per ottenere in maniera diretta informazioni riguardo al filesystem su cui si trova un certo file, sono \func{statfs} e \func{fstatfs}, i cui prototipi sono: \begin{functions} @@ -964,13 +964,13 @@ date e del tempo in un sistema unix-like, e quelle per convertire i vari tempi nelle differenti rappresentazioni che vengono utilizzate. -\subsection{La misura del tempo in unix} +\subsection{La misura del tempo in Unix} \label{sec:sys_unix_time} -Storicamente i sistemi unix-like hanno sempre mantenuto due distinti -valori per i tempi all'interno del sistema, essi sono rispettivamente -chiamati \textit{calendar time} e \textit{process time}, secondo le -definizioni: +Storicamente i sistemi unix-like hanno sempre mantenuto due distinti tipi di +dati per la misure dei tempi all'interno del sistema: essi sono +rispettivamente chiamati \textit{calendar time} e \textit{process time}, +secondo le definizioni: \begin{itemize} \item \textit{calendar time}: è il numero di secondi dalla mezzanotte del primo gennaio 1970, in tempo universale coordinato (o UTC), data che viene @@ -980,11 +980,11 @@ definizioni: viene mantenuto l'orologio del calcolatore, e viene usato ad esempio per indicare le date di modifica dei file o quelle di avvio dei processi. Per memorizzare questo tempo è stato riservato il tipo primitivo \type{time\_t}. -\item \textit{process time}: talvolta anche detto tempo di CPU. Viene misurato +\item \textit{process time}: detto anche tempo di processore. Viene misurato in \textit{clock tick}, corrispondenti al numero di interruzioni effettuate dal timer di sistema, e che per Linux avvengono ogni centesimo di - secondo\footnote{eccetto per la piattaforma alpha dove avvengono ogni - millesimo di secondo}. Il dato primitivo usato per questo tempo è + secondo.\footnote{eccetto per la piattaforma alpha dove avvengono ogni + millesimo di secondo.} Il dato primitivo usato per questo tempo è \type{clock\_t}, inoltre la costante \macro{HZ} restituisce la frequenza di operazione del timer, e corrisponde dunque al numero di tick al secondo. Lo standard POSIX definisce allo stesso modo la costante \macro{CLK\_TCK}); @@ -992,32 +992,35 @@ definizioni: \secref{sec:sys_limits}). \end{itemize} -In genere si usa il \textit{calendar time} per tenere le date dei file e le -informazioni analoghe che riguardano i tempi di ``orologio'', usati ad esempio -per i demoni che compiono lavori amministrativi ad ore definite, come -\cmd{cron}. Di solito questo vene convertito automaticamente dal valore in UTC -al tempo locale, utilizzando le opportune informazioni di localizzazione +In genere si usa il \textit{calendar time} per esprimere le date dei file e le +informazioni analoghe che riguardano i cosiddetti \textsl{tempi di orologio}, +che vengono usati ad esempio per i demoni che compiono lavori amministrativi +ad ore definite, come \cmd{cron}. + +Di solito questo tempo viene convertito automaticamente dal valore in UTC al +tempo locale, utilizzando le opportune informazioni di localizzazione (specificate in \file{/etc/timezone}). E da tenere presente che questo tempo è -mantenuto dal sistema e non corrisponde all'orologio hardware del calcolatore. +mantenuto dal sistema e non è detto che corrisponda al tempo tenuto +dall'orologio hardware del calcolatore. -Il \textit{process time} di solito si esprime in secondi e viene usato appunto -per tenere conto dei tempi di esecuzione dei processi. Per ciascun processo il -kernel tiene tre di questi tempi: -\begin{itemize*} -\item \textit{clock time} -\item \textit{user time} -\item \textit{system time} -\end{itemize*} -il primo è il tempo ``reale'' (viene anche chiamato \textit{wall clock time}) -dall'avvio del processo, e misura il tempo trascorso fino alla sua -conclusione; chiaramente un tale tempo dipende anche dal carico del sistema e -da quanti altri processi stavano girando nello stesso periodo. Il secondo -tempo è quello che la CPU ha speso nell'esecuzione delle istruzioni del -processo in user space. Il terzo è il tempo impiegato dal kernel per eseguire -delle system call per conto del processo medesimo (tipo quello usato per -eseguire una \func{write} su un file). In genere la somma di user e system -time viene chiamato \textit{CPU time}. +Anche il \textit{process time} di solito si esprime in secondi, ma provvede una +precisione ovviamente superiore al \textit{calendar time} (la cui granularità +minima è il secondo) e viene usato per tenere conto dei tempi di esecuzione +dei processi. Per ciascun processo il kernel calcola tre tempi diversi: +\begin{description*} +\item[\textit{clock time}]: il tempo \textsl{reale} (viene chiamato anche + \textit{wall clock time}) passato dall'avvio del processo. Chiaramente tale + tempo dipende anche dal carico del sistema e da quanti altri processi + stavano girando nello stesso periodo. +\item[\textit{user time}]: il tempo che la CPU ha impiegato nell'esecuzione + delle istruzioni del processo in user space. +\item[\textit{system time}]: il tempo che la CPU ha impiegato nel kernel per + eseguire delle system call per conto del processo. +\end{description*} +In genere la somma di \textit{user time} e \textit{system time} indica il +tempo di processore totale in cui il sistema è stato effettivamente impegnato +nell'eseguire un certo processo e viene chiamato \textit{CPU time}. @@ -1044,11 +1047,11 @@ costante \macro{EOF} (a seconda della funzione); ma questo valore segnala solo che c'è stato un errore, non il tipo di errore. Per riportare il tipo di errore il sistema usa la variabile globale -\var{errno}\footnote{L'uso di una variabile globale può comportare alcuni +\var{errno},\footnote{L'uso di una variabile globale può comportare alcuni problemi (ad esempio nel caso dei thread) ma lo standard ISO C consente anche di definire \var{errno} come un \textit{modifiable lvalue}, quindi si può anche usare una macro, e questo è infatti il modo usato da Linux per - renderla locale ai singoli thread.}, definita nell'header \file{errno.h}; la + renderla locale ai singoli thread.} definita nell'header \file{errno.h}; la variabile è in genere definita come \type{volatile} dato che può essere cambiata in modo asincrono da un segnale (per una descrizione dei segnali si veda \secref{cha:signals}), ma dato che un manipolatore di segnale scritto @@ -1095,7 +1098,7 @@ errore sconosciuto. La funzione utilizza una stringa statica che non deve essere modificata dal programma e che è utilizzabile solo fino ad una chiamata successiva a \func{strerror}; nel caso si usino i thread è provvista\footnote{questa funzione è una estensione GNU, non fa parte dello - standard POSIX} una versione apposita: + standard POSIX.} una versione apposita: \begin{prototype}{string.h} {char *strerror\_r(int errnum, char *buff, size\_t size)} Analoga a \func{strerror} ma ritorna il messaggio in un buffer @@ -1106,7 +1109,7 @@ provvista\footnote{questa funzione che utilizza un buffer che il singolo thread deve allocare, per evitare i problemi connessi alla condivisione del buffer statico. Infine, per completare la caratterizzazione dell'errore, si può usare anche la variabile -globale\footnote{anche questa è una estensione GNU} +globale\footnote{anche questa è un'estensione GNU.} \var{program\_invocation\_short\_name} che riporta il nome del programma attualmente in esecuzione. -- 2.30.2 From 52c484e2b0db3aba63c356136bea66f9aec48ef5 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Mon, 4 Mar 2002 23:36:36 +0000 Subject: [PATCH 16/16] Finito con alarm e abort. --- signal.tex | 144 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 134 insertions(+), 10 deletions(-) diff --git a/signal.tex b/signal.tex index 90885ff..3bc1af1 100644 --- a/signal.tex +++ b/signal.tex @@ -823,7 +823,7 @@ indenfinitamente, che per questo vengono chiamate \textsl{lente}. Un elenco dei casi in cui si presenta questa situazione è il seguente: \begin{itemize*} \item lettura da file che possono bloccarsi in attesa di dati non ancora - presenti (come per certi dispositivi, la rete o le pipe). + presenti (come per certi file di dispositivo, la rete o le pipe). \item scrittura sugli stessi file, nel caso in cui dati non possano essere accettati immediatamente. \item apertura di un file di dipositivo che richiede operazioni non immediate @@ -1047,8 +1047,6 @@ La funzione provvede un meccanismo che consente ad un processo di predisporre un'interruzione nel futuro, (ad esempio per effettuare una qualche operazione dopo un certo periodo di tempo), programmando l'emissione si un segnale di \macro{SIGALARM} dopo il numero di secondi specificato da \param{seconds}. -Chiaramente la precisione è determinata da quella dell'orologio di sistema, e -sono sempre possibili ritardi in caso di un sistema eccessivamente carico. Se si specifica per \param{seconds} un valore nullo non verrà inviato nessun segnale; siccome alla chiamata viene cancellato ogni precedente allarme, @@ -1076,10 +1074,14 @@ timer per ciascun processo: \end{itemize} Il timer usato da \func{alarm} è il \textit{clock time}, e corrisponde cioè al -tempo reale. Dato che \func{alarm} non consente di usare gli altri timer, e -non può specificare intervalli con precisione maggiore al secondo le -\acr{glibc} provvedono la funzione \func{setitimer} che permette un controllo -completo, a scapito di un uso molto più complesso. Il suo prototipo è: +tempo reale. La funzione, pur essendo molto semplice, presenta numerosi +limiti: non consente di usare gli altri timer, non può specificare intervalli +con precisione maggiore del secondo e genera il segnale una sola volta. + +Per ovviare a questi limiti Linux deriva da BSD la funzione \func{setitimer} +che permette di usare un timer qualunque e l'invio di segnali periodici, al +costo però di una maggiore complessità d'uso e di una minore portabilità. Il +suo prototipo è: \begin{prototype}{sys/time.h}{int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue)} @@ -1091,9 +1093,9 @@ completo, a scapito di un uso molto pi \macro{EFAULT}.} \end{prototype} -Il valore di \param{which} permette di specificare quale dei tre timer usare; -i possibili valori sono riportati in \tabref{tab:sig_setitimer_values}. - +Il valore di \param{which} permette di specificare quale dei tre timer +illustrati in precedenza usare; i possibili valori sono riportati in +\tabref{tab:sig_setitimer_values}. \begin{table}[htb] \centering \begin{tabular}[c]{|l|l|} @@ -1111,10 +1113,132 @@ i possibili valori sono riportati in \tabref{tab:sig_setitimer_values}. \label{tab:sig_setitimer_values} \end{table} +Il valore della struttura specificata \param{value} viene usato per settare il +timer, se il puntatore \param{ovalue} non è nullo il precedente valore viene +salvato qui. I valori dei timer devono essere indicati attraverso una +struttura \var{itimerval}, definita in \ref{fig:file_stat_struct}. + +La struttura è composta da due membri, il primo, \var{it\_interval} definisce +il periodo del timer; il secondo, \var{it\_value} il tempo mancante alla +scadenza. Entrambi esprimono i tempi tramite una struttura \var{timeval} che +permette una precisione fino al microsecondo. + +Ciascun timer decrementa il valore di \var{it\_value} fino a zero, poi invia +il segnale e resetta \var{it\_value} al valore di \var{it\_interval}, +ripetendo il ciclo; se \var{it\_interval} è nullo il timer si ferma. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} +struct itimerval { + struct timeval it_interval; /* next value */ + struct timeval it_value; /* current value */ +}; +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* microseconds */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \var{itimerval}, che definisce i valori dei timer di + sistema.} + \label{fig:sig_itimerval} +\end{figure} + +L'uso di \func{setitimer} consente dunque un controllo completo di tutte le +caratteristiche dei timer, ed in effetti la stessa \func{alarm}, benché +definita direttamente nello standard POSIX.1, può a sua volta essere espressa +in termini di \func{setitimer}, come evidenziato dal manuale delle \acr{glibc} +\cite[glibc] che ne riporta la definizione in \figref{fig:sig_alarm_def}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} +unsigned int alarm(unsigned int seconds) +{ + struct itimerval old, new; + new.it_interval.tv_usec = 0; + new.it_interval.tv_sec = 0; + new.it_value.tv_usec = 0; + new.it_value.tv_sec = (long int) seconds; + if (setitimer(ITIMER_REAL, &new, &old) < 0) + return 0; + else + return old.it_value.tv_sec; +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Definizione di \func{alarm} in termini di \func{setitimer}.} + \label{fig:sig_alarm_def} +\end{figure} + +Si deve comunque tenere presente che la precisione di queste funzioni è +limitata da quella del timer di sistema (in genere 10~ms). Il sistema assicura +comunque che il segnale non sarà mai generato prima della scadenza programmata +(l'arrotondamento cioè è sempre effettuato per eccesso). Una seconda causa di +potenziali ritardi è che il segnale viene generato alla scandenza del timer, +ma poi deve essere consegnato; se il processo è attivo (questo è sempre vero +per \macro{ITIMER\_VIRT}) la consegna è immediata, altrimenti può esserci un +ulteriore ritardo che può variare a seconda del carico del sistema. + +Dato che sia \func{alarm} che \func{setitimer} non consentono di leggere il +valore corrente di un timer senza modificarlo, è possibile usare la funzione +\func{getitimer}, il cui prototipo è: +\begin{prototype}{sys/time.h}{int getitimer(int which, struct + itimerval *value)} + + Legge in \param{value} il valore del timer specificato da \func{which}. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore e restituisce gli stessi errori di \func{getitimer}} +\end{prototype} +\noindent i cui parametri hanno lo stesso significato e formato di quelli di +\func{setitimer}. + + +L'ultima funzione che permette l'invio diretto di un segnale è \func{abort}; +che, come accennato in \ref{sec:proc_termination}, permette di abortire +l'esecuzione di un programma tramite l'invio di \macro{SIGABRT}. Il suo +prototipo è: +\begin{prototype}{stdlib.h}{void abort(void)} + + Abortisce il processo corrente. + + \bodydesc{La funzione non ritorna, il processo è terminato inviando il + segnale di \macro{SIGABRT}.} +\end{prototype} + +La differenza fra questa funzione e l'uso di \func{raise} è che anche se il +segnale è bloccato o ignorato, la funzione ha effetto lo stesso. Il segnale +può però essere intercettato per effettuare eventuali operazioni di chiusura +prima della terminazione del processo. + +Lo standard ANSI C richiede inoltre che anche se il manipolatore ritorna, la +funzione non ritorni comunque. Lo standard POSIX.1 va oltre e richiede che se +il processo non viene terminato direttamente dal manipolatore sia la stessa +\func{abort} a farlo al ritorno dello stesso. Inoltre, sempre seguendo lo +standard POSIX, prima della terminazione tutti i file aperti e gli stream +saranno chiusi ed i buffer scaricati su disco. Non verranno invece eseguite le +funzioni registrate con \func{at\_exit} e \func{on\_exit}. + \subsection{Le funzioni \func{pause} e \func{sleep}} \label{sec:sig_pause_sleep} +Il metodo tradizionale per fare attendere ad un processo fino all'arrivo di un +segnale è quello di usare la funzione \func{pause}, il cui prototipo è: +\begin{prototype}{unistd.h}{int pause(void)} + + Pone il processo in stato di sleep fino al ritorno di un manipolatore. + + \bodydesc{La funzione ritorna solo dopo che un segnale è stato ricevuto ed + il relativo manipilatore è ritornato, nel qual caso restituisce -1 e setta + \var{errno} a \macro{EINTR}.} +\end{prototype} -- 2.30.2