X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileintro.tex;h=92efd2abf427b68a5802689583509c9697537cda;hp=c03e36032d1b25e5e39a42c322eb9b982a0cf4e2;hb=b99370b46df36884995d94ed00de41aa51b4e027;hpb=55bb7b212e8a450f8c9c0f840d5cf0de4c428380 diff --git a/fileintro.tex b/fileintro.tex index c03e360..92efd2a 100644 --- a/fileintro.tex +++ b/fileintro.tex @@ -1,401 +1,722 @@ -\chapter{I files: introduzione} -\label{cha:files_intro} - -Uno dei concetti fondamentali della architettura di unix è il cosiddetto -\textit{everything is a file}, cioè il fatto che l'accesso ai vari dispositivi -di input/output del computer viene effettuato attraverso un'interfaccia -astratta che tratta le periferiche allo stesso modo degli usuali file di -dati. - -Questo significa che si può accedere cioè a qualunque periferica del computer, +% fileintro.tex +%% +%% Copyright (C) 2000-2003 Simone Piccardi. Permission is granted to +%% copy, distribute and/or modify this document under the terms of the GNU Free +%% Documentation License, Version 1.1 or any later version published by the +%% Free Software Foundation; with the Invariant Sections being "Prefazione", +%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the +%% license is included in the section entitled "GNU Free Documentation +%% License". +%% +\chapter{L'architettura dei file} +\label{cha:file_intro} + +Uno dei concetti fondamentali dell'architettura di un sistema Unix è il +cosiddetto \textit{everything is a file}, cioè il fatto che l'accesso ai vari +dispositivi di input/output del computer viene effettuato attraverso +un'interfaccia astratta che tratta le periferiche allo stesso modo dei normali +file di dati. + +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 files}). 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 un'introduzione alle principali caratteristiche -di questa interfaccia, su come essa viene implementata in Linux e su come sono -organizzati i file nel sistema. - - -\section{I file in un sistema unix-like} -\label{sec:fileintr_overview} - -Visto il ruolo fondamentale che i files vengono ad assumere in un sistema -unix, è anzitutto opportuno fornire un'introduzione dettagliata su come essi -vengono trattati dal sistema. In particolare occorre tenere presente dov'è che -si situa il limite fondamentale fra kernel space e user space che tracciavamo -al \capref{cha:intro_unix}. - -Partiamo allora da come viene strutturata nel sistema la disposizione dei -file: per potervi accedere il kernel usa una apposita interfaccia che permetta -di strutturare l'informazione tenuta sullo spazio grezzo disponibile sui -dischi, cioè quello che si chiama un \textit{filesystem} (useremo per brevità -questo nome al posto della più prolissa traduzione italiana sistema di file). - -Sarà attraverso quest'ultimo che il kernel andrà a gestire l'accesso ai dati -memorizzati all'interno del disco stesso, strutturando l'informazione in files -e directory (su questo aspetto torneremo con maggiori dettagli in -\secref{sec:filedir_filesystem}). Per poter accedere ai file contenuti in un -disco occorrerà perciò attivare il filesystem, questo viene fatto -\textsl{montando} il disco (o la partizione del disco). - -%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 - -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 quello che viene chiamato \textit{pathname}, cioè il -percorso che si deve fare 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 subdirectory. - -Alcuni filesystem speciali (come \texttt{/proc} che contiene un'interfaccia ad +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 +interfacce con cui i processi accedono ai file (che tratteremo in dettaglio +nei capitoli seguenti), per poi passare ad una descrizione più dettagliata +delle modalità con cui detto accesso viene realizzato dal sistema. + + + +\section{L'architettura generale} +\label{sec:file_access_arch} + +Per poter accedere ai file, il kernel deve mettere a disposizione dei +programmi le opportune interfacce che consentano di leggerne il contenuto; il +sistema cioè deve provvedere ad organizzare e rendere accessibile in maniera +opportuna l'informazione tenuta sullo spazio grezzo disponibile sui dischi. +Questo viene fatto strutturando l'informazione sul disco attraverso quello che +si chiama un \textit{filesystem} (vedi \ref{sec:file_arch_func}), essa poi +viene resa disponibile ai processi attraverso quello che viene chiamato il +\textsl{montaggio} del \textit{filesystem}. +% (approfondiremo tutto ciò in \secref{sec:file_arch_func}). + +In questa sezione faremo una panoramica generica su come il sistema presenta +i file ai processi, trattando l'organizzazione di file e directory, i tipi di +file ed introducendo le interfacce disponibili e le loro caratteristiche. + + +\subsection{L'organizzazione di file e directory} +\label{sec:file_organization} + +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 (quella che +viene chiamata \textit{root directory}) viene montata all'avvio. Un file +viene identificato dall'utente usando quello che viene chiamato +\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 bootloader 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 \file{/}); tutti gli ulteriori filesystem +che possono essere su altri dispositivi dovranno poi essere inseriti +nell'albero montandoli su opportune directory del filesystem montato come +radice. + +Alcuni filesystem speciali (come \file{/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 dispositivo (questi ultimi, per -convenzione, sono inseriti nella directory \texttt{/dev}). - -\subsection{Il \textit{virtual filesystem} di Linux} -\label{sec:fileintr_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. -L'argomento è abbastanza ``esoterico'' e questa sezione può essere saltata ad -una prima lettura; è bene però tenere presente che vengono introdotti qui -alcuni termini che potranno comparire in seguito, come \textit{inode}, -\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 che il kernel rende disponibile ai programmi in user space -attraverso la quale vengono manipolati i files; esso provvede un livello di -indirezione che permette di collegare le operazioni di manipolazione sui files -alle operazioni di I/O e gestisce l'organizzazione di questi ultimi nei vari -modi in cui diversi filesystem la effettuano, permettendo la coesistenza -di filesystem differenti all'interno dello stesso albero delle directory - -Quando un processo esegue una system call che opera su un file il kernel -chiama sempre una funzione implementata nel VFS; la funzione eseguirà le -manipolazioni sulle strutture generiche e ridirigendo la chiamata alla -opportune routine del filesystem specifico a cui si fa riferimento, saranno -queste a chiamare le funzioni di piu basso livello che eseguono le operazioni -di I/O sul dispositivo fisico, secondo lo schema riportato in \nfig. - -\begin{figure}[htb] - \centering - - \caption{Schema delle operazioni del VFS} - \label{fig:fileintro_VFS_scheme} -\end{figure} - -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} (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 ``files'' 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 dentries ``vivono'' in memoria e non vengono mai salvate su disco, vengono -usate per motivi di velocità, gli inodes invece stanno 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 dal metodo \texttt{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. - -Una volta che il VFS ha a disposizione la dentry (ed il relativo inode) -diventa possibile accedere alle varie 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 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:fileunix_fd}). Un elenco delle operazioni -previste dal kernel è riportato in \ntab. +stesso, ma anche essi devono essere montati all'interno dell'albero dei file. + +Una directory, come vedremo in maggior dettaglio in +\secref{sec:file_vfs_work}, è anch'essa un file, solo che è un file +particolare che il kernel riconosce come tale. Il suo scopo è quello di +contenere una lista di nomi di file e le informazioni che associano ciascun +nome al contenuto. Dato che questi nomi possono corrispondere ad un qualunque +oggetto del filesystem, compresa un'altra directory, si ottiene naturalmente +un'organizzazione ad albero inserendo directory in altre directory. + +Un file può essere indicato rispetto alla directory corrente semplicemente +specificandone il nome\footnote{Il manuale delle \acr{glibc} chiama i nomi + contenuti nelle directory \textsl{componenti} (in inglese \textit{file name + 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\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 +procedimento con cui si individua il file a cui esso fa riferimento è chiamato +risoluzione del nome (\textit{file name resolution} o \textit{pathname + resolution}). La risoluzione viene fatta esaminando il \textit{pathname} da +sinistra a destra e localizzando ogni nome nella directory indicata dal nome +precedente usando \file{/} come separatore\footnote{nel caso di nome vuoto, il + costrutto \file{//} viene considerato equivalente a \file{/}.}: ovviamente, +perché il procedimento funzioni, occorre che i nomi indicati come directory +esistano e siano effettivamente directory, inoltre i permessi (si veda +\secref{sec:file_access_control}) devono consentire l'accesso all'intero +\textit{pathname}. + +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}. + +I nomi \file{.} e \file{..} hanno un significato speciale e vengono inseriti +in ogni directory: il primo fa riferimento alla directory corrente e il +secondo alla directory \textsl{genitrice} (o \textit{parent directory}) cioè +la directory che contiene il riferimento alla directory corrente; nel caso la +directory corrente coincida con la directory radice, allora il riferimento è a +se stessa. + + +\subsection{I tipi di file} +\label{sec:file_file_types} + +Come detto in precedenza, in Unix esistono vari tipi di file; in Linux questi +sono implementati come oggetti del \textit{Virtual File System} (vedi +\secref{sec:file_vfs_work}) e sono presenti in tutti i filesystem unix-like +utilizzabili con Linux. L'elenco dei vari tipi di file definiti dal +\textit{Virtual File System}\index{Virtual File System} è riportato in +\tabref{tab:file_file_types}. + +Si tenga ben presente che questa classificazione non ha nulla a che fare con +la classificazione dei file (che in questo caso sono sempre file di dati) in +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}\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 \centering - \begin{tabular}[c]{c p{7cm}} - \textbf{funzione} & \textbf{operazione} \\ + \begin{tabular}[c]{|l|l|p{7cm}|} \hline - \textit{open} & apre il file \\ - \textit{read} & legge dal file \\ - \textit{write} & scrive sul file \\ - \textit{llseek} & sposta la posizione corrente sul file \\ - \textit{ioctl} & accede alle operazioni di controllo - (tramite la \texttt{ioctl})\\ - \textit{readdir} & per leggere il contenuto di una directory \\ - \textit{poll} & \\ - \textit{mmap} & chiamata dalla system call \texttt{mmap}. - mappa il file in memoria\\ - \textit{release} & chiamata quando l'ultima referenza a un file - aperto è chiusa\\ - \textit{fsync} & chiamata dalla system call \texttt{fsync} \\ - \textit{fasync} & chiamate da \texttt{fcntl} quando è abilitato - il modo asincrono per l'I/O su file. \\ + \multicolumn{2}{|c|}{\textbf{Tipo di file}} & \textbf{Descrizione} \\ \hline - \end{tabular} - \caption{Operazioni sui file definite nel VFS.} - \label{tab:fileintr_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 della seriale ad esempio la -\textit{seek}) non saranno disponibili, però con questo sistema l'utilizzo di -diversi filesystem (come quelli usati da Windows o MacOs) è immediato e -(relativamente) trasparente per l'utente ed il programmatore. - -\subsection{Il controllo di accesso} -\label{sec:fileintr_access_ctrl} - -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 dettaglio più avanti (vedi -\secref{sec:filedir_access_control}), 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 accennato in \secref{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 (una descrizione più dettagliata dei vari -permessi associati ai file è riportata in \secref{sec:filedir_suid_sgid}). I -restanti tre bit sono usati per indicare alcune caratteristiche più complesse -(\textit{suid}, \textit{sgid}, e \textit{sticky}) su cui pure torneremo in -seguito (vedi \secref{sec:filedir_suid_sgid} e \secref{sec:filedir_sticky}). - -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; inoltre ad un processo sono associati diversi identificatori, -torneremo su questo in maggiori dettagli in seguito in \secref{sec:proc_perms}. - -\subsection{I tipi di files} -\label{sec:fileintr_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 definiti nel VFS è il seguente: - -\begin{table}[htb] - \begin{center} - \begin{tabular}[c]{l l p{7cm}} - \multicolumn{2}{c}{\textbf{Nome}} & \textbf{Descrizione} \\ \hline - \textit{regular file} & \textsl{file normale} & + \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 inodes \\ + 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 sequenziale \\ + un file che identifica una periferica ad accesso a caratteri \\ \textit{block device} & \textsl{dispositivo a blocchi} & - un file che identifica una periferica ad accesso diretto \\ - \textit{fifo} & \textsl{tubo} & + 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{manicotto} & + 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} - \label{tab:fileintr_file_types} - \end{center} + \label{tab:file_file_types} \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. -% (con i kernel -% della serie 2.4 è disponibile una forma di accesso diretto ai dischi il -% \textit{raw access} che però non ha nulla a che fare con questo). - -Una seconda differenza è nel formato dei file ASCII; in Unix la fine riga è -codificata in maniera diversa da Windows o MacIntosh, in particolare il fine -riga è il carattere \texttt{LF} (o \verb|\n|) al posto del \texttt{CR} -(\verb|\r|) del mac e del \texttt{CR LF} di Windows. Questo può causare alcuni +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 byte. Non esiste cioè differenza per come vengono visti dal +sistema file di diverso contenuto o formato (come nel caso di quella fra file +di testo e binari che c'è in Windows) né c'è una strutturazione a record per +il cosiddetto ``\textsl{accesso diretto}'' come nel caso del +VMS.\footnote{questo vale anche per i dispositivi a blocchi: la strutturazione + dell'I/O in blocchi di 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\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 è +il carattere \texttt{LF} (o \verb|\n|) al posto del \texttt{CR} (\verb|\r|) +del Mac e del \texttt{CR LF} di Windows.\footnote{per questo esistono in Linux + dei programmi come \cmd{unix2dos} e \cmd{dos2unix} che effettuano una + conversione fra questi due formati di testo.} Questo può causare alcuni problemi qualora nei programmi si facciano assunzioni sul terminatore della riga. +Si ricordi infine che un kernel Unix non fornisce nessun supporto per la +tipizzazione dei file di dati e che non c'è nessun supporto del sistema per le +estensioni come parte del filesystem.\footnote{non è così ad esempio nel + filesystem HFS dei Mac, che supporta delle risorse associate ad ogni file, + che specificano fra l'altro il contenuto ed il programma da usare per + leggerlo. In realtà per alcuni filesystem, come l'XFS della SGI, esiste la + possibilità di associare delle risorse ai file, ma è una caratteristica + tutt'ora poco utilizzata, dato che non corrisponde al modello classico dei + file in un sistema Unix.} Ciò nonostante molti programmi adottano delle +convenzioni per i nomi dei file, ad esempio il codice C normalmente si mette +in file con l'estensione \file{.c}; un'altra tecnica molto usata è quella di +utilizzare i primi 4 byte del file per memorizzare un \textit{magic number} +che classifichi il contenuto; entrambe queste tecniche, per quanto usate ed +accettate in maniera diffusa, restano solo delle convenzioni il cui rispetto è +demandato alle applicazioni stesse. -\section{Una panoramica sull'uso dei file} -\label{sec:fileintr_io_overview} - -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 comunicazione impedendo ogni ulteriore -operazione. \subsection{Le due interfacce ai file} -\label{sec:fileintr_io_api} +\label{sec:file_io_api} -In unix le modalità di accesso ai file e le relative interfacce di -programmazione sono due, basate su due diversi meccanismi di connessione. +In Linux le modalità di accesso ai file e le relative interfacce di +programmazione sono due, basate su due diversi meccanismi con cui è possibile +accedere al loro contenuto. + +La prima è l'interfaccia standard di Unix, quella che il manuale delle +\acr{glibc} chiama interfaccia dei descrittori di file (o \textit{file + descriptor}). È un'interfaccia specifica dei sistemi unix-like e fornisce +un accesso non bufferizzato; la tratteremo in dettaglio in +\capref{cha:file_unix_interface}. -La prima è l'interfaccia standard di unix, quella che il manuale delle glibc -chiama interfaccia dei descrittore di file (o \textit{file descriptor}). È -un'interfaccia specifica di unix e provvede un accesso non bufferizzato. L'interfaccia è primitiva ed essenziale, l'accesso viene detto non 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è -semplici variabili di tipo \texttt{int}). L'interfaccia è definita -nell'header \texttt{unistd.h}. - -La seconda interfaccia è quella che il manuale della glibc chiama degli -\textit{stream}, essa provvede funzioni più evolute e un accesso bufferizzato -(controllato dalla implementazione fatta dalle librerie del C). Questa è -l'interfaccia standard usata dal linguaggio C e perciò si trova anche su tutti -i sistemi non Unix. Gli 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 \texttt{FILE *}. -L'interfaccia è definita nell'header \texttt{stdio.h}. +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{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}\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 (pipes, 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}). - -Gli stream forniscono un'interfaccia di alto livello costruita sopra quella -dei file descriptor, che tratta tutti i file nello stesso modo, con -l'eccezione di poter scegliere tra 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 gli 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. +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 +diversi stili di bufferizzazione. Il maggior vantaggio degli \textit{stream} +è 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}\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 file descriptor da uno stream -ed eseguirvi operazioni di basso livello, o associare in un secondo tempo uno -stream ad un file descriptor. +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}\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 stream per la loro maggiore portabilità -essendo questi ultimi definiti nello standard ANSI 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:fileint_unix_spec} - -Essendo un sistema multitasking e multiutente esistono alcune caratteristiche -specifiche di Unix che devono essere tenute in conto nell'accesso 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 attributi 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 mantenuto 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 appunto creare ed inizializzare una tale struttura, per cui se si -apre due volte lo stesso file all'interno dello stesso processo, si otterranno -due file descriptor o due stream che avranno ancora una posizione corrente nel -file assolutamente indipendente. - -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 ai -dati, e lo spazio su disco non verrà rilasciato fintanto che il file non sarà -chiuso e l'ultimo riferimento cancellato. È pertanto possibile (come vedremo -in dettaglio in \secref{sec:filedir_link}) aprire un file provvisorio per -cancellarlo immediatamente dopo; in questo modo all'uscita del programma il -file scomparirà definitivamente dal disco, ma il file ed il suo contenuto -saranno disponibili per tutto il tempo in cui il processo è attivo. - -Ritorneremo su questo più avanti, quando tratteremo l'input/output sui file, -esaminando in dettaglio come tutto ciò viene realizzato. - -Si ricordi infine che in unix non esistono i tipi di file e che non c'è nessun -supporto per le estensioni come parte del filesystem. Ciò non ostante molti -programmi adottano delle convenzioni per i nomi dei file, ad esempio il codice -C normalmente si mette in file con l'estensione .c, ma questa è, appunto, solo -una convenzione. +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} +% \label{sec:fileint_unix_spec} + +% Essendo un sistema multitasking e multiutente esistono alcune caratteristiche +% specifiche di un sistema unix-like che devono essere tenute in conto +% nell'accesso 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 attributi 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 +% byte 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 mantenuto 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 appunto creare ed inizializzare una tale struttura, per cui se si +% apre due volte lo stesso file all'interno dello stesso processo, si otterranno +% due file descriptor o due stream che avranno ancora una posizione corrente nel +% file assolutamente indipendente. + +% 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 ai +% dati, e lo spazio su disco non verrà rilasciato fintanto che il file non sarà +% chiuso e l'ultimo riferimento cancellato. È pertanto possibile (come vedremo +% in dettaglio in \secref{sec:file_link}) aprire un file provvisorio per +% cancellarlo immediatamente dopo; in questo modo all'uscita del programma il +% file scomparirà definitivamente dal disco, ma il file ed il suo contenuto +% saranno disponibili per tutto il tempo in cui il processo è attivo. + +% Ritorneremo su questo più avanti in \secref{sec:file_fd}, quando tratteremo +% l'input/output sui file, esaminando in dettaglio come tutto ciò viene +% realizzato. + + +\section{L'architettura della gestione dei file} +\label{sec:file_arch_func} + +%% Per capire fino in fondo le proprietà di file e directory in un sistema +%% unix-like ed il comportamento delle relative funzioni di manipolazione, +%% occorre una breve introduzione al funzionamento della gestione dei file da +%% parte del kernel e sugli oggetti su cui è basato un filesystem. In particolare +%% occorre tenere presente dov'è che si situa la divisione fondamentale fra +%% kernel space e user space che tracciavamo al \capref{cha:intro_unix}. + +In questa sezione esamineremo come viene implementato l'accesso ai file in +Linux, come il kernel può gestire diversi tipi di filesystem, descrivendo +prima le caratteristiche generali di un filesystem di un sistema unix-like, +per poi trattare in maniera un po' più dettagliata il filesystem più usato con +Linux, l'\acr{ext2}. + +% in particolare si riprenderà, approfondendolo sul piano dell'uso nelle +% funzioni di libreria, il concetto di \textit{inode} di cui abbiamo brevemente +% accennato le caratteristiche (dal lato dell'implementazione nel kernel) in +% \secref{sec:file_vfs}. + + +\subsection{Il \textit{Virtual File System} di Linux} +\label{sec:file_vfs} + +% Questa sezione riporta informazioni sui dettagli di come il kernel gestisce i +% file. L'argomento è abbastanza ``esoterico'' e questa sezione può essere +% saltata ad una prima lettura; è bene però tenere presente che vengono +% introdotti qui alcuni termini che potranno comparire in seguito, come +% \textit{inode}, \textit{dentry}, \textit{dcache}. + +In Linux il concetto di \textit{everything is a file} è stato implementato +attraverso il \textit{Virtual File System} (da qui in avanti VFS) che è uno +strato intermedio che il kernel usa per accedere ai più svariati filesystem +mantenendo la stessa interfaccia per i programmi in user space. Esso fornisce +un livello di indirezione che permette di collegare le operazioni di +manipolazione sui file alle operazioni di I/O, e gestisce l'organizzazione di +queste ultime nei vari modi in cui i diversi filesystem le effettuano, +permettendo la coesistenza di filesystem differenti all'interno dello stesso +albero delle directory. + +Quando un processo esegue una system call che opera su un file, il kernel +chiama sempre una funzione implementata nel VFS; la funzione eseguirà le +manipolazioni sulle strutture generiche e utilizzerà poi la chiamata alle +opportune routine del filesystem specifico a cui si fa riferimento. Saranno +queste a chiamare le funzioni di più basso livello che eseguono le operazioni +di I/O sul dispositivo fisico, secondo lo schema riportato in +\figref{fig:file_VFS_scheme}. + +\begin{figure}[htb] + \centering + \includegraphics[width=7cm]{img/vfs} + \caption{Schema delle operazioni del VFS.} + \label{fig:file_VFS_scheme} +\end{figure} + +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}\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 +filesystem tutto quello che occorre è chiamare la funzione +\code{register\_filesystem} passandole un'apposita struttura +\code{file\_system\_type} che contiene i dettagli per il riferimento +all'implementazione del medesimo, che sarà aggiunta alla citata tabella. + +In questo modo quando viene effettuata la richiesta di montare un nuovo disco +(o qualunque altro \textit{block device} che può contenere un filesystem), il +VFS può ricavare dalla citata tabella il puntatore alle funzioni da chiamare +nelle operazioni di montaggio. Quest'ultima è responsabile di leggere da disco +il superblock (vedi \secref{sec:file_ext2}), inizializzare tutte le variabili +interne e restituire uno speciale descrittore dei filesystem montati al VFS; +attraverso quest'ultimo diventa possibile accedere alle routine specifiche per +l'uso di quel filesystem. + +Il primo oggetto usato dal VFS è il descrittore di filesystem, un puntatore ad +una apposita struttura che contiene vari dati come le informazioni comuni ad +ogni filesystem, i dati privati relativi a quel filesystem specifico, e i +puntatori alle funzioni del kernel relative al filesystem. Il VFS può così +usare le funzioni contenute nel \textit{filesystem descriptor} per accedere +alle routine specifiche di quel filesystem. + +Gli altri due descrittori usati dal VFS sono relativi agli altri due oggetti +su cui è strutturata l'interfaccia. Ciascuno di essi contiene le informazioni +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\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} +\label{sec:file_vfs_work} + +La funzione più importante implementata dal VFS è la system call \func{open} +che permette di aprire un file. Dato un pathname viene eseguita una ricerca +dentro la \textit{directory entry cache} (in breve \textit{dcache}), una +tabella che contiene tutte le \textit{directory entry} (in breve +\textit{dentry}) che permette di associare in maniera rapida ed efficiente il +pathname a una specifica \textit{dentry}. + +Una singola \textit{dentry} contiene in genere il puntatore ad un +\textit{inode}\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}\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\index{inode} corrispondente in memoria. + +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\index{inode} e passarli in user space. + +L'apertura di un file richiede comunque un'altra operazione, l'allocazione di +una struttura di tipo \struct{file} in cui viene inserito un puntatore alla +\textit{dentry} e una struttura \struct{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 +\tabref{tab:file_file_operations}. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Funzione} & \textbf{Operazione} \\ + \hline + \hline + \textsl{\code{open}} & apre il file (vedi \secref{sec:file_open}). \\ + \textsl{\code{read}} & legge dal file (vedi \secref{sec:file_read}).\\ + \textsl{\code{write}} & scrive sul file (vedi \secref{sec:file_write}).\\ + \textsl{\code{llseek}} & sposta la posizione corrente sul file (vedi + \secref{sec:file_lseek}). \\ + \textsl{\code{ioctl}} & accede alle operazioni di controllo + (vedi \secref{sec:file_ioctl}).\\ + \textsl{\code{readdir}}& legge il contenuto di una directory \\ + \textsl{\code{poll}} & usata nell'I/O multiplexing (vedi + \secref{sec:file_multiplexing}). \\ + \textsl{\code{mmap}} & mappa il file in memoria (vedi + \secref{sec:file_memory_map}). \\ + \textsl{\code{release}}& chiamata quando l'ultimo riferimento a un file + aperto è chiuso. \\ + \textsl{\code{fsync}} & sincronizza il contenuto del file (vedi + \secref{sec:file_sync}). \\ + \textsl{\code{fasync}} & abilita l'I/O asincrono (vedi + \secref{sec:file_asyncronous_io}) sul file. \\ + \hline + \end{tabular} + \caption{Operazioni sui file definite nel VFS.} + \label{tab:file_file_operations} +\end{table} + +In questo modo per ciascun file diventano possibili una serie di operazioni +(non è detto che tutte siano disponibili), che costituiscono l'interfaccia +astratta del VFS. Qualora se ne voglia eseguire una, il kernel andrà ad +utilizzare l'opportuna routine dichiarata in \struct{f\_ops} appropriata al +tipo di file in questione. + +Pertanto è possibile scrivere allo stesso modo sulla porta seriale come su un +normale file di dati; ovviamente certe operazioni (nel caso della seriale ad +esempio la \code{seek}) non saranno disponibili, però con questo sistema +l'utilizzo di diversi filesystem (come quelli usati da Windows o MacOs) è +immediato e (relativamente) trasparente per l'utente ed il programmatore. + + +\subsection{Il funzionamento di un filesystem Unix} +\label{sec:file_filesystem} + +Come già accennato in \secref{sec:file_organization} Linux (ed ogni sistema +unix-like) organizza i dati che tiene su disco attraverso l'uso di un +filesystem. Una delle caratteristiche di Linux rispetto agli altri Unix è +quella di poter supportare, grazie al VFS, una enorme quantità di filesystem +diversi, ognuno dei quali ha una sua particolare struttura e funzionalità +proprie. Per questo, per il momento non entreremo nei dettagli di un +filesystem specifico, ma daremo una descrizione a grandi linee che si adatta +alle caratteristiche comuni di qualunque filesystem di sistema unix-like. + +Lo spazio fisico di un disco viene usualmente diviso in partizioni; ogni +partizione può contenere un filesystem. La strutturazione tipica +dell'informazione su un disco è riportata in \figref{fig:file_disk_filesys}; +in essa si fa riferimento alla struttura del filesystem \acr{ext2}, che +prevede una separazione dei dati in \textit{blocks group} che replicano il +superblock (ma sulle caratteristiche di \acr{ext2} torneremo in +\secref{sec:file_ext2}). È comunque caratteristica comune di tutti i +filesystem per Unix, indipendentemente da come poi viene strutturata nei +dettagli questa informazione, prevedere una divisione fra la lista degli +inode\index{inode} e lo spazio a disposizione per i dati e le directory. + +\begin{figure}[htb] + \centering + \includegraphics[width=14cm]{img/disk_struct} + \caption{Organizzazione dello spazio su un disco in partizioni e + filesystem.} + \label{fig:file_disk_filesys} +\end{figure} + +Se si va ad esaminare con maggiore dettaglio la strutturazione +dell'informazione all'interno del singolo filesystem (tralasciando i dettagli +relativi al funzionamento del filesystem stesso come la strutturazione in +gruppi dei blocchi, il superblock e tutti i dati di gestione) possiamo +esemplificare la situazione con uno schema come quello esposto in +\figref{fig:file_filesys_detail}. + +\begin{figure}[htb] + \centering + \includegraphics[width=14cm]{img/filesys_struct} + \caption{Strutturazione dei dati all'interno di un filesystem.} + \label{fig:file_filesys_detail} +\end{figure} + +Da \figref{fig:file_filesys_detail} si evidenziano alcune delle +caratteristiche di base di un filesystem, sulle quali è bene porre attenzione +visto che sono fondamentali per capire il funzionamento delle funzioni che +manipolano i file e le directory che tratteremo nel prossimo capitolo; in +particolare è opportuno ricordare sempre che: + +\begin{enumerate} + +\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 + contatore che contiene il numero di riferimenti (\textit{link count}) che + sono stati fatti ad esso; solo quando questo contatore si annulla i dati del + file vengono 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}\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{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}\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} + +Infine è bene avere presente che, essendo file pure loro, esiste un numero di +riferimenti anche per le directory; per cui, se a partire dalla situazione +mostrata in \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\index{inode}. + +\begin{figure}[htb] + \centering + \includegraphics[width=14cm]{img/dir_links} + \caption{Organizzazione dei link per le directory.} + \label{fig:file_dirs_link} +\end{figure} + +La nuova directory avrà allora un numero di riferimenti pari a due, in quanto +è referenziata dalla directory da cui si era partiti (in cui è inserita la +nuova voce che fa riferimento a \file{img}) e dalla voce \file{.} +che è sempre inserita in ogni directory; questo vale sempre per ogni directory +che non contenga a sua volta altre directory. Al contempo, la directory da +cui si era partiti avrà un numero di riferimenti 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 \acr{ext2}. Esso supporta tutte le +caratteristiche di un filesystem standard Unix, è in grado di gestire nomi di +file lunghi (256 caratteri, estensibili a 1012) con una dimensione massima di +4~Tb. + +Oltre alle caratteristiche standard, \acr{ext2} fornisce alcune estensioni che +non sono presenti sugli altri filesystem Unix. Le principali sono le seguenti: +\begin{itemize} +\item i \textit{file attributes} consentono di modificare il comportamento del + kernel quando agisce su gruppi di file. Possono essere impostati 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 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 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 \acr{sgid} impostato (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). +\item il filesystem implementa link simbolici veloci, in cui il nome del file + 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 + aggiungere dati (caratteristica utilizzabile per la protezione dei file di + log). +\end{itemize} + +La struttura di \acr{ext2} è stata ispirata a quella del filesystem di BSD: +un filesystem è composto da un insieme di blocchi, la struttura generale è +quella riportata in \figref{fig:file_filesys_detail}, in cui la partizione +è divisa in gruppi di blocchi. + +Ciascun gruppo di blocchi contiene una copia delle informazioni essenziali del +filesystem (superblock e descrittore del filesystem sono quindi ridondati) per +una maggiore affidabilità e possibilità di recupero in caso di corruzione del +superblock principale. + +\begin{figure}[htb] + \centering + \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 raggruppamenti di blocchi ha inoltre degli effetti positivi nelle +prestazioni dato che viene ridotta la distanza fra i dati e la tabella degli +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\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. + + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "gapil" +%%% End: