Initial revision
[gapil.git] / files.tex
diff --git a/files.tex b/files.tex
new file mode 100644 (file)
index 0000000..17878c5
--- /dev/null
+++ b/files.tex
@@ -0,0 +1,394 @@
+\chapter{I files}
+\label{cha:files}
+
+Uno dei concetti fondamentali del design di unix è il cosiddetto
+\textit{everything is a file}, cioè il fatto che l'accesso alle periferiche
+viene gestito attraverso un'interfaccia astratta che tratta nello stesso modo
+sia i normali file di dati che le periferiche. Si può accedere cioè a queste
+ultime (con l'eccezione delle interfacce di rete) attraverso i cosiddetti file
+di dispositivo (i \textit{device files}) che permettono ai programmi di
+interfacciarsi alle varie periferiche come la seriale, la parallela, la
+console, e gli stessi dischi.
+
+\section{Concetti generali sui files}
+\label{sec:file_gen}
+
+Per poter accedere ai singoli file su disco è necessario l'uso di un
+\textit{filesystem}, cioè di un'interfaccia del kernel che permetta di
+strutturare l'informazione tenuta sullo spazio grezzo disponibile sui dischi
+in files e directory. Sarà quest'ultimo a gestire l'accesso ai dati
+memorizzati all'interno del disco stesso, e l'uso delle normali directory e
+files.
+
+%In generale un filesystem piazzerà opportunamente sul disco dei blocchi di
+%informazioni riservate che tengono conto degli inodes allocati, di quelli
+%liberi, e delle posizioni fisiche su disco dei dati contenuti nei files, per
+
+Per poter accedere ai file contenuti in un disco occorrerà poi attivare il
+filesystem \textit{montando} il disco (o la partizione del disco). 
+
+In unix, a differenza di quanto avviene in altri sistemi operativi, tutti i
+file vengono tenuti all'interno di un unico albero la cui radice (la directory
+di \textit{root}) viene montata all'avvio. Pertanto un file viene identificato
+dall'utente usando il suo \textit{pathname}, cioè il percorso completo che si
+deve fare a partire dalla radice, per accedere al file.
+
+Dopo la fase di inizializzazione il kernel riceve dal boot loader
+l'indicazione di quale dispositivo contiene il filesystem da usare come punto
+di partenza e questo viene montato come radice dell'albero (cioè nella
+directory \texttt{/}); tutti gli ulteriori dischi devono poi essere inseriti
+nell'albero utilizzando opportune sottodirectory (in genere sotto
+\texttt{/mnt}) .
+
+Alcuni filesystem speciali (come \texttt{/proc} che contiene un'interfaccia ad
+alcune strutture interne del kernel) sono generati automaticamente dal kernel
+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 files come le FIFO, i
+link, i socket e gli stessi i file di dipositivo (questi ultimi, per
+convenzione, sono inseriti nella directory \texttt{/dev}).
+
+\subsection{Il \textit{virtual filesystem} di linux}
+\label{sec:file_vfs}
+
+Esamineremo adesso come viene implementato l'accesso ai files in linux. Questa
+sezione riporta informazioni sui dettagli di come il kernel gestisce i files,
+ed è basata sul documento di Richard Goochs distribuito coi sorgenti del
+kernel (\texttt{linux/Documentation/vfs.txt}).
+
+L'argomento è abbastanza ``esoterico'' e questa sezione può essere saltata ad
+una prima lettura; è bene però tenere presente che vengono intodotti qui
+alcuni termini che potranno comparire in seguito, come \textit{imode},
+\textit{dentry}, \textit{dcache}.
+
+In linux il concetto di \textit{everything is a file} è stato implementato
+attraverso il \textit{virtual filesystem} (da qui in avanti VFS) che è
+l'interfaccia astratta che il kernel rende disponibile ai programmi in user
+space attraverso la quale vengono manipolati i files, esso provvede anche
+un'astrazione delle operazioni di manipolazione sui files che permette la
+coesistenza di diversi filesystem all'interno dello stesso kernel.
+
+La funzione più importante implementata dal VFS è la system call \texttt{open}
+che permette di aprire un file. Dato un pathname viene eseguita una ricerca
+dentro la \textit{directory entry cache} (la \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 che può essere un file, una directory, una FIFO, un file di
+dispositivo, o una qualsiasi altra cosa che possa essere rappresentata dal
+VFS (sui tipi di files possibili torneremo in seguito).
+
+Le dentries ``vivono'' in memoria e non vengono mai salvate su disco, vengono
+usate per motivi di velocità, gli inodes invece vivono su disco e vengono
+copiati in memoria quando serve, ed ogni cambiamento viene copiato
+all'indietro sul disco, gli inodes che stanno in memoria sono inodes del VFS
+ed è ad essi che puntano le singole dentry.
+
+La dcache costituisce perciò una sorta di vista completa di tutto l'albero dei
+files, 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 dalla metodo \texttt{lookup()} dell'inode
+della directory che contiene il file; esso viene installato dallo specifico
+filesystem su cui l'inode va a vivere.
+
+Una volta che il VFS ha a disposizione la dentry (ed il relativo inode)
+diventa possibile accedere a operazioni sul file come la \texttt{open} per
+aprire il file o la \texttt{stat} per leggere i dati dell'inode e passarli in
+user space.
+
+L'apertura di un file richiede comunque un'altra operazione, l'allocazione di
+una struttura di tipo \texttt{file} in cui viene inserito un puntatore alla
+dentry e una struttura \verb|f_ops| che contiene i puntatotori ai metodi che
+implementano le operazioni disponibili sul file. In questo modo i processi in
+use space possono accedere alle operazioni attraverso detti metodi, che
+saranno diversi a seconda del tipo di file (o dispositivo) aperto. Un elenco
+delle operazioni disponibili è riportato in \ntab.
+
+% La struttura file viene poi inserita nella tavola dei file
+% , non
+% tutte le operazioni possibili sono definite per tutti i dispositivi; un elenco
+% delle operazioni definite in linux è riportato in \ntab.
+
+\begin{table}[htb]
+  \centering
+  \begin{tabular}[c]{c l}
+    \textbf{funzione} & \textbf{operazione} \\
+    \hline
+    open & apre il file \\
+    read & legge dal file \\
+    write & scrive sul file \\ 
+    llseek & sposta la posizione corrente sul file \\
+    ioctl & accede alle operazioni di controllo (tramite la \texttt{ioctl})\\
+    readdir & per leggere il contenuto di una directory \\
+    poll & \\
+    mmap & chiamata dalla system call \texttt{mmap} \\
+    release & chiamata quando l'ultima referenza a un file aperto è chiusa\\
+    fsync & chiamata dalla system call \texttt{fsync} \\
+    fasync & chiamate da \texttt{fcntl} quando è abilitato il modo asincrono
+    per l'I/O su file. \\
+    \hline
+  \end{tabular}
+  \caption{Operazioni sui file definite nel VFS.}
+  \label{tab:file_operations}
+\end{table}
+
+In questo modo per ciascun file diventano utilizzabili una serie di operazioni
+(non è dette che tutte siano disponibili), che costituiscono l'interfaccia
+astratta del VFS, e qualora se ne voglia eseguire una il kernel andrà ad
+utilizzare la opportuna routine dichiarata in \verb|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 la \textit{seek}) non saranno
+disponibili, però con questo sistema l'utilizzo di diversi filesystem è
+immediato e (relativamente) trasparente per l'utente ed il programmatore.
+
+\subsection{Proprietari e permessi}
+\label{sec:file_perms}
+
+In unix è implementata da qualunque filesystem standard una forma elementare
+(ma adatta alla maggior parte delle esigenze) di controllo di accesso ai
+files. Torneremo sull'argomento in dettaglo più avanti, qui ci limitiamo ad
+una introduzione dei concetti essenziali.
+
+Si tenga conto poi che quanto diremo è vero solo per filesystem di tipo unix,
+e non è detto che sia applicabile (ed infatti non è vero per il filesystem di
+windows) a un filesystem qualunque. Esistono inoltre estensioni che permettono
+di implementare le ACL (\textit{Access Control List}) che sono un meccanismo
+di controllo di accesso molto più sofisticato.
+
+Ad ogni file unix associa sempre l'utente che ne è proprietario (il cosiddetto
+\textit{owner}) e il gruppo di appartenenza, secondo il meccanismo degli uid e
+gid spiegato in \ref{sec:intro_usergroup}, e un insieme di permessi che sono
+divisi in tre classi, e cioè attribuiti rispettivamente al proprietario, a
+qualunque utente faccia parte del gruppo cui appartiene il file, e a tutti gli
+altri utenti.
+
+I permessi sono espressi da un insieme di 12 bit, di questi i nove meno
+significativi sono usati a gruppi di tre per indicare i permessi base di
+lettura, scrittura ed esecuzione (indicati rispettivamente con le lettere
+\textit{w}, \textit{r} \textit{x}) applicabili rispettivamente al
+proprietario, al gruppo, a tutti. I restanti tre bit sono usati per indicare
+alcune caratteristiche più complesse (\textit{suid}, \textit{sgid}, e
+\textit{sticky}) su cui torneremo in seguito.
+
+Tutte queste informazioni sono tenute per ciascun file nell'inode. Quando un
+processo cerca l'accesso al file esso controlla i propri uid e gid
+confrontandoli con quelli del file e se l'operazione richiesta è compatibile
+con i permessi associati al file essa viene eseguita, altrimenti viene
+bloccata ed è restituito un errore di \texttt{EPERM}. Questo procedimento non
+viene eseguito per l'amministratore di sistema (il cui uid è zero) il quale ha
+pertanto accesso senza restrizione a qualunque file del sistema.
+
+In realtà il procedimento è più complesso di quanto descritto in maniera
+elementare qui, in quanto ad un processo sono associati diversi
+identificatori, torneremo su questo in maggiori dettagli in seguito in
+\ref{sec:proc_perms}.
+
+\subsection{I tipi di files}
+\label{sec:file_types}
+
+Come detto in precedenza esistono vari tipi di oggetti implementati del VFS
+per i quali è disponibile l'interfaccia astratta da esso provveduta. Un elenco
+dei vari tipi di file è il seguente:
+\begin{table}[htb]
+  \begin{center}
+    \begin{tabular}[c]{l l l}
+      \textit{regular file} & \textsl{file normale} &
+      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 inodes \\
+      \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 sequenziale \\
+      \textit{block device} & \textsl{dispositivo a blocchi} &
+      un file che identifica una periferica ad accesso diretto \\
+      \textit{fifo} & \textsl{tubo} &
+      un file speciale che identifica una linea di comunicazione software
+      (unidirezionale) \\
+      \textit{socket} & \textsl{presa}
+      un file speciale che identifica una linea di comunicazione software
+      (bidirezionale) \\
+    \end{tabular}
+    \caption{Tipologia dei file definiti nel VFS}
+    \label{tab:file_types}
+  \end{center}
+\end{table}
+
+Tutto ciò non ha ovviamente nulla a che fare con la classificazione sui tipi
+di file (in questo caso file di dati) in base al loro contenuto, o tipo di
+accesso.  Una delle differenze principali con altri sistemi operativi (come il
+VMS o windows) è che per unix tutti i file di dati sono identici e contengono
+un flusso continuo di bytes; non esiste cioè differenza per come vengono visti
+dal sistema file di diverso contenuto o formato (come nel caso di quella fra
+file di testo e binari che c'è in windows) né c'è una strutturazione a record
+per il cosiddetto ``accesso diretto'' come nel caso del VMS.
+
+Una seconda differenza è nel formato dei file ascii; in unix la fine riga è
+codificata in maniera diversa da windows o macintosh, in particolare il fine
+riga è il carattere \texttt{LF} al posto del \texttt{CR} del mac e del
+\texttt{CR LF} di windows. Questo può causare alcuni problemi qualora si
+facciano assunzioni sul terminatore della riga.
+
+
+\section{Una panoramica sull'I/O sui file}
+\label{sec:file_io_base}
+
+Per poter accedere al contenuto dei file occorre anzitutto aprirlo. Questo
+crea un canale di comunicazione che permette di eseguire una serie di
+operazioni. Una volta terminate le operazioni, il file dovrà essere chiuso, e
+questo chiuderà il canale di comuniczione impedendo ogni ulteriore operazione.
+
+
+\subsection{Le due interfacce ai file}
+\label{sec:file_base_api}
+
+In unix le modalità di accesso ai file e le relative insterfacce di
+programmazione sono due, basate su due diversi meccanismi di connessione. 
+
+La prima è quella dei cosiddetti \textit{file descriptor} (descrittore di
+file), che è specifica di unix e che provvede un accesso diretto a basso
+livello non bufferizzato, con un'interfaccia primitiva ed essenziale; i file
+descriptors sono rappresetati da numeri interi (cioè semplici variabili di
+tipo \texttt{int}). L'interfaccia è definita nell'header \texttt{unistd.h}.
+
+La seconda è quella degli \textit{stream}, che provvede un'interfaccia più
+complessa e un'accesso bufferizzato, questa è anche l'intefaccia standard
+usata dal linguaggio C e che perciò si trova anche su tutti i sistemi non
+unix. Gli stream sono più complessi e sono rappresentati da puntatori ad un
+opportuno oggetto, cioè del tipo \texttt{FILE *}. L'interfaccia è definita
+nell'header \texttt{stdio.h}.
+
+Entrambe le interfacce possono essere usate per l'accesso ai file come agli
+altri oggetti del VFS (pipes, socket, device), ma per poter accedere alle
+operazioni di controllo sul particolare tipo di oggetto del VFS scelto occorre
+usare l'interfaccia a basso livello dei 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 \ref{sec:file_bohhhhh}).
+
+Gli stream però forniscono un'interfaccia di alto livello costruita sopra
+quella dei file descriptor, e tratta tutti i file nello stesso modo, con
+l'eccezione di poter scegliere tre diversi stili di bufferizzazione.  Il
+maggior vantaggio degli stream è che l'interfaccia per le operazioni di
+input/output è enormemente più ricca di quella dei file descriptor, che
+provvedono solo funzioni elementari per la lettura/scrittura diretta di
+blocchi di bytes.  In particolare 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 a
+basso livello dei file decriptor, è sempre possibile extrarre il file
+descriptor da uno stream ed eseguirvi operazioni di basso livello, o associare
+in un secondo tempo uno stream ad un file descriptor.
+
+In generale, se non necessitano specificamente le funzionalità di basso
+livello, è opportuno usare sempre gli stream per la loro maggiore portabilità
+essendo questi ultimi definito nello stadard ISO C; l'interfaccia con i file
+descriptor invece segue solo lo standard POSIX.1 dei sistemi unix ed è
+pertanto di portabilità più limitata.
+
+\subsection{Caratteristiche specifiche dei file in unix}
+\label{sec:files_spec_unix}
+
+Essendo un sistema multitasking e multiutente esistono alcune caratteristiche
+specifiche di unix che devono essere tenute in conto nell'acceso ai file. È
+infatti normale che più processi o programmi possano accedere
+contemporaneamente allo stesso file e devono poter eseguire le loro operazioni
+indipendentemente da quello che fanno gli altri processi.
+
+Per questo motivo le strutture usate per all'accesso ai file sono relative al
+processo che effettua l'accesso.  All'apertura di ogni file infatti viene
+creata all'interno del processo una apposita struttura in cui sono memorizzati
+tutti gli attributi del medesimo, che viene utilizzata per tutte le
+operazioni. Questa è una struttura che resta locale al processo stesso; in
+questo modo processi diversi possono usare le proprie strutture locali per
+accedere ai file (che può essere sempre lo stesso) in maniera assolutamente
+indipendente.
+
+Questo ha delle conseguenze di cui è bene tenere conto; ad esempio in tutti i
+sistemi POSIX uno degli attibuti di un file aperto è la posizione corrente nel
+file, cioè il punto nel file in cui verrebbe letto o scritto alla operazione
+successiva. Essa è rappresentata da un numero intero che indica il numero di
+bytes dall'inizio del file, che viene (a meno che non si apra il file in
+append) inizializzato a zero all'apertura del medesimo. 
+
+
+Questo è uno dei dati che viene mantento nella suddetta struttura, per cui
+ogni processo avrà la sua posizione corrente nel file, che non sarà
+influenzata da quello che altri processi possono fare. Anzi, aprire un file
+significa appuntio creare ed inizializzare una tale struttura, per cui se si
+apre due volte lo stesso file all'interno dello stesso processo, si otterrano
+due file descriptor o due stream che avreanno ancora un posizione corrente nel
+file assolutamente independente.
+
+Si tenga conto inoltre che un'altro dei dati contenuti nella struttura di
+accesso è un riferimento all'inode del file, pertanto anche se il file viene
+cancellato da un altro processo, sarà sempre possibile mantenere l'accesso,
+e lo spazio su disco non verrà rilasciato fintanto che il file non sarà chiuso
+e l'ultimo riferimento cancellato. È pertanto possibile (e pratica comune)
+aprire un file provvisorio per cancellarlo immediatamente dopo; in questo modo
+all'uscita del programma il file scomparità definitivamente dal disco, ma il
+file ed il suo contenuto saranno disponibili per tutto il tempo in cui il
+processo è attivo.
+
+
+\subsection{L'accesso ai files: nomi e direcory}
+\label{sec:file_names}
+
+L'organizzazione dei nomi dei file deriva direttamente dall'organizzazione dei
+medesimi nella gerarchia di directory descritta in \ref{sec:file_base}; una
+directory comunque, come già specificato in \ref{sec:file_vfs}, è solo un
+particolare tipo di file che il sistema riconosce come tale, che contiene le
+informazioni che associano un inode ad un nome. Per questo, anche se è usuale
+parlare di ``file in una directory'' in realtà una directory contiene solo dei
+riferimenti ai file, non i file stessi.
+
+I nomi contenuti nelle directory sono chiamati componenti (\textit{file name
+  components}), un file può essere indicato rispetto alla directory corrente
+semplicemente specificando il nome da essa contenuto. Una directory contiene
+semplicemente un elenco di questi componenti, che possono corrispondere a un
+qualunque oggetto del filesystem, compresa un'altra directory.
+
+Un nome di file è composto da una serie di componenti separati da una
+\texttt{/} (in linux più \texttt{/} consecutive sono considerate equivalenti
+ad una sola). Il nome completo di un file viene usualmente chiamato
+\textit{pathname}, anche se questo può generare confusione, dato che con
+\textit{path} si indica anche un insieme di directory su cui effettuare una
+ricerca (ad esempio quello in cui si cercano i comandi).
+
+Il processo con cui si associa ad un pathname uno specifico file (cioè si
+identifica l'inode a cui fare riferimento) è chiamato risoluzione del nome
+(\textit{file name resoluzion} o \textit{pathname resolution}).
+La risoluzione viene fatta esaminando il pathname da destra a sinistra e
+localizzando ogni componente nella directory indicata dal componente
+precedente: ovviamente perchè il procedimento funzioni occorre che i
+componenti indicati come directory esistano e siano effettivamente directory,
+inoltre i permessi devono consenite l'accesso.
+
+
+Se il pathname comincia per \texttt{/} la ricerca parte dalla directory radice
+del processo che (a meno di un \textit{chroot} su cui torneremo in seguito) è
+la stessa per tutti i processi ed equivale alla radice dell'albero
+(\ref{sec:file_gen}): in questo caso si parla di un pathname
+\textsl{assoluto}. Altrimenti la ricerca parte dalla directory di lavoro
+corrente (\texit{current working directory}, su cui pure torneremo più avanti)
+ed il pathname è detto \textsl{relativo}.
+
+I componenti \texttt{.} e \texttt{..} hanno un significato speciale e vengono
+inseriti in ogni directory; 
+
+
+%La struttura fondamentale che contiene i dati essenziali relativi ai file è il
+%cosiddetto \textit{inode}; questo conterrà informazioni come il
+%tipo di file (file di dispositivo, directory, file di dati, per un elenco
+%completo vedi \ntab), i permessi (vedi \ref{sec:file_perms}), le date (vedi
+%\ref{sec:file_times}).