-\chapter{I file: l'architettura}
+\chapter{L'architettura dei file}
\label{cha:file_intro}
Uno dei concetti fondamentali della architettura di unix è il cosiddetto
contempo tratteremo l'organizzazione dei file in un sistema unix-like, e le
varie caratteristiche distintive.
+
+
\section{L'organizzazione di file e directory}
\label{sec:file_organization}
stesso, ma anche essi devono essere montati all'interno dell'albero.
All'interno dello stesso albero si potranno poi inserire anche gli altri
-oggetti visti attraverso l'interfaccia che manipola i file come le FIFO, i
+oggetti visti attraverso l'interfaccia che manipola i file come le fifo, i
link, i socket e gli stessi i file di dispositivo (questi ultimi, per
convenzione, sono inseriti nella directory \file{/dev}).
directory.
Il nome completo di file generico è composto da una serie di nomi separati da
-una \texttt{/} (in Linux più \texttt{/} consecutive sono considerate
+una \file{/} (in Linux più \file{/} consecutive sono considerate
equivalenti ad una sola). Il nome completo di un file viene usualmente
chiamato \textit{pathname}, e anche se il manuale della glibc depreca questo
nome (poiché genererebbe confusione, dato che con \textit{path} si indica
indicati come directory esistano e siano effettivamente directory, inoltre i
permessi devono consentire l'accesso.
-Se il pathname comincia per \texttt{/} la ricerca parte dalla directory radice
+Se il pathname comincia per \file{/} la ricerca parte dalla directory radice
del processo; questa, a meno di un \textit{chroot} (su cui torneremo in
seguito, vedi \secref{sec:xxx_chroot}) è la stessa per tutti i processi ed
equivale alla directory radice dell'albero (come descritto in
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 file descriptors sono rappresentati da numeri interi (cioè
+dispositivi); i file descriptor sono rappresentati da numeri interi (cioè
semplici variabili di tipo \type{int}). L'interfaccia è definita
nell'header \file{unistd.h}.
\type{FILE *}. L'interfaccia è definita nell'header \type{stdio.h}.
Entrambe le interfacce possono essere usate per l'accesso ai file come agli
-altri oggetti del VFS (pipe, socket, device), ma per poter accedere alle
-operazioni di controllo sul particolare tipo di oggetto del VFS scelto occorre
-usare l'interfaccia standard di unix coi file descriptors. Allo stesso modo
-devono essere usati i file descriptor se si vuole ricorrere a modalità
-speciali di I/O come il polling o il non-bloccante (vedi
-\secref{sec:file_xxx}).
+altri oggetti del VFS (pipe, socket, device, sui quali torneremo in dettaglio
+a tempo opportuno), ma per poter accedere alle operazioni di controllo sul
+particolare tipo di oggetto del VFS scelto occorre usare l'interfaccia
+standard di unix coi file descriptor. Allo stesso modo devono essere usati i
+file descriptor se si vuole ricorrere a modalità speciali di I/O come il
+polling o il non-bloccante (vedi \secref{sec:file_noblocking}).
Gli stream forniscono un'interfaccia di alto livello costruita sopra quella
dei file descriptor, che tratta tutti i file nello stesso modo, con
% abbiamo brevemente accennato le caratteristiche (dal lato dell'implementazione
% nel kernel) in \secref{sec:file_vfs}.
+
\subsection{Il \textit{virtual filesystem} di Linux}
\label{sec:file_vfs}
\begin{figure}[htb]
\centering
- \includegraphics[width=7cm]{img/vfs.eps}
+ \includegraphics[width=7cm]{img/vfs}
\caption{Schema delle operazioni del VFS}
\label{fig:file_VFS_scheme}
\end{figure}
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
filesystem tutto quello che occorre è chiamare la funzione
-\func{register\_filesystem} passandole un'apposita struttura
+\code{register\_filesystem} passandole un'apposita struttura
(\var{file\_system\_type}) che contiene i dettagli per il riferimento
all'implementazione del medesimo, che sarà aggiunta alla citata tabella.
una apposita struttura che contiene vari dati come le informazioni comuni ad
ogni filesystem, i dati privati relativi a quel filesystem specifico, e i
puntatori alle funzioni del kernel relative al filesystem. Il VFS può così
-usare le funzioni contenute nel filesystem decriptor per accedere alle routine
+usare le funzioni contenute nel filesystem descriptor per accedere alle routine
specifiche di quel filesystem.
Gli altri due descrittori usati dal VFS sono relativi agli altri due oggetti
\label{sec:file_vfs_work}
La funzione più fondamentale implementata dal VFS è la system call
-\texttt{open} che permette di aprire un file. Dato un pathname viene eseguita
+\func{open} che permette di aprire un file. Dato un pathname viene eseguita
una ricerca dentro la \textit{directory entry cache} (in breve
\textit{dcache}), una tabella di hash che contiene tutte le \textit{directory
entry} (in breve \textit{dentry}) che permette di associare in maniera
rapida ed efficiente il pathname a una specifica dentry.
-Una singola 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, una FIFO, un file
-di dispositivo, o una qualsiasi altra cosa che possa essere rappresentata dal
-VFS (sui tipi di ``file'' possibili torneremo in seguito). A ciascuno di essi
-è associata pure una struttura che sta in memoria, e che oltre alle
-informazioni sullo specifico file contiene pure il riferimento alle funzioni
-(i \textsl{metodi}) da usare per poterlo manipolare.
-
-Le dentry ``vivono'' in memoria e non vengono mai salvate su disco, vengono
-usate per motivi di velocità, gli inode invece stanno su disco e vengono
-copiati in memoria quando serve, ed ogni cambiamento viene copiato
-all'indietro sul disco, gli inode che stanno in memoria sono inode del VFS
-ed è ad essi che puntano le singole dentry.
-
-La 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 dcache cioè contiene solo le dentry per i file per i quali è stato
-richiesto l'accesso), quando si vuole risolvere un nuovo pathname il VFS deve
-creare una nuova dentry e caricare l'inode corrispondente in memoria.
-
-Questo procedimento viene eseguito dal metodo \func{lookup()} dell'inode
+Una singola \textit{dentry} contiene in genere il puntatore ad un
+\textit{inode}; quest'ultimo è la struttura base che sta sul disco e che
+identifica un singolo oggetto del VFS sia esso un file ordinario, una
+directory, un link simbolico, una FIFO, un file di dispositivo, o una
+qualsiasi altra cosa che possa essere rappresentata dal VFS (sui tipi di
+``file'' possibili torneremo in seguito). A ciascuno di essi è associata pure
+una struttura che sta in memoria, e che oltre alle informazioni sullo
+specifico file contiene pure il riferimento alle funzioni (i \textsl{metodi})
+da usare per poterlo manipolare.
+
+Le \textit{dentry} ``vivono'' in memoria e non vengono mai salvate su disco,
+vengono usate per motivi di velocità, gli inode invece stanno su disco e
+vengono copiati in memoria quando serve, ed ogni cambiamento viene copiato
+all'indietro sul disco, gli inode che stanno in memoria sono inode del VFS ed
+è ad essi che puntano le singole \textit{dentry}.
+
+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.
+
+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.
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
-dentry e una struttura \verb|f_ops| che contiene i puntatori ai metodi che
-implementano le operazioni disponibili sul file. In questo modo i processi in
-user space possono accedere alle operazioni attraverso detti metodi, che
-saranno diversi a seconda del tipo di file (o dispositivo) aperto (su questo
-torneremo in dettaglio in \secref{sec:file_fd}). Un elenco delle operazioni
-previste dal kernel è riportato in \ntab.
+\textit{dentry} e una struttura \var{f\_ops} che contiene i puntatori ai
+metodi che implementano le operazioni disponibili sul file. In questo modo i
+processi in user space possono accedere alle operazioni attraverso detti
+metodi, che saranno diversi a seconda del tipo di file (o dispositivo) aperto
+(su questo torneremo in dettaglio in \secref{sec:file_fd}). Un elenco delle
+operazioni previste dal kernel è riportato in \ntab.
\begin{table}[htb]
\centering
\textbf{Funzione} & \textbf{Operazione} \\
\hline
\hline
- \textsl{\func{open}} & apre il file \\
- \textsl{\func{read}} & legge dal file \\
- \textsl{\func{write}} & scrive sul file \\
- \textsl{\func{llseek}} & sposta la posizione corrente sul file \\
- \textsl{\func{ioctl}} & accede alle operazioni di controllo
+ \textsl{\code{open}} & apre il file \\
+ \textsl{\code{read}} & legge dal file \\
+ \textsl{\code{write}} & scrive sul file \\
+ \textsl{\code{llseek}} & sposta la posizione corrente sul file \\
+ \textsl{\code{ioctl}} & accede alle operazioni di controllo
(tramite la \func{ioctl})\\
- \textsl{\func{readdir}}& per leggere il contenuto di una directory \\
- \textsl{\func{poll}} & \\
- \textsl{\func{mmap}} & chiamata dalla system call \func{mmap}.
+ \textsl{\code{readdir}}& per leggere il contenuto di una directory \\
+ \textsl{\code{poll}} & \\
+ \textsl{\code{mmap}} & chiamata dalla system call \func{mmap}.
mappa il file in memoria\\
- \textsl{\func{release}}& chiamata quando l'ultima referenza a un file
+ \textsl{\code{release}}& chiamata quando l'ultima referenza a un file
aperto è chiusa\\
- \textsl{\func{fsync}} & chiamata dalla system call \func{fsync} \\
- \textsl{\func{fasync}} & chiamate da \func{fcntl} quando è abilitato
+ \textsl{\code{fsync}} & chiamata dalla system call \func{fsync} \\
+ \textsl{\code{fasync}} & chiamate da \func{fcntl} quando è abilitato
il modo asincrono per l'I/O su file. \\
\hline
\end{tabular}
In questo modo per ciascun file diventano utilizzabili una serie di operazioni
(non è dette che tutte siano disponibili), che costituiscono l'interfaccia
astratta del VFS, e qualora se ne voglia eseguire una il kernel andrà ad
-utilizzare la opportuna routine dichiarata in \verb|f_ops| appropriata al tipo
+utilizzare la opportuna routine dichiarata in \var{f\_ops} appropriata al tipo
di file in questione.
Così sarà possibile scrivere sulla porta seriale come su un file di dati
normale; ovviamente certe operazioni (nel caso della seriale ad esempio la
-\textit{seek}) non saranno disponibili, però con questo sistema l'utilizzo di
+\code{seek}) non saranno disponibili, però con questo sistema l'utilizzo di
diversi filesystem (come quelli usati da Windows o MacOs) è immediato e
(relativamente) trasparente per l'utente ed il programmatore.
Dato un disco lo spazio fisico viene usualmente diviso in partizioni; ogni
partizione può contenere un filesystem; la strutturazione tipica
dell'informazione su un disco è riportata in \nfig; in essa si fa riferimento
-alla struttura del filesystem ext2, che prevede una separazione dei dati in
-\textit{blocks group} che replicano il superblock (ma sulle caratteristiche di
-ext2 torneremo in \secref{sec:file_ext2}). È comunque caratteristica
+alla struttura del filesystem \acr{ext2}, che prevede una separazione dei dati
+in \textit{blocks group} che replicano il superblock (ma sulle caratteristiche
+di \acr{ext2} torneremo in \secref{sec:file_ext2}). È comunque caratteristica
comune di tutti i filesystem unix, indipendentemente da come poi viene
strutturata nei dettagli questa informazione, prevedere una divisione fra la
lista degli inodes e lo spazio a disposizione per i dati e le directory.
\begin{figure}[htb]
\centering
- \includegraphics[width=9cm]{img/disk_struct.eps}
+ \includegraphics[width=9cm]{img/disk_struct}
\caption{Organizzazione dello spazio su un disco in partizioni e filesystem}
\label{fig:file_disk_filesys}
\end{figure}
\begin{figure}[htb]
\centering
- \includegraphics[width=11cm]{img/filesys_struct.eps}
+ \includegraphics[width=11cm]{img/filesys_struct}
\caption{Strutturazione dei dati all'interno di un filesystem}
\label{fig:file_filesys_detail}
\end{figure}
\begin{figure}[htb]
\centering
- \includegraphics[width=11cm]{img/dir_links.eps}
+ \includegraphics[width=11cm]{img/dir_links}
\caption{Organizzazione dei link per le directory}
\label{fig:file_dirs_link}
\end{figure}
cui si era partiti avrà un numero di riferiementi di almeno tre, in quanto
adesso sarà referenziata anche dalla voce \file{..} di \file{img}.
+
\subsection{Il filesystem \textsl{ext2}}
\label{sec:file_ext2}
Il filesystem standard usato da Linux è il cosiddetto \textit{second extended
- filesystem}, identificato dalla sigla \textsl{ext2}. Esso supporta tutte le
+ filesystem}, identificato dalla sigla \acr{ext2}. Esso supporta tutte le
caratteristiche di un filesystem standard unix, è in grado di gestire
filename lunghi (256 caratteri, estendibili a 1012), una dimensione fino a
4~Tb.
kernel quando agisce su gruppi di file. Possono essere settati su file e
directory e in quest'ultimo caso i nuovi file creati nella directory
ereditano i suoi attributi.
-\item sono supportate entrambe le semantiche di BSD e SYSV come opzioni di
+\item sono supportate entrambe le semantiche di BSD e SVr4 come opzioni di
montaggio. La semantica BSD comporta che i file in una directory sono creati
con lo stesso identificatore di gruppo della directory che li contiene. La
- semantica SYSV comporta che i file vengono creati con l'identificatore del
+ semantica SVr4 comporta che i file vengono creati con l'identificatore del
gruppo primario del processo, eccetto il caso in cui la directory ha il bit
- di sgid settato (per una descrizione dettagliata del significato di questi
- termini si veda \secref{sec:file_access_control}), nel qual caso file e
- sotto-directory ereditano sia il \acr{gid} che lo \acr{sgid}.
+ di \acr{sgid} settato (per una descrizione dettagliata del significato di
+ questi termini si veda \secref{sec:file_access_control}), nel qual caso file
+ e subdirectory ereditano sia il \acr{gid} che lo \acr{sgid}.
\item l'amministratore può scegliere la dimensione dei blocchi del filesystem
in fase di creazione, a seconda delle sue esigenze (blocchi più grandi
permettono un accesso più veloce, ma sprecano più spazio disco).
una maggiore affidabilità e possibilità di recupero in caso di corruzione del
superblock principale.
-
\begin{figure}[htb]
\centering
- \includegraphics[width=9cm]{img/dir_struct.eps}
+ \includegraphics[width=9cm]{img/dir_struct}
\caption{Struttura delle directory nel \textit{second extented filesystem}.}
\label{fig:file_ext2_dirs}
\end{figure}
-L'utilizzo di raggrupamenti di blocchi ha inoltre degli effetti positivi nelle
+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
-inodes.
+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
-
-