In particolare ci concentreremo sulle modalità con le quali il protocollo dà
inizio e conclude una connessione; faremo inoltre anche un breve accenno al
significato di alcuni dei vari stati che il protocollo assume durante la vita
-di una connessione, che possono essere osservati per ciascun socket attivo con
-l'uso del programma \cmd{netstat}.
+di una connessione, che possono essere osservati per ciascun
+socket\index{socket} attivo con l'uso del programma \cmd{netstat}.
\subsection{La creazione della connessione: il \textit{three way handshake}}
\label{sec:TCPel_conn_cre}
Una descrizione completa del funzionamento del protocollo va al di là degli
obiettivi di questo libro; un approfondimento sugli aspetti principali si
trova in \capref{cha:tcp_protocol}, ma per una trattazione esauriente il
-miglior riferimento resta (FIXME citare lo Stevens); qui ci limiteremo a
-descrivere brevemente un semplice esempio di connessione e le transizioni che
-avvengono nei due casi appena citati (creazione e terminazione della
-connessione).
+miglior riferimento resta \cite{TCPIll1}; qui ci limiteremo a descrivere
+brevemente un semplice esempio di connessione e le transizioni che avvengono
+nei due casi appena citati (creazione e terminazione della connessione).
In assenza di connessione lo stato del TCP è \texttt{CLOSED}; quando una
applicazione esegue una apertura attiva il TCP emette un SYN e lo stato
funzione \func{rresvport} assegnando al socket una porta libera
nell'intervallo fra 512 e 1023.
-Data una connessione TCP si suole chiamare \textit{socket pair} la
-combinazione dei quattro numeri che definiscono i due capi della connessione e
-cioè l'indirizzo IP locale e la porta TCP locale, e l'indirizzo IP remoto e la
-porta TCP remota; questa combinazione, che scriveremo usando una notazione del
-tipo (195.110.112.152:22, 192.84.146.100:20100), identifica univocamente una
-connessione su internet. Questo concetto viene di solito esteso anche a UDP,
+Data una connessione TCP si suole chiamare \textit{socket pair}\footnote{da
+ non confondere con la coppia di socket della omonima funzione
+ \func{socketpair} che fanno riferimento ad una coppia di socket sulla stessa
+ macchina, non ai capi di una connessione TCP.} la combinazione dei quattro
+numeri che definiscono i due capi della connessione e cioè l'indirizzo IP
+locale e la porta TCP locale, e l'indirizzo IP remoto e la porta TCP remota;
+questa combinazione, che scriveremo usando una notazione del tipo
+(195.110.112.152:22, 192.84.146.100:20100), identifica univocamente una
+connessione su internet. Questo concetto viene di solito esteso anche a UDP,
benché in questo caso non abbia senso parlare di connessione. L'utilizzo del
programma \cmd{netstat} permette di visualizzare queste informazioni nei campi
\textit{Local Address} e \textit{Foreing Address}.
Abbiamo visto in \secref{sec:sig_gen_beha}, affrontando la suddivisione fra
\textit{fast} e \textit{slow} system call, che in certi casi le funzioni di
I/O possono bloccarsi indefinitamente.\footnote{si ricordi però che questo può
- accadere solo per le pipe, i socket ed alcuni file di dispositivo; sui file
- normali le funzioni di lettura e scrittura ritornano sempre subito.} Ad
-esempio le operazioni di lettura possono bloccarsi quando non ci sono dati
-disponibili sul descrittore su cui si sta operando.
+ accadere solo per le pipe, i socket\index{socket} ed alcuni file di
+ dispositivo\index{file!di dispositivo}; sui file normali le funzioni di
+ lettura e scrittura ritornano sempre subito.} Ad esempio le operazioni di
+lettura possono bloccarsi quando non ci sono dati disponibili sul descrittore
+su cui si sta operando.
Questo comportamento causa uno dei problemi più comuni che ci si trova ad
affrontare nelle operazioni di I/O, che è quello che si verifica quando si
mentre si è bloccati su uno di essi su di un'altro potrebbero essere presenti
dei dati; così che nel migliore dei casi si avrebbe una lettura ritardata
inutilmente, e nel peggiore si potrebbe addirittura arrivare ad un
-\textit{deadlock}.
+\textit{deadlock}\index{deadlock}.
Abbiamo già accennato in \secref{sec:file_open} che è possibile prevenire
questo tipo di comportamento aprendo un file in modalità
l'accesso diventi possibile. Il primo ad introdurre questa modalità di
operazione, chiamata usualmente \textit{I/O multiplexing}, è stato
BSD,\footnote{la funzione è apparsa in BSD4.2 e standardizzata in BSD4.4, ma è
- stata portata su tutti i sistemi che supportano i \textit{socket}, compreso
- le varianti di System V.} con la funzione \func{select}, il cui prototipo
-è:
+ stata portata su tutti i sistemi che supportano i
+ \textit{socket}\index{socket}, compreso le varianti di System V.} con la
+funzione \func{select}, il cui prototipo è:
\begin{functions}
\headdecl{sys/time.h}
\headdecl{sys/types.h}
attivi.
Benché la modalità di apertura asincrona di un file possa risultare utile in
-varie occasioni (in particolar modo con i socket e gli altri file per i quali
-le funzioni di I/O sono system call lente), essa è comunque limitata alla
-notifica della disponibilità del file descriptor per le operazioni di I/O, e
-non ad uno svolgimento asincrono delle medesime. Lo standard POSIX.1b
-definisce anche una interfaccia apposita per l'I/O asincrono, che prevede un
-insieme di funzioni dedicate, completamente separate rispetto a quelle usate
-normalmente.
+varie occasioni (in particolar modo con i socket\index{socket} e gli altri
+file per i quali le funzioni di I/O sono system call lente), essa è comunque
+limitata alla notifica della disponibilità del file descriptor per le
+operazioni di I/O, e non ad uno svolgimento asincrono delle medesime. Lo
+standard POSIX.1b definisce anche una interfaccia apposita per l'I/O
+asincrono, che prevede un insieme di funzioni dedicate, completamente separate
+rispetto a quelle usate normalmente.
In generale questa interfaccia è completamente astratta e può essere
implementata sia direttamente nel kernel, che in user space attraverso l'uso
\section{Il file locking}
\label{sec:file_locking}
+\index{file!locking|(}
In \secref{sec:file_sharing} abbiamo preso in esame le modalità in cui un
sistema unix-like gestisce la condivisione dei file da parte di processi
diversi. In quell'occasione si è visto come, con l'eccezione dei file aperti
In generale si distinguono due tipologie di \textit{file lock}:\footnote{di
seguito ci riferiremo sempre ai blocchi di accesso ai file con la
nomenclatura inglese di \textit{file lock}, o più brevemente con
- \textit{lock}, per evitare confuzioni linguistiche con il blocco di un
+ \textit{lock}, per evitare confusioni linguistiche con il blocco di un
processo (cioè la condizione in cui il processo viene posto in stato di
\textit{sleep}).} la prima è il cosiddetto \textit{shared lock}, detto anche
\textit{read lock} in quanto serve a bloccare l'accesso in scrittura su un
Si tenga presente infine che il controllo di accesso è effettuato quando si
apre un file, l'unico controllo residuo è che il tipo di lock che si vuole
-otternere deve essere compatibile con le modalità di apertura dello stesso (di
+ottenere deve essere compatibile con le modalità di apertura dello stesso (di
lettura per un read lock e di scrittura per un write lock).
%% Si ricordi che
fondamentale da capire è che un lock, qualunque sia l'interfaccia che si usa,
anche se richiesto attraverso un file descriptor, agisce sempre su un file;
perciò le informazioni relative agli eventuali \textit{file lock} sono
-mantenute a livello di inode,\footnote{in particolare, come accennato in
- \figref{fig:file_flock_struct}, i \textit{file lock} sono mantenuti un una
- \textit{linked list}\index{linked list} di strutture \var{file\_lock}. La
- lista è referenziata dall'indirizzo di partenza mantenuto dal campo
- \var{i\_flock} della struttura \var{inode} (per le definizioni esatte si
- faccia riferimento al file \file{fs.h} nei sorgenti del kernel). Un bit del
- campo \var{fl\_flags} di specifica se si tratta di un lock in semantica BSD
- (\const{FL\_FLOCK}) o POSIX (\const{FL\_POSIX}).} dato che questo è l'unico
-riferimento in comune che possono avere due processi diversi che aprono lo
-stesso file.
+mantenute a livello di inode\index{inode},\footnote{in particolare, come
+ accennato in \figref{fig:file_flock_struct}, i \textit{file lock} sono
+ mantenuti un una \textit{linked list}\index{linked list} di strutture
+ \var{file\_lock}. La lista è referenziata dall'indirizzo di partenza
+ mantenuto dal campo \var{i\_flock} della struttura \var{inode} (per le
+ definizioni esatte si faccia riferimento al file \file{fs.h} nei sorgenti
+ del kernel). Un bit del campo \var{fl\_flags} di specifica se si tratta di
+ un lock in semantica BSD (\const{FL\_FLOCK}) o POSIX (\const{FL\_POSIX}).}
+dato che questo è l'unico riferimento in comune che possono avere due processi
+diversi che aprono lo stesso file.
\begin{figure}[htb]
\centering
\item[\errcode{EDEADLK}] Si è richiesto un lock su una regione bloccata da
un altro processo che è a sua volta in attesa dello sblocco di un lock
mantenuto dal processo corrente; si avrebbe pertanto un
- \textit{deadlock}. Non è garantito che il sistema riconosca sempre
- questa situazione.
+ \textit{deadlock}\index{deadlock}. Non è garantito che il sistema
+ riconosca sempre questa situazione.
\item[\errcode{EINTR}] La funzione è stata interrotta da un segnale prima
di poter acquisire un lock.
\end{errlist}
\begin{figure}[htb]
\centering \includegraphics[width=9cm]{img/file_lock_dead}
- \caption{Schema di una situazione di \textit{deadlock}.}
+ \caption{Schema di una situazione di \textit{deadlock}\index{deadlock}.}
\label{fig:file_flock_dead}
\end{figure}
porta ad un \textit{deadlock}\index{deadlock}, dato che a quel punto anche il
processo 2 si bloccherebbe, e niente potrebbe sbloccare l'altro processo. Per
questo motivo il kernel si incarica di rilevare situazioni di questo tipo, ed
-impedirle restituendo un errore di \errcode{EDEADLK} alla funzione che cerca di
-acquisire un lock che porterebbe ad un \textit{deadlock}.
+impedirle restituendo un errore di \errcode{EDEADLK} alla funzione che cerca
+di acquisire un lock che porterebbe ad un \textit{deadlock}.
\begin{figure}[!bht]
\centering \includegraphics[width=13cm]{img/file_posix_lock}
\var{fl\_start} e \var{fl\_end}. La struttura è comunque la stessa, solo
che in questo caso nel campo \var{fl\_flags} è impostato il bit
\const{FL\_POSIX} ed il campo \var{fl\_file} non viene usato.} il lock è
-sempre associato all'inode, solo che in questo caso la titolarità non viene
-identificata con il riferimento ad una voce nella file table, ma con il valore
-del \acr{pid} del processo.
+sempre associato all'inode\index{inode}, solo che in questo caso la titolarità
+non viene identificata con il riferimento ad una voce nella file table, ma con
+il valore del \acr{pid} del processo.
Quando si richiede un lock il kernel effettua una scansione di tutti i lock
presenti sul file\footnote{scandisce cioè la linked list delle strutture
stati creati usando altri file descriptor che restano aperti.
Dato che il controllo sull'accesso ai lock viene eseguito sulla base del
-\acr{pid} del processo, possiamo anche prendere in cosiderazione un'altro
+\acr{pid} del processo, possiamo anche prendere in considerazione un'altro
degli aspetti meno chiari di questa interfaccia e cioè cosa succede quando si
richiedono dei lock su regioni che si sovrappongono fra loro all'interno
stesso processo. Siccome il controllo, come nel caso della rimozione, si basa
Nel caso della semantica BSD, essendo i lock relativi a tutto un file e non
accumulandosi,\footnote{questa ultima caratteristica è vera in generale, se
- cioè si richiede più volte lo stesso file lock, o più lock sula stessa
+ cioè si richiede più volte lo stesso file lock, o più lock sulla stessa
sezione di file, le richieste non si cumulano e basta una sola richiesta di
rilascio per cancellare il lock.} la cosa non ha alcun effetto; la funzione
ritorna con successo, senza che il kernel debba modificare la lista dei lock.
\end{verbatim}%$
\end{minipage}\vspace{1mm}
\par\noindent
-come ci aspettimo questo non sarà consentito.
+come ci aspettiamo questo non sarà consentito.
Il programma di norma esegue il tentativo di acquisire il lock in modalità non
bloccante, se però usiamo l'opzione \cmd{-b} possiamo impostare la modalità
\end{minipage}\vspace{1mm}
\par\noindent
il primo comando acquisisce subito un read lock, e quindi non cambia nulla, ma
-se proviamo adesso a richidere un write lock che non potrà essere acquisito
+se proviamo adesso a richiedere un write lock che non potrà essere acquisito
otterremo:
\vspace{1mm}
comportamento non ha molto senso, dato che comunque qualunque accesso
diretto al file è consentito.} in Linux è stata però fatta la scelta
implementativa\footnote{per i dettagli si possono leggere le note relative
- all'implementazione, mantenute insime ai sorgenti del kernel nel file
+ all'implementazione, mantenute insieme ai sorgenti del kernel nel file
\file{Documentation/mandatory.txt}.} di seguire questo comportamento
soltanto quando si chiama \func{mmap} con l'opzione \const{MAP\_SHARED} (nel
qual caso la funzione fallisce con il solito \errcode{EAGAIN}) che comporta la
possibilità di modificare il file.
+\index{file!locking|)}
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 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
-\textit{hard link}). Il prototipo della funzione e le sue caratteristiche
-principali, come risultano dalla pagina di manuale, sono le seguenti:
+altrettante diverse associazioni allo stesso inode\index{inode}; si noti poi
+che nessuno di questi nomi viene ad assumere una particolare preferenza o
+originalità rispetto agli altri.
+
+Per aggiungere un nome ad un inode\index{inode} si utilizza la funzione
+\func{link}; si suole chiamare questo tipo di associazione un collegamento
+diretto (o \textit{hard link}). Il prototipo della funzione e le sue
+caratteristiche principali, come risultano dalla pagina di manuale, sono le
+seguenti:
\begin{prototype}{unistd.h}
{int link(const char *oldpath, const char *newpath)}
Crea un nuovo collegamento diretto al file indicato da \var{oldpath}
suo prototipo è il seguente:
\begin{prototype}{unistd.h}{int unlink(const char *pathname)}
Cancella il nome specificato dal pathname nella relativa directory e
- decrementa il numero di riferimenti nel relativo inode. Nel caso di link
- simbolico cancella il link simbolico; nel caso di socket, fifo o file di
- dispositivo rimuove il nome, ma come per i file i processi che hanno aperto
- uno di questi oggetti possono continuare ad utilizzarlo.
+ decrementa il numero di riferimenti nel relativo inode\index{inode}. Nel
+ caso di link simbolico cancella il link simbolico; nel caso di
+ socket\index{socket}, fifo o file di dispositivo\index{file!di dispositivo}
+ rimuove il nome, ma come per i file i processi che hanno aperto uno di
+ questi oggetti possono continuare ad utilizzarlo.
\bodydesc{La funzione restituisce zero in caso di successo e -1 per un
errore, nel qual caso il file non viene toccato. La variabile
proprietari del file o proprietari della directory (o root, per cui nessuna
delle restrizioni è applicata).
-Una delle caratteristiche di queste funzioni è che la creazione/rimozione
-del nome dalla directory e l'incremento/decremento del numero di riferimenti
-nell'inode devono essere effettuati in maniera atomica (si veda
+Una delle caratteristiche di queste funzioni è che la creazione/rimozione del
+nome dalla directory e l'incremento/decremento del numero di riferimenti
+nell'inode\index{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
- count} mantenuto nell'inode diventa zero lo spazio occupato viene rimosso. A
-questo però si aggiunge un'altra condizione, e cioè che non ci siano processi
-che abbiano detto file aperto.
+ count} mantenuto nell'inode\index{inode} diventa zero lo spazio occupato
+viene rimosso. A 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
temporanei su disco in caso di crash dei programmi; la tecnica è quella di
\label{sec:file_symlink}
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.
+riferimenti agli inode\index{inode}, pertanto può funzionare soltanto per file
+che 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.
\end{prototype}
La modalità con cui avviene la cancellazione è analoga a quella di
-\func{unlink}: fintanto che il numero di link all'inode della directory non
-diventa nullo e nessun processo ha la directory aperta lo spazio occupato su
-disco non viene rilasciato. Se un processo ha la directory aperta la funzione
-rimuove il link all'inode e nel caso sia l'ultimo, pure le voci standard
-\file{.} e \file{..}, a questo punto il kernel non consentirà di creare più
-nuovi file nella directory.
+\func{unlink}: fintanto che il numero di link all'inode\index{inode} della
+directory non diventa nullo e nessun processo ha la directory aperta lo spazio
+occupato su disco non viene rilasciato. Se un processo ha la directory aperta
+la funzione rimuove il link all'inode\index{inode} e nel caso sia l'ultimo,
+pure le voci standard \file{.} e \file{..}, a questo punto il kernel non
+consentirà di creare più nuovi file nella directory.
\subsection{La creazione di file speciali}
Finora abbiamo parlato esclusivamente di file, directory e link simbolici; in
\secref{sec:file_file_types} abbiamo visto però che il sistema prevede pure
-degli altri tipi di file speciali, come i file di dispositivo e le fifo (i
-socket sono un caso a parte, che vedremo in \capref{cha:socket_intro}).
+degli altri tipi di file speciali, come i file di dispositivo
+\index{file!di dispositivo}
+e le fifo (i socket\index{socket} sono un caso a parte, che
+vedremo in \capref{cha:socket_intro}).
La manipolazione delle caratteristiche di questi file e la loro cancellazione
può essere effettuata con le stesse funzioni che operano sui file regolari; ma
codici di errore.} l'uso per la creazione di una fifo è consentito anche
agli utenti normali.
-I nuovi inode creati con \func{mknod} apparterranno al proprietario e al
-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.
+I nuovi inode\index{inode} creati con \func{mknod} apparterranno al
+proprietario e al 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\index{inode}.
Per creare una fifo (un file speciale, su cui torneremo in dettaglio in
\secref{sec:ipc_named_pipe}) lo standard POSIX specifica l'uso della funzione
\secref{sec:proc_fork}), la directory corrente della shell diventa anche la
directory corrente di qualunque comando da essa lanciato.
-In genere il kernel tiene traccia per ciascun processo dell'inode della
-directory di lavoro corrente, per ottenere il pathname occorre usare una
-apposita funzione di libreria, \func{getcwd}, il cui prototipo è:
+In genere il kernel tiene traccia per ciascun processo dell'inode\index{inode}
+della directory di lavoro corrente, per ottenere il pathname occorre usare una
+apposita funzione di libreria, \func{getcwd}, il cui prototipo è:
\begin{prototype}{unistd.h}{char *getcwd(char *buffer, size\_t size)}
Restituisce il filename completo della directory di lavoro corrente nella
stringa puntata da \var{buffer}, che deve essere precedentemente
\section{La manipolazione delle caratteristiche dei files}
\label{sec:file_infos}
-Come spiegato in \secref{sec:file_filesystem} tutte le informazioni
-generali relative alle caratteristiche di ciascun file, a partire dalle
-informazioni relative al controllo di accesso, sono mantenute nell'inode.
+Come spiegato in \secref{sec:file_filesystem} tutte le informazioni generali
+relative alle caratteristiche di ciascun file, a partire dalle informazioni
+relative al controllo di accesso, sono mantenute nell'inode\index{inode}.
Vedremo in questa sezione come sia possibile leggere tutte queste informazioni
usando la funzione \func{stat}, che permette l'accesso a tutti i dati
-memorizzati nell'inode; esamineremo poi le varie funzioni usate per manipolare
-tutte queste informazioni (eccetto quelle che riguardano la gestione del
-controllo di accesso, trattate in in \secref{sec:file_access_control}).
+memorizzati nell'inode\index{inode}; esamineremo poi le varie funzioni usate
+per manipolare tutte queste informazioni (eccetto quelle che riguardano la
+gestione del controllo di accesso, trattate in in
+\secref{sec:file_access_control}).
\subsection{Le funzioni \func{stat}, \func{fstat} e \func{lstat}}
Dato che il valore numerico può variare a seconda delle implementazioni, lo
standard POSIX definisce un insieme di macro per verificare il tipo di file,
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 \tabref{tab:file_type_macro}.
+standard per i link simbolici e i socket\index{socket} definite da BSD;
+l'elenco completo delle macro con cui è possibile estrarre l'informazione da
+\var{st\_mode} è riportato in \tabref{tab:file_type_macro}.
\begin{table}[htb]
\centering
\footnotesize
\hline
\macro{S\_ISREG(m)} & file regolare \\
\macro{S\_ISDIR(m)} & directory \\
- \macro{S\_ISCHR(m)} & device a caratteri \\
- \macro{S\_ISBLK(m)} & device a blocchi\\
+ \macro{S\_ISCHR(m)} & dispositivo a caratteri \\
+ \macro{S\_ISBLK(m)} & dispositivo a blocchi\\
\macro{S\_ISFIFO(m)} & fifo \\
\macro{S\_ISLNK(m)} & link simbolico \\
- \macro{S\_ISSOCK(m)} & socket \\
+ \macro{S\_ISSOCK(m)} & socket\index{socket} \\
\hline
\end{tabular}
\caption{Macro per i tipi di file (definite in \texttt{sys/stat.h}).}
\hline
\hline
\const{S\_IFMT} & 0170000 & bitmask per i bit del tipo di file \\
- \const{S\_IFSOCK} & 0140000 & socket \\
+ \const{S\_IFSOCK} & 0140000 & socket\index{socket} \\
\const{S\_IFLNK} & 0120000 & link simbolico \\
\const{S\_IFREG} & 0100000 & file regolare \\
- \const{S\_IFBLK} & 0060000 & device a blocchi \\
+ \const{S\_IFBLK} & 0060000 & dispositivo a blocchi \\
\const{S\_IFDIR} & 0040000 & directory \\
- \const{S\_IFCHR} & 0020000 & device a caratteri \\
+ \const{S\_IFCHR} & 0020000 & dispositivo a caratteri \\
\const{S\_IFIFO} & 0010000 & fifo \\
\hline
\const{S\_ISUID} & 0004000 & set UID bit \\
\func{ftruncate} si hanno i valori:
\begin{errlist}
\item[\errcode{EBADF}] \var{fd} non è un file descriptor.
- \item[\errcode{EINVAL}] \var{fd} è un riferimento ad un socket, non a un file
- o non è aperto in scrittura.
+ \item[\errcode{EINVAL}] \var{fd} è un riferimento ad un
+ socket\index{socket}, non a un file o non è aperto in scrittura.
\end{errlist}
per \func{truncate} si hanno:
\begin{errlist}
\label{sec:file_file_times}
Il sistema mantiene per ciascun file tre tempi. Questi sono registrati
-nell'inode insieme agli altri attributi del file e possono essere letti
-tramite la funzione \func{stat}, che li restituisce attraverso tre campi della
-struttura \var{stat} di \figref{fig:file_stat_struct}. Il significato di detti
-tempi e dei relativi campi è riportato nello schema in
+nell'inode\index{inode} insieme agli altri attributi del file e possono essere
+letti tramite la funzione \func{stat}, che li restituisce attraverso tre campi
+della struttura \var{stat} di \figref{fig:file_stat_struct}. Il significato di
+detti tempi e dei relativi campi è riportato nello schema in
\tabref{tab:file_file_times}, dove è anche riportato un esempio delle funzioni
che effettuano cambiamenti su di essi.
modifica (il \textit{modification time} \var{st\_mtime}) e il tempo di
cambiamento di stato (il \textit{change time} \var{st\_ctime}). Il primo
infatti fa riferimento ad una modifica del contenuto di un file, mentre il
-secondo ad una modifica dell'inode; siccome esistono molte operazioni (come la
-funzione \func{link} e molte altre che vedremo in seguito) che modificano solo
-le informazioni contenute nell'inode senza toccare il file, diventa necessario
-l'utilizzo di un altro tempo.
+secondo ad una modifica dell'inode\index{inode}; siccome esistono molte
+operazioni (come la funzione \func{link} e molte altre che vedremo in seguito)
+che modificano solo le informazioni contenute nell'inode\index{inode} senza
+toccare il file, diventa necessario l'utilizzo di un altro tempo.
-Il sistema non tiene conto dell'ultimo accesso all'inode, pertanto funzioni
-come \func{access} o \func{stat} non hanno alcuna influenza sui tre tempi. Il
-tempo di ultimo accesso (ai dati) viene di solito usato per cancellare i file
-che non servono più dopo un certo lasso di tempo (ad esempio \cmd{leafnode}
-cancella i vecchi articoli sulla base di questo tempo).
+Il sistema non tiene conto dell'ultimo accesso all'inode\index{inode},
+pertanto funzioni come \func{access} o \func{stat} non hanno alcuna influenza
+sui tre tempi. Il tempo di ultimo accesso (ai dati) viene di solito usato per
+cancellare i file che non servono più dopo un certo lasso di tempo (ad esempio
+\cmd{leafnode} cancella i vecchi articoli sulla base di questo tempo).
Il tempo di ultima modifica invece viene usato da \cmd{make} per decidere
quali file necessitano di essere ricompilati o (talvolta insieme anche al
\begin{prototype}{utime.h}
{int utime(const char *filename, struct utimbuf *times)}
-Cambia i tempi di ultimo accesso e modifica dell'inode specificato da
-\param{filename} secondo i campi \var{actime} e \var{modtime} di
-\param{times}. Se questa è \val{NULL} allora viene usato il tempo corrente.
+Cambia i tempi di ultimo accesso e modifica dell'inode\index{inode}
+specificato da \param{filename} secondo i campi \var{actime} e \var{modtime}
+di \param{times}. Se questa è \val{NULL} allora viene usato il tempo corrente.
\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
errore, nel qual caso \var{errno} assumerà uno dei valori:
Si tenga presente che non è comunque possibile specificare il tempo di
cambiamento di stato del file, che viene comunque cambiato dal kernel tutte le
-volte che si modifica l'inode (quindi anche alla chiamata di \func{utime}).
-Questo serve anche come misura di sicurezza per evitare che si possa
-modificare un file nascondendo completamente le proprie tracce. In realtà la
-cosa resta possibile, se si è in grado di accedere al file di dispositivo,
-scrivendo direttamente sul disco senza passare attraverso il filesystem, ma
-ovviamente in questo modo la cosa è molto più complicata da realizzare.
+volte che si modifica l'inode\index{inode} (quindi anche alla chiamata di
+\func{utime}). Questo serve anche come misura di sicurezza per evitare che si
+possa modificare un file nascondendo completamente le proprie tracce. In
+realtà la cosa resta possibile, se si è in grado di accedere al file di
+dispositivo, scrivendo direttamente sul disco senza passare attraverso il
+filesystem, ma ovviamente in questo modo la cosa è molto più complicata da
+realizzare.
\end{figure}
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 di nuovo
-\figref{fig:file_stat_struct}).
+memorizzati nell'inode\index{inode}; in particolare essi sono contenuti in
+alcuni bit del 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 privilegi usando le lettere
\cmd{u} (per \textit{user}), \cmd{g} (per \textit{group}) e \cmd{o} (per
Questo significa che si può accedere a qualunque periferica del computer,
dalla seriale, alla parallela, alla console, e agli stessi dischi attraverso i
-cosiddetti file di dispositivo (i \textit{device file}). Questi sono dei file
-speciali agendo sui quali i programmi possono leggere, scrivere e compiere
-operazioni direttamente sulle periferiche, usando le stesse funzioni che si
-usano per i normali file di dati.
+cosiddetti file di dispositivo\index{file!di dispositivo} (i \textit{device
+ file}). Questi sono dei file speciali agendo sui quali i programmi possono
+leggere, scrivere e compiere operazioni direttamente sulle periferiche, usando
+le stesse funzioni che si usano per i normali file di dati.
In questo capitolo forniremo una descrizione dell'architettura dei file in
Linux, iniziando da una panoramica sulle caratteristiche principali delle
file vengono tenuti all'interno di un unico albero la cui radice (quella che
viene chiamata \textit{root directory}) viene montata all'avvio. Un file
viene identificato dall'utente usando quello che viene chiamato
-\textit{pathname}\footnote{il manuale della \acr{glibc} depreca questa
- nomenclatura, che genererebbe confusione poiché \textit{path} indica anche
- un insieme di directory su cui effettuare una ricerca (come quello in cui si
- cercano i comandi). Al suo posto viene proposto l'uso di \textit{filename} e
- di componente per il nome del file all'interno della directory. Non
- seguiremo questa scelta dato che l'uso della parola \textit{pathname} è
- ormai così comune che mantenerne l'uso è senz'altro più chiaro
- dell'alternativa proposta.}, cioè il percorso che si deve fare per accedere
-al file a partire dalla \textit{root directory}, che è composto da una serie
-di nomi separati da una \file{/}.
+\textit{pathname}\index{pathname}\footnote{il manuale della \acr{glibc}
+ depreca questa nomenclatura, che genererebbe confusione poiché \textit{path}
+ indica anche un insieme di directory su cui effettuare una ricerca (come
+ quello in cui si cercano i comandi). Al suo posto viene proposto l'uso di
+ \textit{filename} e di componente per il nome del file all'interno della
+ directory. Non seguiremo questa scelta dato che l'uso della parola
+ \textit{pathname} è ormai così comune che mantenerne l'uso è senz'altro più
+ chiaro dell'alternativa proposta.}, cioè il percorso che si deve fare per
+accedere al file a partire dalla \textit{root directory}, che è composto da
+una serie di nomi separati da una \file{/}.
All'avvio del sistema, completata la fase di inizializzazione, il kernel
riceve dal boot loader l'indicazione di quale dispositivo contiene il
components}), noi li chiameremo più semplicemente \textit{nomi}.} da essa
contenuto. All'interno dello stesso albero si potranno poi inserire anche
tutti gli altri oggetti visti attraverso l'interfaccia che manipola i file
-come le fifo, i link, i socket e gli stessi file di dispositivo (questi
+come le fifo, i link, i socket\index{socket} e gli stessi file di dispositivo
+\index{file!di dispositivo} (questi
ultimi, per convenzione, sono inseriti nella directory \file{/dev}).
Il nome completo di un file viene chiamato \textit{pathname} ed il
\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
-\secref{sec:file_chroot}) è la stessa per tutti i processi ed equivale alla
-directory radice dell'albero dei file: in questo caso si parla di un
-\textsl{pathname assoluto}\index{pathname assoluto}. Altrimenti la ricerca
-parte dalla directory corrente (su cui torneremo in
+Se il \textit{pathname}\index{pathname} comincia per \file{/} la ricerca parte
+dalla directory radice del processo; questa, a meno di un \func{chroot} (su
+cui torneremo in \secref{sec:file_chroot}) è la stessa per tutti i processi ed
+equivale alla directory radice dell'albero dei file: in questo caso si parla
+di un \textsl{pathname assoluto}\index{pathname!assoluto}. Altrimenti la
+ricerca parte dalla directory corrente (su cui torneremo in
\secref{sec:file_work_dir}) ed il pathname è detto \textsl{pathname
- relativo}\index{pathname relativo}.
+ 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
base al loro contenuto, o tipo di accesso. Essa riguarda invece il tipo di
oggetti; in particolare è da notare la presenza dei cosiddetti file speciali.
Alcuni di essi, come le \textit{fifo} (che tratteremo in
-\secref{sec:ipc_named_pipe}) ed i \textit{socket} (che tratteremo in
-\capref{cha:socket_intro}) non sono altro che dei riferimenti per utilizzare
-delle funzionalità di comunicazione fornite dal kernel. Gli altri sono i
-\textsl{file di dispositivo} (o \textit{device file}) che costituiscono una
-interfaccia diretta per leggere e scrivere sui dispositivi fisici; essi
-vengono suddivisi in due grandi categorie, \textsl{a blocchi} e \textsl{a
- caratteri} a seconda delle modalità in cui il dispositivo sottostante
-effettua le operazioni di I/O.\footnote{in sostanza i dispositivi a blocchi
- (ad esempio i dischi) corrispondono a periferiche per le quali è richiesto
- che l'I/O venga effettuato per blocchi di dati di dimensioni fissate (ad
- esempio le dimensioni di un settore), mentre nei dispositivi a caratteri
- l'I/O viene effettuato senza nessuna particolare struttura.}
+\secref{sec:ipc_named_pipe}) ed i \textit{socket}\index{socket} (che
+tratteremo in \capref{cha:socket_intro}) non sono altro che dei riferimenti
+per utilizzare delle funzionalità di comunicazione fornite dal kernel. Gli
+altri sono i \textsl{file di dispositivo}\index{file!di dispositivo} (o
+\textit{device file}) che costituiscono una interfaccia diretta per leggere e
+scrivere sui dispositivi fisici; essi vengono suddivisi in due grandi
+categorie, \textsl{a blocchi} e \textsl{a caratteri} a seconda delle modalità
+in cui il dispositivo sottostante effettua le operazioni di I/O.\footnote{in
+ sostanza i dispositivi a blocchi (ad esempio i dischi) corrispondono a
+ periferiche per le quali è richiesto che l'I/O venga effettuato per blocchi
+ di dati di dimensioni fissate (ad esempio le dimensioni di un settore),
+ mentre nei dispositivi a caratteri l'I/O viene effettuato senza nessuna
+ particolare struttura.}
\begin{table}[htb]
\footnotesize
\textit{regular file} & \textsl{file regolare} &
un file che contiene dei dati (l'accezione normale di file) \\
\textit{directory} & \textsl{cartella o direttorio} &
- un file che contiene una lista di nomi associati a degli \textit{inodes}
- (vedi \secref{sec:file_vfs}). \\
+ un file che contiene una lista di nomi associati a degli
+ \textit{inode}\index{inode} (vedi \secref{sec:file_vfs}). \\
\textit{symbolic link} & \textsl{collegamento simbolico} &
un file che contiene un riferimento ad un altro file/directory \\
\textit{char device} & \textsl{dispositivo a caratteri} &
un file che identifica una periferica ad accesso a blocchi \\
\textit{fifo} & \textsl{``coda''} &
un file speciale che identifica una linea di comunicazione software
- (unidirezionale) \\
- \textit{socket} & \textsl{``presa''} &
+ unidirezionale (vedi \secref{sec:ipc_named_pipe}).\\
+ \textit{socket}\index{socket} & \textsl{``presa''} &
un file speciale che identifica una linea di comunicazione software
- (bidirezionale) \\
+ bidirezionale (vedi \capref{cha:socket_intro}) \\
\hline
\end{tabular}
\caption{Tipologia dei file definiti nel VFS}
dimensione fissa avviene solo all'interno del kernel, ed è completamente
trasparente all'utente. Inoltre talvolta si parla di \textsl{accesso
diretto} riferendosi alla capacità, che non ha niente a che fare con tutto
- ciò, di effettuare, attraverso degli appositi file di dispositivo,
- operazioni di I/O direttamente sui dischi senza passare attraverso un
- filesystem (il cosiddetto \textit{raw access}, introdotto coi kernel della
- serie 2.4.x).}
+ ciò, di effettuare, attraverso degli appositi file di
+ dispositivo\index{file!di dispositivo}, operazioni di I/O direttamente sui
+ dischi senza passare attraverso un filesystem (il cosiddetto \textit{raw
+ access}, introdotto coi kernel della serie 2.4.x).}
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 è
bufferizzato in quanto la lettura e la scrittura vengono eseguite chiamando
direttamente le system call del kernel (in realtà il kernel effettua al suo
interno alcune bufferizzazioni per aumentare l'efficienza nell'accesso ai
-dispositivi); i \textit{file descriptor}\index{file descriptor} sono
+dispositivi); i \textit{file descriptor}\index{file!descriptor} sono
rappresentati da numeri interi (cioè semplici variabili di tipo \ctyp{int}).
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 fornisce funzioni più evolute e un accesso
-bufferizzato (controllato dalla implementazione fatta dalle \acr{glibc}), la
-tratteremo in dettaglio nel \capref{cha:files_std_interface}.
+\textit{stream}\index{file!stream}. Essa fornisce 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
-tipo \ctyp{FILE *}. L'interfaccia è definita nell'header \file{stdio.h}.
+anche su tutti i sistemi non Unix. Gli \textit{stream}\index{file!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 tipo \ctyp{FILE *}. L'interfaccia è definita nell'header
+\file{stdio.h}.
Entrambe le interfacce possono essere usate per l'accesso ai file come agli
-altri oggetti del VFS (fifo, socket, device, sui quali torneremo in dettaglio
-a tempo opportuno), ma per poter accedere alle operazioni di controllo
-(descritte in \ref{sec:file_fcntl} e \ref{sec:file_ioctl}) su un qualunque
-tipo di oggetto del VFS occorre usare l'interfaccia standard di Unix 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}).
+altri oggetti del VFS (fifo, socket\index{socket}, device, sui quali torneremo
+in dettaglio a tempo opportuno), ma per poter accedere alle operazioni di
+controllo (descritte in \secref{sec:file_fcntl} e \secref{sec:file_ioctl}) su
+un qualunque tipo di oggetto del VFS occorre usare l'interfaccia standard di
+Unix con i \textit{file descriptor}. Allo stesso modo devono essere usati i
+\textit{file descriptor}\index{file!descriptor} se si vuole ricorrere a
+modalità speciali di I/O come il \textit{file locking}\index{file!locking} o
+l'I/O non-bloccante (vedi \capref{cha:file_advanced}).
Gli \textit{stream} forniscono un'interfaccia di alto livello costruita sopra
quella dei \textit{file descriptor}, che permette di poter scegliere tra
è che l'interfaccia per le operazioni di input/output è enormemente più ricca
di quella dei \textit{file descriptor}, che forniscono solo funzioni
elementari per la lettura/scrittura diretta di blocchi di byte. In
-particolare gli \textit{stream} dispongono di tutte le funzioni di
-formattazione per l'input e l'output adatte per manipolare anche i dati in
-forma di linee o singoli caratteri.
+particolare gli \textit{stream}\index{file!stream} dispongono di tutte le
+funzioni di formattazione per l'input e l'output adatte per manipolare anche i
+dati in forma di linee o singoli caratteri.
In ogni caso, dato che gli stream sono implementati sopra l'interfaccia
standard di Unix, è sempre possibile estrarre il \textit{file descriptor} da
uno stream ed eseguirvi operazioni di basso livello, o associare in un secondo
-tempo uno \textit{stream} ad un \textit{file descriptor}.
+tempo uno \textit{stream}\index{file!stream} ad un \textit{file
+ descriptor}\index{file!descriptor}.
In generale, se non necessitano specificatamente le funzionalità di basso
-livello, è opportuno usare sempre gli \textit{stream} per la loro maggiore
-portabilità, essendo questi ultimi definiti nello standard ANSI C;
-l'interfaccia con i \textit{file descriptor} infatti segue solo lo standard
-POSIX.1 dei sistemi Unix, ed è pertanto di portabilità più limitata.
+livello, è opportuno usare sempre gli \textit{stream}\index{file!stream} per
+la loro maggiore portabilità, essendo questi ultimi definiti nello standard
+ANSI C; l'interfaccia con i \textit{file descriptor}\index{file!descriptor}
+infatti segue solo lo standard POSIX.1 dei sistemi Unix, ed è pertanto di
+portabilità più limitata.
% \subsection{Caratteristiche specifiche dei file in Unix}
Il VFS definisce un insieme di funzioni che tutti i filesystem devono
implementare. L'interfaccia comprende tutte le funzioni che riguardano i file;
le operazioni sono suddivise su tre tipi di oggetti: \textit{filesystem},
-\textit{inode} e \textit{file}, corrispondenti a tre apposite strutture
-definite nel kernel.
+\textit{inode}\index{inode} e \textit{file}, corrispondenti a tre apposite
+strutture definite nel kernel.
Il VFS usa una tabella mantenuta dal kernel che contiene il nome di ciascun
filesystem supportato: quando si vuole inserire il supporto di un nuovo
su cui è strutturata l'interfaccia. Ciascuno di essi contiene le informazioni
relative al file in uso, insieme ai puntatori alle funzioni dello specifico
filesystem usate per l'accesso dal VFS; in particolare il descrittore
-dell'inode contiene i puntatori alle funzioni che possono essere usate su
-qualunque file (come \func{link}, \func{stat} e \func{open}), mentre il
-descrittore di file contiene i puntatori alle funzioni che vengono usate sui
-file già aperti.
+dell'inode\index{inode} contiene i puntatori alle funzioni che possono essere
+usate su qualunque file (come \func{link}, \func{stat} e \func{open}), mentre
+il descrittore di file contiene i puntatori alle funzioni che vengono usate
+sui file già aperti.
\subsection{Il funzionamento del VFS}
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 (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.
+\textit{inode}\index{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\index{file!di dispositivo}, o una 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 \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}.
+vengono usate per motivi di velocità, gli \textit{inode}\index{inode} invece
+stanno su disco e vengono copiati in memoria quando serve, ed ogni cambiamento
+viene copiato all'indietro sul disco, gli inode\index{inode} che stanno in
+memoria sono inode\index{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 è
parziale (la \textit{dcache} cioè contiene solo le \textit{dentry} per i file
per i quali è stato richiesto l'accesso), quando si vuole risolvere un nuovo
-pathname il VFS deve creare una nuova \textit{dentry} e caricare l'inode
-corrispondente in memoria.
+pathname il VFS deve creare una nuova \textit{dentry} e caricare
+l'inode\index{inode} corrispondente in memoria.
-Questo procedimento viene eseguito dal metodo \code{lookup()} dell'inode
-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.
+Questo procedimento viene eseguito dal metodo \code{lookup()}
+dell'inode\index{inode} 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 \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.
+dell'inode\index{inode} e passarli in user space.
L'apertura di un file richiede comunque un'altra operazione, l'allocazione di
una struttura di tipo \var{file} in cui viene inserito un puntatore alla
\secref{sec:file_ext2}). È comunque caratteristica 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.
+inode\index{inode} e lo spazio a disposizione per i dati e le directory.
\begin{figure}[htb]
\centering
\begin{enumerate}
-\item L'\textit{inode} contiene tutte le informazioni riguardanti il file: il
- tipo di file, i permessi di accesso, le dimensioni, i puntatori ai blocchi
- fisici che contengono i dati e così via; le informazioni che la funzione
- \func{stat} fornisce provengono dall'\textit{inode}; dentro una directory si
- troverà solo il nome del file e il numero dell'\textit{inode} ad esso
- associato, cioè quella che da qui in poi chiameremo una \textsl{voce} (come
- traduzione 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 L'\textit{inode}\index{inode} contiene tutte le informazioni riguardanti
+ il file: il tipo di file, i permessi di accesso, le dimensioni, i puntatori
+ ai blocchi fisici che contengono i dati e così via; le informazioni che la
+ funzione \func{stat} fornisce provengono dall'\textit{inode}; dentro una
+ directory si troverà solo il nome del file e il numero
+ dell'\textit{inode}\index{inode} ad esso associato, cioè quella che da qui
+ in poi chiameremo una \textsl{voce} (come traduzione 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 \figref{fig:file_filesys_detail} si possono avere più
voci che puntano allo stesso \textit{inode}. Ogni \textit{inode} ha un
file vengono effettivamente rimossi dal disco. Per questo la funzione per
cancellare un file si chiama \func{unlink}, ed in realtà non cancella
affatto i dati del file, ma si limita ad eliminare la relativa voce da una
- directory e decrementare il numero di riferimenti nell'\textit{inode}.
-
+ directory e decrementare il numero di riferimenti
+ nell'\textit{inode}\index{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.
+ riferimenti ad \textit{inode}\index{inode} 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 viene spostato fisicamente, viene semplicemente creata una
- nuova voce per l'\textit{inode} in questione e rimossa la vecchia (questa è
- la modalità in cui opera normalmente il comando \cmd{mv} attraverso la
- funzione \func{rename}).
+ nuova voce per l'\textit{inode}\index{inode} in questione e rimossa la
+ vecchia (questa è la modalità in cui opera normalmente il comando \cmd{mv}
+ attraverso la funzione \func{rename}).
\end{enumerate}
mostrata in \figref{fig:file_filesys_detail} creiamo una nuova directory
\file{img} nella directory \file{gapil}, avremo una situazione come quella in
\figref{fig:file_dirs_link}, dove per chiarezza abbiamo aggiunto dei numeri di
-inode.
+inode\index{inode}.
\begin{figure}[htb]
\centering
in fase di creazione, a seconda delle sue esigenze (blocchi più grandi
permettono un accesso più veloce, ma sprecano più spazio disco).
\item il filesystem implementa link simbolici veloci, in cui il nome del file
- non è salvato su un blocco, ma tenuto all'interno dell'inode (evitando
- letture multiple e spreco di spazio), non tutti i nomi però possono essere
- gestiti così per limiti di spazio (il limite è 60 caratteri).
+ non è salvato su un blocco, ma tenuto all'interno dell'inode\index{inode}
+ (evitando letture multiple e spreco di spazio), non tutti i nomi però
+ possono essere gestiti così per limiti di spazio (il limite è 60 caratteri).
\item vengono supportati i file immutabili (che possono solo essere letti) per
la protezione di file di configurazione sensibili, o file
\textit{append-only} che possono essere aperti in scrittura solo per
L'utilizzo di raggruppamenti di blocchi ha inoltre degli effetti positivi nelle
prestazioni dato che viene ridotta la distanza fra i dati e la tabella degli
-inode.
+inode\index{inode}.
Le directory sono implementate come una linked list con voci di dimensione
-variabile. Ciascuna voce della lista contiene il numero di inode, la sua
-lunghezza, il nome del file e la sua lunghezza, secondo lo schema in
+variabile. Ciascuna voce della lista contiene il numero di inode\index{inode},
+la sua lunghezza, il nome del file e la sua lunghezza, secondo lo schema in
\figref{fig:file_ext2_dirs}; in questo modo è possibile implementare nomi per
i file anche molto lunghi (fino a 1024 caratteri) senza sprecare spazio disco.
\subsection{I \textit{file stream}}
\label{sec:file_stream}
+\index{file!stream|(}
Come più volte ribadito, l'interfaccia dei file descriptor è un'interfaccia di
basso livello, che non provvede nessuna forma di formattazione dei dati e
nessuna forma di bufferizzazione per ottimizzare le operazioni di I/O.
(sui quali sono basati), ed in particolare continua a valere quanto visto in
\secref{sec:file_sharing} a proposito dell'accesso condiviso ed in
\secref{sec:file_access_control} per il controllo di accesso.
+\index{file!stream|)}
\subsection{Gli oggetti \ctyp{FILE}}
preventivamente chiuso.
Infine \func{fdopen} viene usata per associare uno stream ad un file
-descriptor esistente ottenuto tramite una altra funzione (ad esempio con
-una \func{open}, una \func{dup}, o una \func{pipe}) e serve quando si
-vogliono usare gli stream con file come le fifo o i socket, che non
+descriptor esistente ottenuto tramite una altra funzione (ad esempio con una
+\func{open}, una \func{dup}, o una \func{pipe}) e serve quando si vogliono
+usare gli stream con file come le fifo o i socket\index{socket}, che non
possono essere aperti con le funzioni delle librerie standard del C.
\begin{table}[htb]
punto prestabilito; sempre che l'operazione di riposizionamento sia supportata
dal file sottostante lo stream, quando cioè si ha a che fare con quello che
viene detto un file ad \textsl{accesso casuale}.\footnote{dato che in un
- sistema Unix esistono vari tipi di file, come le fifo ed i dispositivi, non
- è scontato che questo sia sempre vero.}
+ sistema Unix esistono vari tipi di file, come le fifo ed i file di
+ dispositivo\index{file!di dispositivo}, non è scontato che questo sia sempre
+ vero.}
In GNU/Linux ed in generale in ogni sistema unix-like la posizione nel file è
espressa da un intero positivo, rappresentato dal tipo \type{off\_t}, il
Esamineremo in questo capitolo la prima delle due interfacce di programmazione
-per i file, quella dei \textit{file descriptor}\index{file descriptor},
+per i file, quella dei \textit{file descriptor}\index{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 è
\subsection{L'architettura dei \textit{file descriptor}}
\label{sec:file_fd}
-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 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.
+\index{file!descriptor|(} 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\index{inode} del file e 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}\index{file descriptor}.
+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.
\item una tabella che contiene un puntatore alla relativa voce nella
\textit{file table} per ogni file aperto.
\end{itemize*}
-il \textit{file descriptor}\index{file descriptor} in sostanza è l'intero
-positivo che indicizza quest'ultima tabella.
+il \textit{file descriptor} in sostanza è l'intero positivo che indicizza
+quest'ultima tabella.
La \textit{file table} è una tabella che contiene una voce per ciascun file
che è stato aperto nel sistema. In Linux è costituita da strutture di tipo
\item lo stato del file (nel campo \var{f\_flags}).
\item il valore della posizione corrente (l'\textit{offset}) nel file (nel
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.
+\item un puntatore all'inode\index{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\index{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.
\end{figure}
Ritorneremo su questo schema più volte, dato che esso è fondamentale per
capire i dettagli del funzionamento dell'interfaccia dei \textit{file
- descriptor}\index{file descriptor}.
+ descriptor}.
+\index{file!descriptor|)}
+
+
\subsection{I file standard}
\label{sec:file_std_descr}
-Come accennato i \textit{file descriptor}\index{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 ne è stato chiuso nessuno in precedenza).
+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 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 appena
-detto, avranno come \textit{file descriptor}\index{file descriptor} i valori
+detto, avranno come \textit{file descriptor}\index{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à.
facendo riferimento ad un programma in cui lo \textit{standard input} è
associato ad 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).
+stesso inode\index{inode}).
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
\footnotetext[2]{la pagina di manuale di \func{open} segnala che questa
opzione è difettosa su NFS, e che i programmi che la usano per stabilire un
- \textsl{file di lock}\index{file di lock} possono incorrere in una race
+ \textsl{file di lock}\index{file!di lock} possono incorrere in una race
condition\index{race condition}. Si consiglia come alternativa di usare un
file con un nome univoco e la funzione \func{link} per verificarne
l'esistenza (vedi \secref{sec:ipc_file_lock}).}
ed inoltre \errval{EIO}.}
\end{prototype}
-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} è 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.
+La chiusura di un file rilascia ogni blocco (il \textit{file
+ locking}\index{file!locking} è trattato in \secref{sec:file_locking}) che il
+processo poteva avere acquisito 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.
Si ricordi che quando un processo termina anche tutti i suoi file descriptor
vengono chiusi, molti programmi sfruttano questa caratteristica e non usano
successo e -1 in caso di errore nel qual caso \var{errno} assumerà uno dei
valori:
\begin{errlist}
- \item[\errcode{ESPIPE}] \param{fd} è una pipe, un socket o una fifo.
+ \item[\errcode{ESPIPE}] \param{fd} è una pipe, un socket\index{socket} o una
+ fifo.
\item[\errcode{EINVAL}] \param{whence} non è un valore valido.
\end{errlist}
ed inoltre \errval{EBADF}.}
di byte richiesti eccede quelli disponibili la funzione ritorna comunque, ma
con un numero di byte inferiore a quelli richiesti.
-Lo stesso comportamento avviene caso di lettura dalla rete (cioè su un socket,
-come vedremo in \secref{sec:sock_io_behav}), o per la lettura da certi file di
-dispositivo, come le unità a nastro, che restituiscono sempre i dati ad un
-singolo blocco alla volta.
+Lo stesso comportamento avviene caso di lettura dalla rete (cioè su un
+socket\index{socket}, come vedremo in \secref{sec:sock_io_behav}), o per la
+lettura da certi file di dispositivo, come le unità a nastro, che
+restituiscono sempre i dati ad un singolo blocco alla volta.
In realtà anche le due condizioni segnalate dagli errori \errcode{EINTR} e
\errcode{EAGAIN} non sono errori. La prima si verifica quando la \func{read} è
situazione come quella illustrata in \figref{fig:file_mult_acc}: ciascun
processo avrà una sua voce nella \textit{file table} referenziata da un
diverso file descriptor nella sua \var{file\_struct}. Entrambe le voci nella
-\textit{file table} faranno però riferimento allo stesso inode su disco.
+\textit{file table} faranno però riferimento allo stesso inode\index{inode} su
+disco.
Questo significa che ciascun processo avrà la sua posizione corrente sul file,
la sua modalità di accesso e versioni proprie di tutte le proprietà che
\item ciascun processo può scrivere indipendentemente; dopo ciascuna
\func{write} la posizione corrente sarà cambiata solo nel processo. Se la
scrittura eccede la dimensione corrente del file questo verrà esteso
- automaticamente con l'aggiornamento del campo \var{i\_size} nell'inode.
+ automaticamente con l'aggiornamento del campo \var{i\_size}
+ nell'inode\index{inode}.
\item se un file è in modalità \const{O\_APPEND} tutte le volte che viene
effettuata una scrittura la posizione corrente viene prima impostata alla
- dimensione corrente del file letta dall'inode. Dopo la scrittura il file
- viene automaticamente esteso.
-\item l'effetto di \func{lseek} è solo quello di cambiare il campo \var{f\_pos}
- nella struttura \var{file} della \textit{file table}, non c'è nessuna
- operazione sul file su disco. Quando la si usa per porsi alla fine del file
- la posizione viene impostata leggendo la dimensione corrente dall'inode.
+ dimensione corrente del file letta dall'inode\index{inode}. Dopo la
+ scrittura il file viene automaticamente esteso.
+\item l'effetto di \func{lseek} è solo quello di cambiare il campo
+ \var{f\_pos} nella struttura \var{file} della \textit{file table}, non c'è
+ nessuna operazione sul file su disco. Quando la si usa per porsi alla fine
+ del file la posizione viene impostata leggendo la dimensione corrente
+ dall'inode\index{inode}.
\end{itemize}
\begin{figure}[htb]
maniera imprevedibile. Il sistema però fornisce in alcuni casi la possibilità
di eseguire alcune operazioni di scrittura in maniera coordinata anche senza
utilizzare meccanismi di sincronizzazione più complessi (come il \textit{file
- locking}, che esamineremo in \secref{sec:file_locking}).
+ locking}\index{file!locking}, che esamineremo in \secref{sec:file_locking}).
Un caso tipico di necessità di accesso condiviso in scrittura è quello in cui
vari processi devono scrivere alla fine di un file (ad esempio un file di
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 \textsl{file di lock}\index{file di lock}, bloccandosi se il file
+creare un \textsl{file di lock}\index{file!di lock}, bloccandosi se il file
esiste. In questo caso la sequenza logica porterebbe a verificare prima
l'esistenza del file con una \func{stat} per poi crearlo con una \func{creat};
di nuovo avremmo la possibilità di una race condition\index{race condition} da
file specificato, ed attendono fino alla conclusione delle operazioni;
\func{fsync} forza anche la sincronizzazione dei metadati del file (che
riguardano sia le modifiche alle tabelle di allocazione dei settori, che gli
-altri dati contenuti nell'inode che si leggono con \var{fstat} come i tempi
-del file).
+altri dati contenuti nell'inode\index{inode} che si leggono con \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
gestione sia delle loro proprietà, che di tutta una serie di ulteriori
funzionalità che il kernel può mettere a disposizione.\footnote{ad esempio si
gesticono con questa funzione l'I/O asincrono (vedi
- \secref{sec:file_asyncronous_io}) e il file locking (vedi
- \secref{sec:file_locking}).}
+ \secref{sec:file_asyncronous_io}) e il file locking\index{file!locking}
+ (vedi \secref{sec:file_locking}).}
Per queste operazioni di manipolazione e di controllo su proprietà e
caratteristiche un file descriptor, viene usata la funzione \func{fcntl}, il
avanti quando affronteremo le problematiche ad esse relative (in particolare
le tematiche relative all'I/O asincrono sono trattate in maniera esaustiva in
\secref{sec:file_asyncronous_io} mentre quelle relative al \textit{file
- locking} saranno esaminate in \secref{sec:file_locking}).
+ locking}\index{file!locking} saranno esaminate in
+\secref{sec:file_locking}).
Si tenga presente infine che quando si usa la funzione per determinare le
modalità di accesso con cui è stato aperto il file (attraverso l'uso del
\type{clock\_t} & contatore del tempo di sistema.\\
\type{dev\_t} & Numero di dispositivo.\\
\type{gid\_t} & Identificatore di un gruppo.\\
- \type{ino\_t} & Numero di \textit{inode}.\\
+ \type{ino\_t} & Numero di \textit{inode}\index{inode}.\\
\type{key\_t} & Chiave per il System V IPC.\\
\type{loff\_t} & Posizione corrente in un file.\\
\type{mode\_t} & Attributi di un file.\\
Si potrebbe obiettare che sarebbe molto più semplice salvare il risultato
intermedio su un file temporaneo. Questo però non tiene conto del fatto che un
\textit{CGI} deve poter gestire più richieste in concorrenza, e si avrebbe una
-evidente race condition in caso di accesso simultaneo a detto
-file.\footnote{il problema potrebbe essere superato determinando in anticipo
- un nome appropriato per il file temporaneo, che verrebbe utilizzato dai vari
- sotto-processi, e cancellato alla fine della loro esecuzione; ma a questo le
- cose non sarebbero più tanto semplici.} L'uso di una pipe invece permette
-di risolvere il problema in maniera semplice ed elegante, oltre ad essere
-molto più efficiente, dato che non si deve scrivere su disco.
+evidente race condition\index{race condition} in caso di accesso simultaneo a
+detto file.\footnote{il problema potrebbe essere superato determinando in
+ anticipo un nome appropriato per il file temporaneo, che verrebbe utilizzato
+ dai vari sotto-processi, e cancellato alla fine della loro esecuzione; ma a
+ questo le cose non sarebbero più tanto semplici.} L'uso di una pipe invece
+permette di risolvere il problema in maniera semplice ed elegante, oltre ad
+essere molto più efficiente, dato che non si deve scrivere su disco.
Il programma ci servirà anche come esempio dell'uso delle funzioni di
duplicazione dei file descriptor che abbiamo trattato in
quindi associato allo standard input) in caso di \code{"w"}.
Lo stream restituito da \func{popen} è identico a tutti gli effetti ai file
-stream visti in \secref{cha:files_std_interface}, anche se è collegato ad una
-pipe e non ad un inode, e viene sempre aperto in modalità
+stream visti in \capref{cha:files_std_interface}, anche se è collegato ad una
+pipe e non ad un inode\index{inode}, e viene sempre aperto in modalità
\textit{fully-buffered} (vedi \secref{sec:file_buffering}); l'unica differenza
con gli usuali stream è che dovrà essere chiuso dalla seconda delle due nuove
funzioni, \func{pclose}, il cui prototipo è:
ha definito dei nuovi oggetti, le \textit{fifo}, che hanno le stesse
caratteristiche delle pipe, ma che invece di essere strutture interne del
kernel, visibili solo attraverso un file descriptor, sono accessibili
-attraverso un inode che risiede sul filesystem, così che i processi le possono
-usare senza dovere per forza essere in una relazione di \textsl{parentela}.
+attraverso un inode\index{inode} che risiede sul filesystem, così che i
+processi le possono usare senza dovere per forza essere in una relazione di
+\textsl{parentela}.
Utilizzando una \textit{fifo} tutti i dati passeranno, come per le pipe,
attraverso un apposito buffer nel kernel, senza transitare dal filesystem;
-l'inode allocato sul filesystem serve infatti solo a fornire un punto di
-riferimento per i processi, che permetta loro di accedere alla stessa fifo; il
-comportamento delle funzioni di lettura e scrittura è identico a quello
-illustrato per le pipe in \secref{sec:ipc_pipes}.
+l'inode\index{inode} allocato sul filesystem serve infatti solo a fornire un
+punto di riferimento per i processi, che permetta loro di accedere alla stessa
+fifo; il comportamento delle funzioni di lettura e scrittura è identico a
+quello illustrato per le pipe in \secref{sec:ipc_pipes}.
Abbiamo già visto in \secref{sec:file_mknod} le funzioni \func{mknod} e
\func{mkfifo} che permettono di creare una fifo; per utilizzarne una un
lettura; è possibile anche usare la fifo all'interno di un solo processo, nel
qual caso però occorre stare molto attenti alla possibili situazioni di
stallo.\footnote{se si cerca di leggere da una fifo che non contiene dati si
- avrà un deadlock immediato, dato che il processo si blocca e non potrà
- quindi mai eseguire le funzioni di scrittura.}
+ avrà un deadlock\index{deadlock} immediato, dato che il processo si blocca e
+ non potrà quindi mai eseguire le funzioni di scrittura.}
Per la loro caratteristica di essere accessibili attraverso il filesystem, è
piuttosto frequente l'utilizzo di una fifo come canale di comunicazione nelle
Un meccanismo di comunicazione molto simile alle pipe, ma che non presenta il
problema della unidirezionalità del flusso dei dati, è quello dei cosiddetti
\textsl{socket locali} (o \textit{Unix domain socket}). Tratteremo l'argomento
-dei \textit{socket} in \capref{cha:socket_intro},\footnote{si tratta comunque
- di oggetti di comunicazione che, come le pipe, sono utilizzati attraverso
- dei file descriptor.} nell'ambito dell'interfaccia generale che essi
-forniscono per la programmazione di rete; e vedremo anche
+dei \textit{socket}\index{socket} in \capref{cha:socket_intro},\footnote{si
+ tratta comunque di oggetti di comunicazione che, come le pipe, sono
+ utilizzati attraverso dei file descriptor.} nell'ambito dell'interfaccia
+generale che essi forniscono per la programmazione di rete; e vedremo anche
(in~\secref{sec:sock_sa_local}) come si possono definire dei file speciali (di
tipo \textit{socket}, analoghi a quello associati alle fifo) cui si accede
però attraverso quella medesima interfaccia; vale però la pena esaminare qui
identici ad una pipe bidirezionale.
La funzione \func{socketpair} infatti consente di creare una coppia di file
-descriptor connessi fra di loro (tramite un socket, appunto), senza dover
-ricorrere ad un file speciale sul filesystem, i descrittori sono del tutto
-analoghi a quelli che si avrebbero con una chiamata a \func{pipe}, con la sola
-differenza è che in questo caso il flusso dei dati può essere effettuato in
-entrambe le direzioni. Il prototipo della funzione è:
+descriptor connessi fra di loro (tramite un socket\index{socket}, appunto),
+senza dover ricorrere ad un file speciale sul filesystem, i descrittori sono
+del tutto analoghi a quelli che si avrebbero con una chiamata a \func{pipe},
+con la sola differenza è che in questo caso il flusso dei dati può essere
+effettuato in entrambe le direzioni. Il prototipo della funzione è:
\begin{functions}
\headdecl{sys/types.h}
\headdecl{sys/socket.h}
\funcdecl{int socketpair(int domain, int type, int protocol, int sv[2])}
- Crea una coppia di socket connessi fra loro.
+ Crea una coppia di socket\index{socket} connessi fra loro.
\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EAFNOSUPPORT}] I socket locali non sono supportati.
+ \item[\errcode{EAFNOSUPPORT}] I socket\index{socket} locali non sono
+ supportati.
\item[\errcode{EPROTONOSUPPORT}] Il protocollo specificato non è supportato.
\item[\errcode{EOPNOTSUPP}] Il protocollo specificato non supporta la
- creazione di coppie di socket.
+ creazione di coppie di socket\index{socket}.
\end{errlist}
ed inoltre \errval{EMFILE}, \errval{EFAULT}.
}
La funzione restituisce in \param{sv} la coppia di descrittori connessi fra di
loro: quello che si scrive su uno di essi sarà ripresentato in input
sull'altro e viceversa. I parametri \param{domain}, \param{type} e
-\param{protocol} derivano dall'interfaccia dei socket (che è quella che
-fornisce il substrato per connettere i due descrittori), ma in questo caso i
-soli valori validi che possono essere specificati sono rispettivamente
-\const{AF\_UNIX}, \const{SOCK\_STREAM} e \var{0}.
+\param{protocol} derivano dall'interfaccia dei socket\index{socket} (che è
+quella che fornisce il substrato per connettere i due descrittori), ma in
+questo caso i soli valori validi che possono essere specificati sono
+rispettivamente \const{AF\_UNIX}, \const{SOCK\_STREAM} e \var{0}.
L'utilità di chiamare questa funzione per evitare due chiamate a \func{pipe}
-può sembrare limitata; in realtà l'utilizzo di questa funzione (e dei socket
-locali in generale) permette di trasmettere attraverso le linea non solo dei
-dati, ma anche dei file descriptor: si può cioè passare da un processo ad un
-altro un file descriptor, con una sorta di duplicazione dello stesso non
-all'interno di uno stesso processo, ma fra processi distinti (torneremo su
-questa funzionalità in \secref{sec:xxx_fd_passing}).
+può sembrare limitata; in realtà l'utilizzo di questa funzione (e dei
+socket\index{socket} locali in generale) permette di trasmettere attraverso le
+linea non solo dei dati, ma anche dei file descriptor: si può cioè passare da
+un processo ad un altro un file descriptor, con una sorta di duplicazione
+dello stesso non all'interno di uno stesso processo, ma fra processi distinti
+(torneremo su questa funzionalità in \secref{sec:xxx_fd_passing}).
\section{La comunicazione fra processi di System V}
Il problema è che anche così non c'è la sicurezza che il valore della chiave
sia univoco, infatti esso è costruito combinando il byte di \param{proj\_id)}
-con i 16 bit meno significativi dell'inode del file \param{pathname} (che
-vengono ottenuti attraverso \func{stat}, da cui derivano i possibili errori),
-e gli 8 bit meno significativi del numero del dispositivo su cui è il file.
-Diventa perciò relativamente facile ottenere delle collisioni, specie se i
-file sono su dispositivi con lo stesso \textit{minor number}, come
-\file{/dev/hda1} e \file{/dev/sda1}.
+con i 16 bit meno significativi dell'inode\index{inode} del file
+\param{pathname} (che vengono ottenuti attraverso \func{stat}, da cui derivano
+i possibili errori), e gli 8 bit meno significativi del numero del dispositivo
+su cui è il file. Diventa perciò relativamente facile ottenere delle
+collisioni, specie se i file sono su dispositivi con lo stesso \textit{minor
+ number}, come \file{/dev/hda1} e \file{/dev/sda1}.
In genere quello che si fa è utilizzare un file comune usato dai programmi che
devono comunicare (ad esempio un header comune, o uno dei programmi che devono
della risposta, quest'ultima resta nella coda (così come per le fifo si aveva
il problema delle fifo che restavano nel filesystem). In questo caso però il
problemi sono maggiori, sia perché è molto più facile esaurire la memoria
-dedicata ad una coda di messaggi che gli inode di un filesystem, sia perché,
-con il riutilizzo dei \acr{pid} da parte dei processi, un client eseguito in
-un momento successivo potrebbe ricevere un messaggio non indirizzato a
-lui.
+dedicata ad una coda di messaggi che gli inode\index{inode} di un filesystem,
+sia perché, con il riutilizzo dei \acr{pid} da parte dei processi, un client
+eseguito in un momento successivo potrebbe ricevere un messaggio non
+indirizzato a lui.
che usare \func{MutexRead} per controllare il valore dei mutex prima di
proseguire non servirebbe comunque, dato che l'operazione non sarebbe atomica.
Vedremo in \secref{sec:ipc_posix_sem} come è possibile ottenere un'interfaccia
-analoga senza questo problemi usando il file locking.
+analoga senza questo problemi usando il file locking\index{file!locking}.
\label{sec:ipc_alternatives}
Come abbiamo detto in \secref{sec:ipc_sysv_generic}, e ripreso nella
-descrizione dei signoli oggetti che ne fan parte, il \textit{SysV IPC}
+descrizione dei singoli oggetti che ne fan parte, il \textit{SysV IPC}
presenta numerosi problemi; in \cite{APUE}\footnote{in particolare nel
- capitolo 14.} Stevens ne eeffettua una accurata analisi (alcuni dei
+ capitolo 14.} Stevens ne effettua una accurata analisi (alcuni dei
concetti sono già stati accennati in precedenza) ed elenca alcune possibili
tecniche alternative, che vogliamo riprendere in questa sezione.
In realtà, grazie alla presenza del campo \var{mtype}, le code di messaggi
hanno delle caratteristiche ulteriori, consentendo una classificazione dei
messaggi ed un accesso non rigidamente sequenziale; due caratteristiche che
-sono impossibili da ottenere con le pipe e i socket di \func{socketpair}. A
-queste esigenze però si può comunque ovviare in maniera diversa con un uso
-combinato della memoria condivisa e dei meccanismi di sincronizzazione, per
-cui alla fine l'uso delle code di messaggi classiche è poco diffuso.
+sono impossibili da ottenere con le pipe e i socket\index{socket} di
+\func{socketpair}. A queste esigenze però si può comunque ovviare in maniera
+diversa con un uso combinato della memoria condivisa e dei meccanismi di
+sincronizzazione, per cui alla fine l'uso delle code di messaggi classiche è
+poco diffuso.
\subsection{I \textsl{file di lock}}
\label{sec:ipc_file_lock}
+\index{file!di lock|(}
Come illustrato in \secref{sec:ipc_sysv_sem} i semafori del \textit{SysV IPC}
presentano una interfaccia inutilmente complessa e con alcuni difetti
strutturali, per questo quando si ha una semplice esigenza di sincronizzazione
alternativi.
La prima possibilità, utilizzata fin dalle origini di Unix, è quella di usare
-dei \textsl{file di lock}\index{file di lock} (per i quali esiste anche una
-opportuna directory, \file{/var/lock}, nel filesystem standard). Per questo si
-usa la caratteristica della funzione \func{open} (illustrata in
+dei \textsl{file di lock} (per i quali esiste anche una opportuna directory,
+\file{/var/lock}, nel filesystem standard). Per questo si usa la
+caratteristica della funzione \func{open} (illustrata in
\secref{sec:file_open}) che prevede\footnote{questo è quanto dettato dallo
standard POSIX.1, ciò non toglie che in alcune implementazioni questa
tecnica possa non funzionare; in particolare per Linux, nel caso di NFS, si
(la funzione viene eseguita, ma non è garantita l'atomicità dell'operazione)
se il filesystem su cui si va ad operare è su NFS; in tal caso si può adottare
una tecnica alternativa che prevede l'uso della \func{link} per creare come
-file di lock un hard link ad un file esistente; se il link esiste già e la
-funzione fallisce, significa che la risorsa è bloccata e potrà essere
+\textsl{file di lock} un hard link ad un file esistente; se il link esiste già
+e la funzione fallisce, significa che la risorsa è bloccata e potrà essere
sbloccata solo con un \func{unlink}, altrimenti il link è creato ed il lock
acquisito; il controllo e l'eventuale acquisizione sono atomici; la soluzione
funziona anche su NFS, ma ha un'altro difetto è che è quello di poterla usare
Un generale comunque l'uso di un \textsl{file di lock} presenta parecchi
problemi, che non lo rendono una alternativa praticabile per la
sincronizzazione: anzitutto anche in questo caso, in caso di terminazione
-imprevista del processo, si lascia allocata la risorsa (il file di lock) e
-questa deve essere sempre cancellata esplicitamente. Inoltre il controllo
-della disponibilità può essere eseguito solo con una tecnica di
+imprevista del processo, si lascia allocata la risorsa (il \textsl{file di
+ lock}) e questa deve essere sempre cancellata esplicitamente. Inoltre il
+controllo della disponibilità può essere eseguito solo con una tecnica di
\textit{polling}\index{polling}, ed è quindi molto inefficiente.
La tecnica dei file di lock non di meno ha una sua utilità, e può essere usata
risorsa, senza necessità di attendere che questa si liberi; ad esempio la si
usa spesso per evitare interferenze sull'uso delle porte seriali da parte di
più programmi: qualora si trovi un file di lock il programma che cerca di
-accedere alla seriale si limita a segnalare che la risorsa non è disponibile.
+accedere alla seriale si limita a segnalare che la risorsa non è
+disponibile.\index{file!di lock|)}
\subsection{La sincronizzazione con il \textit{file locking}}
\label{sec:ipc_lock_file}
Dato che i file di lock presentano gli inconvenienti illustrati in precedenza,
la tecnica alternativa più comune è quella di fare ricorso al \textit{file
- locking} (trattato in \secref{sec:file_locking}) usando \func{fcntl} su un
-file creato per l'occasione per ottenere un write lock. In questo modo potremo
-usare il lock come un \textit{mutex}: per bloccare la risorsa basterà
-acquisire il lock, per sbloccarla basterà rilasciare il lock; una richiesta
-fatta con un write lock metterà automaticamente il processo in stato di
-attesa, senza necessità di ricorrere al \textit{polling}\index{polling} per
-determinare la disponibilità della risorsa, e al rilascio della stessa da
-parte del processo che la occupava si otterrà il nuovo lock atomicamente.
+ locking}\index{file!locking} (trattato in \secref{sec:file_locking}) usando
+\func{fcntl} su un file creato per l'occasione per ottenere un write lock. In
+questo modo potremo usare il lock come un \textit{mutex}: per bloccare la
+risorsa basterà acquisire il lock, per sbloccarla basterà rilasciare il lock;
+una richiesta fatta con un write lock metterà automaticamente il processo in
+stato di attesa, senza necessità di ricorrere al
+\textit{polling}\index{polling} per determinare la disponibilità della
+risorsa, e al rilascio della stessa da parte del processo che la occupava si
+otterrà il nuovo lock atomicamente.
Questo approccio presenta il notevole vantaggio che alla terminazione di un
processo tutti i lock acquisiti vengono rilasciati automaticamente (alla
\end{minipage}
\normalsize
\caption{Il codice delle funzioni che permettono di creare un
- \textit{mutex} utilizzando il file locking.}
+ \textit{mutex} utilizzando il file locking\index{file!locking}.}
\label{fig:ipc_flock_mutex}
\end{figure}
-Il codice per implementare un mutex utilizzando il file locking è riportato in
-\figref{fig:ipc_flock_mutex}; a differenza del precedente caso in cui si sono
-usati i semafori le funzioni questa volta sono sufficienti due funzioni,
-\func{LockMutex} e \func{UnlockMutex}, usate rispettivamente per acquisire e
-rilasciare il mutex.
+Il codice per implementare un mutex utilizzando il file
+locking\index{file!locking} è riportato in \figref{fig:ipc_flock_mutex}; a
+differenza del precedente caso in cui si sono usati i semafori le funzioni
+questa volta sono sufficienti due funzioni, \func{LockMutex} e
+\func{UnlockMutex}, usate rispettivamente per acquisire e rilasciare il mutex.
La prima funzione (\texttt{\small 1--22}) serve per acquisire il mutex.
Anzitutto si apre (\texttt{\small 9--11}), creandolo se non esiste, il file
\file{/dev/zero}. In tal caso i valori scritti nella regione mappata non
vengono ignorati (come accade qualora si scriva direttamente sul file), ma
restano in memoria e possono essere riletti secondo le stesse modalità usate
- nele \textit{memory mapping} anonimo.} Un esempio di utilizzo di questa
+ nel \textit{memory mapping} anonimo.} Un esempio di utilizzo di questa
tecnica è mostrato in
Le code di messaggi non sono supportate a livello del kernel, esse però
possono essere implementate, usando la memoria condivisa ed i mutex, con
-funzioni di libreria. In generale esse sono comunque poco usate, i socket, nei
-casi in cui sono sufficienti, sono più comodi, e negli altri casi la
-comunicazione può essere gestita direttamente con la stessa metodologia usata
-per implementare le code di messaggi. Per questo ci limiteremo ad una
-descrizione essenziale.
+funzioni di libreria. In generale esse sono comunque poco usate, i
+socket\index{socket}, nei casi in cui sono sufficienti, sono più comodi, e
+negli altri casi la comunicazione può essere gestita direttamente con la
+stessa metodologia usata per implementare le code di messaggi. Per questo ci
+limiteremo ad una descrizione essenziale.
specifico, implementando un protocollo di applicazione (esempi possono
essere HTTP, POP, telnet, SMTP, etc).
\item Questi dati vengono inviati al livello di trasporto usando
- un'interfaccia opportuna (i \textit{socket}, che esamineremo in dettaglio in
- seguito). Qui verranno spezzati in pacchetti di dimensione opportuna e
- incapsulati nel protocollo di trasporto, aggiungendo ad ogni pacchetto le
- informazioni necessarie per la sua gestione. Questo processo viene
- svolto direttamente nel kernel ad esempio dallo stack TCP nel caso il
+ un'interfaccia opportuna (i \textit{socket}\index{socket}, che esamineremo
+ in dettaglio in seguito). Qui verranno spezzati in pacchetti di dimensione
+ opportuna e incapsulati nel protocollo di trasporto, aggiungendo ad ogni
+ pacchetto le informazioni necessarie per la sua gestione. Questo processo
+ viene svolto direttamente nel kernel ad esempio dallo stack TCP nel caso il
protocollo di trasporto sia questo.
\item Una volta composto il pacchetto nel formato adatto al protocollo di
trasporto usato questo sarà passato al successivo livello, quello di rete,
rete però sono importanti principalmente i due livelli centrali, e soprattutto
quello di trasporto.
-La principale interfaccia di programmazione di rete, quella dei socket, è
-infatti un'interfaccia nei confronti di quest'ultimo. Questo avviene perché al
-di sopra del livello di trasporto i programmi hanno a che fare solo con
-dettagli specifici delle applicazioni, mentre al di sotto vengono curati tutti
-i dettagli relativi alla comunicazione. È pertanto naturale definire una API
-su questo confine tanto più che è proprio li (come evidenziato in
-\figref{fig:net_osi_tcpip_comp}) che nei sistemi Unix (e non solo) viene
-inserita la divisione fra kernel space e user space.
+La principale interfaccia di programmazione di rete, quella dei
+socket\index{socket}, è infatti un'interfaccia nei confronti di quest'ultimo.
+Questo avviene perché al di sopra del livello di trasporto i programmi hanno a
+che fare solo con dettagli specifici delle applicazioni, mentre al di sotto
+vengono curati tutti i dettagli relativi alla comunicazione. È pertanto
+naturale definire una API su questo confine tanto più che è proprio li (come
+evidenziato in \figref{fig:net_osi_tcpip_comp}) che nei sistemi Unix (e non
+solo) viene inserita la divisione fra kernel space e user space.
In realtà in un sistema Unix è possibile accedere anche agli altri livelli
inferiori (e non solo a quello di trasporto) con opportune interfacce (la cosa
lo stesso servizio di trasporto di IPv4 per i pacchetti TCP, UDP e ICPMv6.
\item \textsl{TCP} \textit{Trasmission Control Protocol}. È un protocollo
orientato alla connessione che provvede un trasporto affidabile e
- bidirezionale di un flusso di dati. I socket TCP sono esempi di
- \textit{stream socket}. Il protocollo ha cura di tutti gli aspetti del
- trasporto, come l'acknoweledgment, i timeout, la ritrasmissione, etc. È
+ bidirezionale di un flusso di dati. I socket\index{socket} TCP sono esempi
+ di \textit{stream socket}. Il protocollo ha cura di tutti gli aspetti del
+ trasporto, come l'acknoweledgment, i timeout, la ritrasmissione, etc. È
usato dalla maggior parte delle applicazioni. Può essere usato sia con IPv4
che con IPv6.
\item \textsl{UDP} \textit{User Datagram Protocol}. È un protocollo senza
- connessione a pacchetti. I socket UDP sono esempi di \textit{datagram
- socket}. Contrariamente al TCP in protocollo non è affidabile e non c'è
- garanzia che i pacchetti raggiungano la loro destinazione, né sull'eventuale
- ordine di arrivo. Può essere usato sia con IPv4 che con IPv6.
+ connessione a pacchetti. I socket\index{socket} UDP sono esempi di
+ \textit{datagram socket}. Contrariamente al TCP in protocollo non è
+ affidabile e non c'è garanzia che i pacchetti raggiungano la loro
+ destinazione, né sull'eventuale ordine di arrivo. Può essere usato sia con
+ IPv4 che con IPv6.
\item \textsl{ICMP} \textit{Internet Control Message Protocol}. Gestisce gli
errori e trasporta l'informazione di controllo fra stazioni remote e
instradatori (\textit{host} e \textit{router}). I messaggi sono normalmente
contenuta dell'RFC~768, ma in sostanza esso è una semplice interfaccia a IP
dal livello di trasporto. Quando un'applicazione usa UDP essa scrive un
pacchetto di dati (il cosiddetto \textit{datagram} che da il nome al
-protocollo) su un socket, al pacchetto viene aggiunto un header molto semplice
-(per una descrizione più accurata vedi \secref{sec:xxx_udp}), e poi viene
-passato al livello superiore (IPv4 o IPv6 che sia) che lo spedisce verso la
-destinazione. Dato che né IPv4 né IPv6 garantiscono l'affidabilità niente
-assicura che il pacchetto arrivi a destinazione, né che più pacchetti arrivino
-nello stesso ordine in cui sono stati spediti.
+protocollo) su un socket\index{socket}, al pacchetto viene aggiunto un header
+molto semplice (per una descrizione più accurata vedi \secref{sec:xxx_udp}), e
+poi viene passato al livello superiore (IPv4 o IPv6 che sia) che lo spedisce
+verso la destinazione. Dato che né IPv4 né IPv6 garantiscono l'affidabilità
+niente assicura che il pacchetto arrivi a destinazione, né che più pacchetti
+arrivino nello stesso ordine in cui sono stati spediti.
Pertanto il problema principale che si affronta quando si usa UDP è la
mancanza di affidabilità, se si vuole essere sicuri che i pacchetti arrivino a
Infine UDP è un protocollo che opera senza connessione
(\textit{connectionless}) in quanto non è necessario stabilire nessun tipo di
relazione tra origine e destinazione dei pacchetti. Si hanno così situazioni
-in cui un client può scrivere su uno stesso socket pacchetti destinati a
-server diversi, o un server ricevere su un socket pacchetti provenienti da
-client diversi. Il modo più semplice di immaginarsi il funzionamento di UDP è
-quello della radio, in cui si può ``trasmettere a'' e ``ricevere da'' più
-stazioni usando la stessa frequenza.
+in cui un client può scrivere su uno stesso socket\index{socket} pacchetti
+destinati a server diversi, o un server ricevere su un socket\index{socket}
+pacchetti provenienti da client diversi. Il modo più semplice di immaginarsi
+il funzionamento di UDP è quello della radio, in cui si può ``trasmettere a''
+e ``ricevere da'' più stazioni usando la stessa frequenza.
Nonostante gli evidenti svantaggi comportati dall'inaffidabilità UDP ha il
grande pregio della velocità che in certi casi è essenziale; inoltre si presta
Inoltre TCP è in grado di preservare l'ordine dei dati assegnando un numero di
sequenza ad ogni byte che trasmette. Ad esempio se un'applicazione scrive 3000
-byte su un socket TCP, questi potranno essere spezzati dal protocollo in due
-segmenti (le unità di dati passate da TCP a IP vengono chiamate
-\textit{segment}) di 1500 byte, di cui il primo conterrà il numero di
+byte su un socket\index{socket} TCP, questi potranno essere spezzati dal
+protocollo in due segmenti (le unità di dati passate da TCP a IP vengono
+chiamate \textit{segment}) di 1500 byte, di cui il primo conterrà il numero di
sequenza $1-1500$ e il secondo il numero $1501-3000$. In questo modo anche se
i segmenti arrivano a destinazione in un ordine diverso, o se alcuni arrivano
più volte a causa di ritrasmissioni dovute alla perdita dei ricevuto,
essere ricevuti.
Questa finestra cambia dinamicamente diminuendo con la ricezione dei dati dal
-socket ed aumentando con la lettura di quest'ultimo da parte
+socket\index{socket} ed aumentando con la lettura di quest'ultimo da parte
dell'applicazione, se diventa nulla il buffer di ricezione è pieno e non
verranno accettati altri dati. Si noti che UDP non provvede niente di tutto
ciò per cui nulla impedisce che vengano trasmessi pacchetti ad un rate che il
impostato al \acr{pid} del padre.
\item i valori dei tempi di esecuzione della struttura \var{tms} (vedi
\secref{sec:sys_cpu_times}) che nel figlio sono posti a zero.
-\item i \textit{file lock} (vedi \secref{sec:file_locking}), che non
+\item i \textit{lock} sui file (vedi \secref{sec:file_locking}), che non
vengono ereditati dal figlio.
\item gli allarmi ed i segnali pendenti (vedi \secref{sec:sig_gen_beha}), che
per il figlio vengono cancellati.
571 pts/0 Z 0:00 [forktest <defunct>]
572 pts/0 R 0:00 ps T
\end{verbatim} %$
-\normalsize
-e come si vede, dato che non si è fatto nulla per riceverne lo stato di
-terminazione, i tre processi figli sono ancora presenti pur essendosi
-conclusi, con lo stato di zombie e l'indicazione che sono stati terminati.
-
-La possibilità di avere degli zombie deve essere tenuta sempre presente quando
-si scrive un programma che deve essere mantenuto in esecuzione a lungo e
-creare molti figli. In questo caso si deve sempre avere cura di far leggere
-l'eventuale stato di uscita di tutti i figli (in genere questo si fa
+\normalsize e come si vede, dato che non si è fatto nulla per riceverne lo
+stato di terminazione, i tre processi figli sono ancora presenti pur essendosi
+conclusi, con lo stato di zombie\index{zombie} e l'indicazione che sono stati
+terminati.
+
+La possibilità di avere degli zombie\index{zombie} deve essere tenuta sempre
+presente quando si scrive un programma che deve essere mantenuto in esecuzione
+a lungo e creare molti figli. In questo caso si deve sempre avere cura di far
+leggere l'eventuale stato di uscita di tutti i figli (in genere questo si fa
attraverso un apposito \textit{signal handler}, che chiama la funzione
\func{wait}, vedi \secref{sec:sig_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.
+operazione è necessaria perché anche se gli \textit{zombie}\index{zombie} non
+consumano risorse di memoria o processore, occupano comunque una voce nella
+tabella dei processi, che a lungo andare potrebbe esaurirsi.
Si noti che quando un processo adottato da \cmd{init} termina, esso non
-diviene uno \textit{zombie}; questo perché una delle funzioni di \cmd{init} è
-appunto quella di chiamare la funzione \func{wait} per i processi cui fa da
-padre, completandone la terminazione. Questo è quanto avviene anche quando,
-come nel caso del precedente esempio con \cmd{forktest}, il padre termina con
-dei figli in stato di zombie: alla sua terminazione infatti tutti i suoi figli
-(compresi gli zombie) verranno adottati da \cmd{init}, il quale provvederà a
-completarne la terminazione.
+diviene uno \textit{zombie}\index{zombie}; questo perché una delle funzioni di
+\cmd{init} è appunto quella di chiamare la funzione \func{wait} per i processi
+cui fa da padre, completandone la terminazione. Questo è quanto avviene anche
+quando, come nel caso del precedente esempio con \cmd{forktest}, il padre
+termina con dei figli in stato di zombie\index{zombie}: alla sua terminazione
+infatti tutti i suoi figli (compresi gli zombie\index{zombie}) verranno
+adottati da \cmd{init}, il quale provvederà a completarne la terminazione.
-Si tenga presente infine che siccome gli zombie sono processi già usciti, non
-c'è modo di eliminarli con il comando \cmd{kill}; l'unica possibilità di
-cancellarli dalla tabella dei processi è quella di terminare il processo che
-li ha generati, in modo che \cmd{init} possa adottarli e provvedere a
-concluderne la terminazione.
+Si tenga presente infine che siccome gli zombie\index{zombie} sono processi
+già usciti, non c'è modo di eliminarli con il comando \cmd{kill}; l'unica
+possibilità di cancellarli dalla tabella dei processi è quella di terminare il
+processo che li ha generati, in modo che \cmd{init} possa adottarli e
+provvedere a concluderne la terminazione.
\subsection{Le funzioni \func{wait} e \func{waitpid}}
principale attende le richieste che vengono poi soddisfatte da una serie di
processi figli. Si è già sottolineato al paragrafo precedente come in questo
caso diventi necessario gestire esplicitamente la conclusione dei figli onde
-evitare di riempire di \textit{zombie} la tabella dei processi; le funzioni
-deputate a questo compito sono sostanzialmente due, \func{wait} e
+evitare di riempire di \textit{zombie}\index{zombie} la tabella dei processi;
+le funzioni deputate a questo compito sono sostanzialmente due, \func{wait} e
\func{waitpid}. La prima, il cui prototipo è:
\begin{functions}
\headdecl{sys/types.h}
In genere in un programma non si vuole essere forzati ad attendere la
conclusione di un processo per proseguire, specie se tutto questo serve solo
-per leggerne lo stato di chiusura (ed evitare la presenza di \textit{zombie}),
-per questo la modalità più usata per chiamare queste funzioni è quella di
-utilizzarle all'interno di un \textit{signal handler} (vedremo un esempio di
-come gestire \const{SIGCHLD} con i segnali in \secref{sec:sig_example}). In
-questo caso infatti, dato che il segnale è generato dalla terminazione di un
-figlio, avremo la certezza che la chiamata a \func{wait} non si bloccherà.
+per leggerne lo stato di chiusura (ed evitare la presenza di
+\textit{zombie}\index{zombie}), per questo la modalità più usata per chiamare
+queste funzioni è quella di utilizzarle all'interno di un \textit{signal
+ handler} (vedremo un esempio di come gestire \const{SIGCHLD} con i segnali
+in \secref{sec:sig_example}). In questo caso infatti, dato che il segnale è
+generato dalla terminazione di un figlio, avremo la certezza che la chiamata a
+\func{wait} non si bloccherà.
\begin{table}[!htb]
\centering
\textbf{Stato} & \texttt{STAT} & \textbf{Descrizione} \\
\hline
\hline
- \textbf{Runnable} & \texttt{R} & Il processo è in esecuzione o è pronto ad
- essere eseguito (cioè è in attesa che gli venga assegnata la CPU). \\
- \textbf{Sleep} & \texttt{S} & Il processo processo è in attesa di un
- risposta dal sistema, ma può essere interrotto da un segnale. \\
- \textbf{Uninterrutible Sleep} & \texttt{D} & Il processo è in
- attesa di un risposta dal sistema (in genere per I/O), e non può essere
- interrotto in nessuna circostanza. \\
+ \textbf{Runnable}& \texttt{R} & Il processo è in esecuzione o è pronto ad
+ essere eseguito (cioè è in attesa che gli
+ venga assegnata la CPU). \\
+ \textbf{Sleep} & \texttt{S} & Il processo processo è in attesa di un
+ risposta dal sistema, ma può essere
+ interrotto da un segnale. \\
+ \textbf{Uninterrutible Sleep}& \texttt{D} & Il processo è in
+ attesa di un risposta dal sistema (in
+ genere per I/O), e non può essere
+ interrotto in nessuna circostanza. \\
\textbf{Stopped} & \texttt{T} & Il processo è stato fermato con un
- \const{SIGSTOP}, o è tracciato.\\
- \textbf{Zombie} & \texttt{Z} & Il processo è terminato ma il suo stato di
- terminazione non è ancora stato letto dal padre. \\
+ \const{SIGSTOP}, o è tracciato.\\
+ \textbf{Zombie}\index{zombie} & \texttt{Z} & Il processo è terminato ma il
+ suo stato di terminazione non è ancora
+ stato letto dal padre. \\
\hline
\end{tabular}
\caption{Elenco dei possibili stati di un processo in Linux, nella colonna
\subsection{Le \textit{race condition}\index{race condition} e i
- \textit{deadlock}}
+ \textit{deadlock}\index{deadlock}}
\label{sec:proc_race_cond}
Si definiscono \textit{race condition} tutte quelle situazioni in cui processi
problematiche di questo tipo in \capref{cha:IPC}).
Un caso particolare di \textit{race condition} sono poi i cosiddetti
-\textit{deadlock}, particolarmente gravi in quanto comportano spesso il blocco
-completo di un servizio, e non il fallimento di una singola operazione. Per
-definizione un \textit{deadlock} è una situazione in cui due o più processi
-non sono più in grado di proseguire perché ciascuno aspetta il risultato di
-una operazione che dovrebbe essere eseguita dall'altro.
-
-
-L'esempio tipico di una situazione che può condurre ad un \textit{deadlock} è
-quello in cui un flag di ``occupazione'' viene rilasciato da un evento
-asincrono (come un segnale o un altro processo) fra il momento in cui lo si è
-controllato (trovandolo occupato) e la successiva operazione di attesa per lo
-sblocco. In questo caso, dato che l'evento di sblocco del flag è avvenuto
-senza che ce ne accorgessimo proprio fra il controllo e la messa in attesa,
-quest'ultima diventerà perpetua (da cui il nome di \textit{deadlock}).
+\textit{deadlock}\index{deadlock}, particolarmente gravi in quanto comportano
+spesso il blocco completo di un servizio, e non il fallimento di una singola
+operazione. Per definizione un \textit{deadlock}\index{deadlock} è una
+situazione in cui due o più processi non sono più in grado di proseguire
+perché ciascuno aspetta il risultato di una operazione che dovrebbe essere
+eseguita dall'altro.
+
+
+L'esempio tipico di una situazione che può condurre ad un
+\textit{deadlock}\index{deadlock} è quello in cui un flag di ``occupazione''
+viene rilasciato da un evento asincrono (come un segnale o un altro processo)
+fra il momento in cui lo si è controllato (trovandolo occupato) e la
+successiva operazione di attesa per lo sblocco. In questo caso, dato che
+l'evento di sblocco del flag è avvenuto senza che ce ne accorgessimo proprio
+fra il controllo e la messa in attesa, quest'ultima diventerà perpetua (da cui
+il nome di \textit{deadlock}\index{deadlock}).
In tutti questi casi è di fondamentale importanza il concetto di atomicità
visto in \secref{sec:proc_atom_oper}; questi problemi infatti possono essere
Il servizio prevede vari meccanismi di notifica, e, come ogni altro servizio
in un sistema unix-like, viene gestito attraverso un apposito programma,
\cmd{syslogd}, che è anch'esso un \textsl{demone}. In generale i messaggi di
-errore vengono raccolti dal file speciale \file{/dev/log}, un \textit{socket}
-locale (vedi \secref{sec:sock_sa_local}) dedicato a questo scopo, o via rete,
-con un \textit{socket} UDP, o da un apposito demone, \cmd{klogd}, che estrae i
-messaggi del kernel.\footnote{i messaggi del kernel sono tenuti in un buffer
- circolare e scritti tramite la funzione \func{printk}, analoga alla
- \func{printf} usata in user space; una trattazione eccellente dell'argomento
- si trova in \cite{LinDevDri}, nel quarto capitolo.}
+errore vengono raccolti dal file speciale \file{/dev/log}, un
+\textit{socket}\index{socket} locale (vedi \secref{sec:sock_sa_local})
+dedicato a questo scopo, o via rete, con un \textit{socket} UDP, o da un
+apposito demone, \cmd{klogd}, che estrae i messaggi del kernel.\footnote{i
+ messaggi del kernel sono tenuti in un buffer circolare e scritti tramite la
+ funzione \func{printk}, analoga alla \func{printf} usata in user space; una
+ trattazione eccellente dell'argomento si trova in \cite{LinDevDri}, nel
+ quarto capitolo.}
Il servizio permette poi di trattare i vari messaggi classificandoli
attraverso due indici; il primo, chiamato \textit{facility}, suddivide in
Le \acr{glibc} definiscono una serie di funzioni standard con cui un processo
può accedere in maniera generica al servizio di \textit{syslog}, che però
funzionano solo localmente; se si vogliono inviare i messaggi ad un'altro
-sistema occorre farlo esplicitamente con un socket UDP, o utilizzare le
-capacità di reinvio del servizio.
+sistema occorre farlo esplicitamente con un socket\index{socket} UDP, o
+utilizzare le capacità di reinvio del servizio.
La prima funzione definita dall'interfaccia è \func{openlog}, che apre una
connessione al servizio di \textit{syslog}; essa in generale non è necessaria
\const{SIGPROF} &SL & A & Timer del profiling scaduto \\
\const{SIGSYS} &SL & C & Argomento sbagliato per una subroutine (SVID) \\
\const{SIGTRAP} &SL & C & Trappole per un Trace/breakpoint \\
- \const{SIGURG} &SLB& B & Ricezione di una urgent condition su un socket\\
+ \const{SIGURG} &SLB& B & Ricezione di una \textit{urgent condition} su
+ un socket\index{socket}\\
\const{SIGVTALRM}&SLB& A & Virtual alarm clock \\
\const{SIGXCPU} &SLB& C & Ecceduto il limite sul CPU time \\
\const{SIGXFSZ} &SLB& C & Ecceduto il limite sulla dimensione dei file \\
In genere si intercettano questi segnali per permettere al programma di
terminare in maniera pulita, ad esempio per ripristinare le impostazioni della
-console o eliminare i file di lock prima dell'uscita. In questo caso il
-gestore deve concludersi ripristinando l'azione predefinita e rialzando il
-segnale, in questo modo il programma si concluderà senza effetti spiacevoli,
-ma riportando lo stesso stato di uscita che avrebbe avuto se il gestore non ci
-fosse stato.
+console o eliminare i file di lock\index{file!di lock} prima dell'uscita. In
+questo caso il gestore deve concludersi ripristinando l'azione predefinita e
+rialzando il segnale, in questo modo il programma si concluderà senza effetti
+spiacevoli, ma riportando lo stesso stato di uscita che avrebbe avuto se il
+gestore non ci fosse stato.
L'azione predefinita per tutti questi segnali è causare la terminazione del
processo che li ha causati. In genere oltre a questo il segnale provoca pure
L'azione predefinita è di essere ignorati. Questi segnali sono:
\begin{basedescript}{\desclabelwidth{2.0cm}}
\item[\const{SIGIO}] Questo segnale viene inviato quando un file descriptor è
- pronto per eseguire dell'input/output. In molti sistemi solo i socket e i
- terminali possono generare questo segnale, in Linux questo può essere usato
- anche per i file, posto che la \func{fcntl} abbia avuto successo.
+ pronto per eseguire dell'input/output. In molti sistemi solo i
+ socket\index{socket} e i terminali possono generare questo segnale, in Linux
+ questo può essere usato anche per i file, posto che la \func{fcntl} abbia
+ avuto successo.
\item[\const{SIGURG}] Questo segnale è inviato quando arrivano dei dati
- urgenti o \textit{out of band} su di un socket; per maggiori dettagli al
- proposito si veda \secref{sec:xxx_urgent_data}.
+ urgenti o \textit{out of band} su di un socket\index{socket}; per maggiori
+ dettagli al proposito si veda \secref{sec:xxx_urgent_data}.
\item[\const{SIGPOLL}] Questo segnale è equivalente a \const{SIGIO}, è
definito solo per compatibilità con i sistemi System V.
\end{basedescript}
presenta questa situazione è il seguente:
\begin{itemize}
\item la lettura da file che possono bloccarsi in attesa di dati non ancora
- presenti (come per certi file di dispositivo, i socket o le pipe).
+ presenti (come per certi file di dispositivo\index{file!di dispositivo}, i
+ socket\index{socket} o le pipe).
\item la scrittura sugli stessi file, nel caso in cui dati non possano essere
accettati immediatamente.
\item l'apertura di un file di dispositivo che richiede operazioni non
padre.\footnote{in realtà in SVr4 eredita la semantica di System V, in cui il
segnale si chiama \const{SIGCLD} e viene trattato in maniera speciale; in
System V infatti se si imposta esplicitamente l'azione a \const{SIG\_IGN} il
- segnale non viene generato ed il sistema non genera zombie (lo stato di
- terminazione viene scartato senza dover chiamare una \func{wait}). L'azione
- predefinita è sempre quella di ignorare il segnale, ma non attiva questo
- comportamento. Linux, come BSD e POSIX, non supporta questa semantica ed usa
- il nome di \const{SIGCLD} come sinonimo di \const{SIGCHLD}.} In generale
-dunque, quando non interessa elaborare lo stato di uscita di un processo, si
-può completare la gestione della terminazione installando un gestore per
-\const{SIGCHLD} il cui unico compito sia quello chiamare \func{waitpid} per
-completare la procedura di terminazione in modo da evitare la formazione di
-zombie.
+ segnale non viene generato ed il sistema non genera zombie\index{zombie} (lo
+ stato di terminazione viene scartato senza dover chiamare una \func{wait}).
+ L'azione predefinita è sempre quella di ignorare il segnale, ma non attiva
+ questo comportamento. Linux, come BSD e POSIX, non supporta questa semantica
+ ed usa il nome di \const{SIGCLD} come sinonimo di \const{SIGCHLD}.} In
+generale dunque, quando non interessa elaborare lo stato di uscita di un
+processo, si può completare la gestione della terminazione installando un
+gestore per \const{SIGCHLD} il cui unico compito sia quello chiamare
+\func{waitpid} per completare la procedura di terminazione in modo da evitare
+la formazione di zombie\index{zombie}.
In \figref{fig:sig_sigchld_handl} è mostrato il codice contenente una
implementazione generica di una routine di gestione per \const{SIGCHLD}, (che
di \secref{sec:proc_termination}, invocando \cmd{forktest} con l'opzione
\cmd{-s} (che si limita ad effettuare l'installazione di questa funzione come
gestore di \const{SIGCHLD}) potremo verificare che non si ha più la creazione
-di zombie.
+di zombie\index{zombie}.
% è pertanto
% naturale usare un esempio che ci permette di concludere la trattazione della
Allora, nel caso della terminazione dei processi figli, se si chiamasse
\func{waitpid} una sola volta, essa leggerebbe lo stato di terminazione per un
solo processo, anche se i processi terminati sono più di uno, e gli altri
-resterebbero in stato di zombie per un tempo indefinito.
+resterebbero in stato di zombie\index{zombie} per un tempo indefinito.
Per questo occorre ripetere la chiamata di \func{waitpid} fino a che essa non
ritorni un valore nullo, segno che non resta nessun processo di cui si debba
capitare (ad esempio se il sistema è molto carico) che il tempo di attesa
scada prima dell'esecuzione quest'ultima, cosicché essa sarebbe eseguita dopo
l'arrivo di \const{SIGALRM}. In questo caso ci si troverebbe di fronte ad un
-deadlock, in quanto \func{pause} non verrebbe mai più interrotta (se non in
-caso di un altro segnale).
+deadlock\index{deadlock}, in quanto \func{pause} non verrebbe mai più
+interrotta (se non in caso di un altro segnale).
Questo problema può essere risolto (ed è la modalità con cui veniva fatto in
SVr2) usando la funzione \func{longjmp} (vedi \secref{sec:proc_longjmp}) per
\const{SIGSEGV} e \const{SIGBUS} avvalorano \var{si\_addr} con l'indirizzo cui
è avvenuto l'errore, \const{SIGIO} (vedi \secref{sec:file_asyncronous_io})
avvalora \var{si\_fd} con il numero del file descriptor e \var{si\_band} per i
-dati urgenti su un socket.
+dati urgenti su un socket\index{socket}.
Benché sia possibile usare nello stesso programma sia \func{sigaction} che
\func{signal} occorre molta attenzione, in quanto le due funzioni possono
\item Ripristinare la maschera dei segnali originaria.
\end{enumerate*}
Per quanto possa sembrare strano bloccare la ricezione di un segnale per poi
-riabilitarla immediatamente dopo, in questo modo si evita il deadlock dovuto
-all'arrivo del segnale prima dell'esecuzione di \func{sigsuspend}.
+riabilitarla immediatamente dopo, in questo modo si evita il
+deadlock\index{deadlock} dovuto all'arrivo del segnale prima dell'esecuzione
+di \func{sigsuspend}.
\subsection{Ulteriori funzioni di gestione}
segnale \const{SIGCHLD} al padre, ma dato che non si è installato un
manipolatore e che l'azione predefinita per questo segnale è quella di essere
ignorato, non avendo predisposto la ricezione dello stato di terminazione,
-otterremo che il processo figlio entrerà nello stato di zombie (si riveda
-quanto illustrato in \secref{sec:sig_sigchld}), come risulterà ripetendo il
-comando \cmd{ps}:
+otterremo che il processo figlio entrerà nello stato di zombie\index{zombie}
+(si riveda quanto illustrato in \secref{sec:sig_sigchld}), come risulterà
+ripetendo il comando \cmd{ps}:
\begin{verbatim}
2356 pts/0 S 0:00 ./echod
2359 pts/0 Z 0:00 [echod <defunct>]
\end{verbatim}
-Poiché non è possibile lasciare processi zombie che pur inattivi occupano
-spazio nella tabella dei processi e a lungo andare saturerebbero le risorse
-del kernel, occorrerà ricevere opportunamente lo stato di terminazione del
-processo (si veda \secref{sec:proc_wait}), cosa che faremo utilizzando
+Poiché non è possibile lasciare processi zombie\index{zombie} che pur inattivi
+occupano spazio nella tabella dei processi e a lungo andare saturerebbero le
+risorse del kernel, occorrerà ricevere opportunamente lo stato di terminazione
+del processo (si veda \secref{sec:proc_wait}), cosa che faremo utilizzando
\const{SIGCHLD} secondo quanto illustrato in \secref{sec:sig_sigchld}.
La prima modifica al nostro server è pertanto quella di inserire la gestione
\noindent
all'esempio illustrato in \figref{fig:TCPsimpl_serv_code}, e linkando il tutto
alla funzione \code{sigchld\_hand}, si risolverà completamente il problema
-degli zombie.
+degli zombie\index{zombie}.
Iniziamo con una descrizione essenziale di cosa sono i \textit{socket} e di
quali sono i concetti fondamentali da tenere presente quando si ha a che fare
con essi.
+\index{socket|(}
+
\subsection{I \textit{socket}}
\label{sec:sock_socket_def}
\textit{dotted decimal} per IPv4 e quello descritto in
\secref{sec:IP_ipv6_notation} per IPv6.
+\index{socket|)}
\section{Un esempio di applicazione}
attiva e il terminale da cui lo si è lanciato è stato sconnesso),
occorrerebbero delle opportune modifiche.
+
+
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "gapil"
long f_blocks; /* blocchi totali nel filesystem */
long f_bfree; /* blocchi liberi nel filesystem */
long f_bavail; /* blocchi liberi agli utenti normali */
- long f_files; /* inodes totali nel filesystem */
- long f_ffree; /* inodes liberi nel filesystem */
+ long f_files; /* inode totali nel filesystem */
+ long f_ffree; /* inode liberi nel filesystem */
fsid_t f_fsid; /* filesystem id */
long f_namelen; /* lunghezza massima dei nomi dei file */
long f_spare[6]; /* riservati per uso futuro */
sono previsti errori.}
\end{prototype}
-La funzione è prevista in SVr4, 4.4BSD e SUSv2, anche se questo ultimo
+La funzione è prevista in SVr4, BSD 4.4 e SUSv2, anche se questo ultimo
standard la etichetta come obsoleta, mentre lo standard POSIX 1003.1-2001 la
ha eliminata. In Linux è implementata come una system call nelle architetture
in cui essa è necessaria, ed in genere restituisce il valore del simbolo
\end{prototype}
La funzione restituisce in ciascun elemento di \param{loadavg} il numero medio
-di processi attivi sulla coda dello scheduler, calcolato su un diverso
-intervalli di tempo. Il numero di intervalli che si vogliono leggere è
-specificato da \param{nelem}, dato che nel caso di Linux il carico viene
-valutato solo su tre intervalli (corrispondenti a 1, 5 e 15 minuti), questo è
-anche il massimo valore che può essere assegnato a questo argomento.
+di processi attivi sulla coda dello scheduler\index{scheduler}, calcolato su
+un diverso intervalli di tempo. Il numero di intervalli che si vogliono
+leggere è specificato da \param{nelem}, dato che nel caso di Linux il carico
+viene valutato solo su tre intervalli (corrispondenti a 1, 5 e 15 minuti),
+questo è anche il massimo valore che può essere assegnato a questo argomento.