X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileintro.tex;h=995129189d236fba35bf549e48a1364276c2cba9;hp=dfd3081dc67781aa9984a8052d20dc99818db1ea;hb=2414addb7861eb72315a3de58b6f2cb5c83ab6ed;hpb=efd164169524125422cf9bb80ff70a0b037886a0 diff --git a/fileintro.tex b/fileintro.tex index dfd3081..9951291 100644 --- a/fileintro.tex +++ b/fileintro.tex @@ -1,377 +1,33 @@ -\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, -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 perferiche, 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 Cap.~\ref{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}. - -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. 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 -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, -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 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 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 albero. - -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. Un elenco -delle operazioni disponibili è riportato in \ntab. - -\begin{table}[htb] - \centering - \begin{tabular}[c]{c p{7cm}} - \textbf{funzione} & \textbf{operazione} \\ - \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. \\ - \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, 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 Sez.~\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 (una descrizione più dettagliata dei vari -permessi associati ai file è riportata in \ref{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 \ref{sec:filedir_suid_sgid} e \ref{sec:filedir_stiky}). - -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 \ref{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 è 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} & - 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{manicotto} & - un file speciale che identifica una linea di comunicazione software - (bidirezionale) \\ - \hline - \end{tabular} - \caption{Tipologia dei file definiti nel VFS} - \label{tab:fileintr_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. -% (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 -problemi qualora si facciano assunzioni sul terminatore della riga. - - -\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} - -In unix le modalità di accesso ai file e le relative interfacce di -programmazione sono due, basate su due diversi meccanismi di connessione. - -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}. - -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 -\ref{sec:file_bohhhhh}). - -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. - -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. - -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 otterrano -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 (e pratica -comune) 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. - +%% fileintro.tex +%% +%% Copyright (C) 2000-2011 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} + +% capitolo eliminato, inglobando altrove + +% LocalWords: everything is device kernel filesystem sez pathname root glibc +% LocalWords: path filename bootloader proc name components fifo socket dev LF +% LocalWords: resolution chroot parent Virtual System like tab cap l'I regular +% LocalWords: inode symbolic char block VFS VMS Windows dell'I raw access Mac +% LocalWords: CR dos HFS l'XFS SGI magic number descriptor system call int ext +% LocalWords: nell'header unistd stream dall'ANSI stdio locking POSIX fig type +% LocalWords: register superblock dell'inode stat entry cache dcache dentry ln +% LocalWords: l'inode lookup ops read write llseek ioctl readdir poll nell'I +% LocalWords: multiplexing mmap fsync fasync seek group dall' dell' img +% LocalWords: count unlink nell' rename gapil second Tb attributes BSD SVr gid +% LocalWords: sgid append only log fs linux extented linked list third MacOS + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "gapil" +%%% End: