X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;ds=sidebyside;f=files.tex;fp=files.tex;h=17878c5775ea7bb1d16c452d2d7bf7a4bb50168b;hb=3d44c36183fe67ed64bff95a36596ad87f620683;hp=0000000000000000000000000000000000000000;hpb=d05c744fc23f1ba7ec85c8268abdc8ee8426d950;p=gapil.git diff --git a/files.tex b/files.tex new file mode 100644 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}).