IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in...

26
Facoltà di Ingegneria Corso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL VIRTUAL FILE SYSTEM DI LINUX” Progetto finale per l’esame di Sistemi Operativi prof. A. Andronico – prof.ssa M. Bianchini

Transcript of IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in...

Page 1: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

Facoltà di IngegneriaCorso di Laurea in Ingegneria Informatica N.O.

Indirizzo Reti e Sistemi MultimedialiA.A. 2001-2002

“IL VIRTUAL FILE SYSTEM DI LINUX”

Progetto finale per l’esame di Sistemi Operativi prof. A. Andronico – prof.ssa M. Bianchini

Relatori:Lavanga Maria RosariaMonaco Rosario

Page 2: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

1 Cenni introduttivi sul file system

Uno dei concetti fondamentali dell'architettura di un sistema Linux è il cosiddetto 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 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.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 file system, essa poi viene resa disponibile ai processi attraverso quello che viene chiamato il montaggio del file system.

1.1 L'organizzazione di file e directory

In Linux, 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 root directory) viene montata all'avvio. Un file viene identificato dall'utente usando quello che viene chiamato pathname, cioè il percorso che si deve fare per accedere al file, che è composto da una serie di nomi separati da una /.Dopo la fase di inizializzazione il kernel riceve dal boot loader l’indicazione di quale dispositivo contiene il file system da usare come punto di partenza e questo viene montato come radice dell’albero (cioè nella directory /); tutti gli ulteriori file system che possono essere su altri dispositivi devono poi essere inseriti nell’albero montandoli su opportune directory del file system montato come radice.Una directory è 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 file system, 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 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 e gli stessi file di dispositivo (questi ultimi, per convenzione, sono inseriti nella directory /dev).Il procedimento con cui si individua il file a cui esso fa riferimento è chiamato risoluzione del nome (file name resolution o pathname resolution). La risoluzione viene fatta esaminando il pathname da destra a sinistra e localizzando ogni nome nella directory indicata dal nome precedente usando / come separatore: ovviamente perché il procedimento funzioni occorre che i nomi indicati come directory esistano e siano effettivamente directory, inoltre i permessi devono consentire l’accesso.Se il pathname comincia per / la ricerca parte dalla directory radice del processo; questa, a meno di un chroot è la stessa per tutti i processi ed equivale alla directory radice dell’albero dei file: in questo caso si parla di un pathname assoluto. Altrimenti la ricerca parte dalla directory corrente ed il pathname è detto pathname relativo.I nomi . e .. hanno un significato speciale e vengono inseriti in ogni directory, il primo fa riferimento alla directory corrente e il secondo alla directory genitrice (o parent directory) cioè la directory che contiene il riferimento alla directory corrente; nel caso questa sia la directory radice allora il riferimento è a se stessa.

2

Page 3: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

1.2 I tipi di file

In Linux i file sono implementati come oggetti del Virtual File System e sono presenti in tutti i file system Linux-like utilizzabili con Linux. L’elenco dei vari tipi di file definiti dal Virtual File System è riportato in tab. 1.1.Si tenga ben presente che questa classificazione non ha nulla a che fare con la classificazione sui tipi di file (che in questo caso sono sempre file di dati) in base al loro contenuto, o tipo di accesso.Infatti una delle differenze principali con altri sistemi operativi (come il VMS o Windows) è che per Linux 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 "accesso diretto" come nel caso del VMS.

Tipo di file Descrizioneregular file file normale un file che contiene dei dati (l'accezione normale di file)directory cartella o direttorio un file che contiene una lista di nomi associati a degli inode.symbolic link

collegamento simbolico

un file che contiene un riferimento ad un altro file/directory

char device dispositivo a caratteri

un file che identifica una periferica ad accesso sequenziale

block device dispositivo a blocchi un file che identifica una periferica ad accesso direttofifo tubo un file speciale che identifica una linea di comunicazione

software (unidirezionale)socket manicotto un file speciale che identifica una linea di comunicazione

software (bidirezionale)

Tabella 1.1: Tipologia dei file definiti nel VFS

1.3 Caratteristiche specifiche dei file in Linux

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. Lo spazio fisico viene usualmente suddiviso in partizioni ognuna delle quali può contenere un file system. La struttura tipica viene riportata nella figura seguente:

3

Page 4: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

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 un’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.La posizione corrente nel file, cioè il punto nel file in cui verrebbe letto o scritto all’operazione successiva è 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 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. Si ricordi infine che in ambiente Linux non esistono i tipi di file e che non c' è nessun supporto per le estensioni come parte del file system. 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 .c, ma questa è, appunto, solo una convenzione.

2 Il Virtual file system di linux

2.1 Architettura generale

In Linux il concetto di everything is a file è stato implementato attraverso il Virtual File System (da qui in avanti VFS) che è l’interfaccia che il kernel rende disponibile ai programmi in user space attraverso la quale vengono manipolati i file; esso provvede un livello di indirezione che permette di collegare le operazioni di manipolazione sui file alle operazioni di I/O e gestisce l’organizzazione di questi ultimi nei vari modi in cui diversi file system la effettuano, permettendo la coesistenza di file system 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 attraverso puntatori alle funzioni di un file system eseguirà le chiamate alle opportune routine del file system specifico a cui si fa riferimento. La struttura che raccoglie questi puntatori è definita nel file /usr/src/linux/include/linux/fs.h. Il percorso userspace-funzioni di basso livello può essere semplificato nel seguente schema:

4

Page 5: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

Figura 2.1: Schema delle operazioni del VFS

Le file_operations che implementano i dettagli di basso livello delle chiamate di sistema sono quelle illustrate qui sotto:

struct file_operations {int (*lseek) (struct inode*, struct file*, off_t, int);int (*read) (struct inode*, struct file*, char*, int);int (*write) (struct inode*, struct file*, char*, int);int (*readdir) (struct inode*, struct file*, struct dirent*, int);int (*select) (struct inode*, struct file*, int, select_table*);int (*ioctl) (struct inode*, struct file*, unsigned int, unsigned long);int (*mmap) (struct inode*, struct file*, struct vm_area_struct*);int (*open) (struct inode*, struct file*);void (*release) (struct inode*, struct file*);int (*fsync) (struct inode*, struct file*);int (*fasync) (struct inode*, struct file*, int);int (*check_media_change) (dev_t dev);int (*revalidate) (dev_t dev);

 ;

Prima di esaminare tali funzioni occorre specificare i messaggi di errore che esse possono restituire. Tali errori sono contenuti nel file /usr/src/linux/include/linux/errno.h e i più comuni sono:……#define EBADF 9 /* numero di file errato */……#define EBUSY 16 /* device or resource busy */……#define ENODEV 19 /* no such device */#define ENOTDIR 20 /* non è una directory */……#define EINVAL 22 /* invalid argument */

5

Page 6: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

2.2 Oggetti e funzionamento del Virtual File System

Il VFS di Linux è concepito secondo una progettazione ad oggetti. E’ costituito da due componenti: un insieme di definizioni che stabiliscono cosa può essere un oggetto di tipo file, e uno strato di software che manipola questi oggetti. I tre tipi principali d’oggetti sono file system, inode e file che corrispondono a tre apposite strutture definite nel kernel: il primo denota un intero file system mentre i secondi rappresentano un file singolo. Per ciascuno di questi oggetti il VFS definisce un insieme di operazioni. Ogni oggetto contiene un puntatore ad una tabella di funzioni, e questa elenca gli indirizzi delle funzioni che realizzano le operazioni richieste per l’oggetto in questione. Il VFS può eseguire un operazione su uno di questi oggetti chiamando una funzione appropriata scelta dalla tabella delle funzioni dell’oggetto, senza dover sapere in anticipo con che tipo di oggetto ha a che fare.Analizziamo ora nello specifico i singoli oggetti. Il file system consiste di una collezione di file e una struttura di directory che organizza tutti i file nel sistema e fornisce informazioni su di essi. Il VFS usa una tabella mantenuta dal kernel che contiene il nome di ciascun file system supportato: quando si vuole inserire il supporto di un nuovo file system tutto quello che occorre è chiamare la funzione register_file system passandole un’apposita struttura (file_system_type) che viene inserita nell’apposita tabella. Il file_system_type contiene i dettagli per il riferimento all’implementazione dello specifico file_system cioè il nome ed una funzione read_super. Questa funzione deve caricare l'inode della directory root del filesystem all'interno di sb-">"s_mounted, dove sb è il super-blocco che viene riempito. Il superblock (struct_superblock) è la struttura dati contenente le informazioni riguardo il file system di cui fa parte (contiene parametri statici del file system: dimensione totale e parametri vari che influiscono sulle politiche di allocazione). Va sottolineato che dopo aver caricato la struttura super_block in memoria, la struttura file_system_type non viene più utilizzata, solo il super-blocco conterrà un puntatore a tale struttura per poter essere in grado di fornire informazioni all’utente. La struttura è definita come segue:

struct file_system_type { struct super_block *(*read_super) (struct super_block *, void *, int); const char *name; int requires_dev;

struct file_system_type * next; /* there's a linked list of types */ }; Nella fase successiva vengono inizializzate tutte le variabili interne e viene attivato uno speciale descrittore dei file system attraverso il quale diventa possibile accedere alle routine specifiche per l’uso di quel file system in causa. Il descrittore di file system è un puntatore ad un’apposita struttura che contiene vari dati come le informazioni comuni ad ogni file system, i dati privati relativi a quel file system specifico, e i puntatori alle funzioni del kernel relative al file system. Il VFS può così usare le funzioni contenute nel file system descriptor per accedere alle routine specifiche di quel file system. Dopo aver montato il file system l’analisi passa ad un secondo oggetto: gli inode Un inode è la struttura base che sta sul disco e che identifica un singolo oggetto del VFS sia esso un file ordinario, una directory, un link simbolico, una FIFO, un file di dispositivo, o una qualsiasi altra cosa che possa essere rappresentata dal VFS infatti gli inode sono dei numeri univoci che identificano i files o quant’altro all’interno del file system.L'inode ha una struttura molto complessa. Esso contiene tutte le informazioni riguardanti il file tranne il suo nome e i dati in esso contenuti: il tipo di file, i permessi e il modo di accesso (i_mode), il riferimento all’utente (i_uid), le dimensioni (i_size), i puntatori ai blocchi fisici che contengono i dati, il numero del dispositivo (i_dev) e soprattutto il numero dell’inode attraverso il quale il kernel identifica il file. Dentro una directory si troverà solo il nome del file e il numero dell'inode ad esso associato, cioè quella che da qui in poi chiameremo una voce (traduzione approssimata dell'inglese directory entry). Separando le informazioni dai dati contenuti nel file si ha la possibilità di implementare gli “hard link” e di utilizzare la notazione “.” e “..” senza la necessità di trattare tali

6

Page 7: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

nomi come speciali. Si possono avere più voci che puntano allo stesso inode. Ogni inode ha un contatore che contiene il numero di riferimenti (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 unlink, ed in realtà non cancella affatto i dati del file, ma si limita a eliminare la relativa voce da una directory e decrementare il numero di riferimenti nell’inode. Il numero di inode nella voce si riferisce ad un inode nello stesso file system e non ci può essere una directory che contiene riferimenti ad inode relativi ad altri file system. Questo limita l’uso del comando ln (che crea una nuova voce per un file esistente, con la funzione link) al file system corrente.Quando si cambia nome ad un file senza cambiare file system il contenuto del file non deve essere spostato, viene semplicemente creata una nuova voce per l’inode in questione e rimossa la vecchia (questa è la modalità in cui opera normalmente il comando mv attraverso la funzione rename). A ciascun inode è associata pure una struttura che sta in memoria, e che oltre alle informazioni sullo specifico file contiene pure il riferimento alle funzioni (i metodi) da usare per poterlo manipolare. Dal momento che l’inode risiedono sul disco bisogna ricordare che è necessario copiarli in memoria quando serve attraverso la struttura super_operations dichiarata da ciascun tipo di file system e usata dal kernel per leggere e scrivere gli inode,salvare le informazioni del super-blocco sul disco e raccogliere statistiche . Nello specifico la struttura degli inode è illustrata nel seguente modo:

struct inode { dev_t i_devunsigned long i_ino;umode_t i_mode;nlink_t i_nlink;uid_t i_uid;gid_t i_gid;dev_t i_rdev;off_t i_size;time_t i_atime;time_t i_mtime;time_t i_ctime;unsigned long i_blksize;unsigned long i_blocks;unsigned long i_version;struct semaphore i_sem;struct inode_operations* i_op;struct super_block* i_sb;struct wait_queue* i_wait;struct file_lock* i_flock;……

 ;

Le super_operation dichiarate sono le seguenti:

struct super_operations {void (*read_inode) (struct inode *); /* fill the structure */

int (*notify_change) (struct inode *, struct iattr *); void (*write_inode) (struct inode *);

void (*put_inode) (struct inode *);void (*put_super) (struct super_block *);void (*write_super) (struct super_block *);

7

Page 8: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

void (*statfs) (struct super_block *, struct statfs *, int);int (*remount_fs) (struct super_block *, int *, char *);

};

Dopo la creazione di una copia in memoria di un inode, il kernel agirà su di lui tramite le sue proprie operazioni. struct inode_operations è il secondo insieme di operazioni che viene dichiarato da ciascun tipo di file system, e sono elencate qui sotto: come si vede tali operazioni si occupano principalmente della gestione dell'albero delle directory. Le operazioni di gestione delle directory fanno parte delle operazioni relative agli inode perché l'implementazione di una apposita dir_operations sarebbe risultato in esecuzioni condizionali aggiuntive per ciascun accesso al file system. Invece si è scelto di far fare il controllo degli errori all'interno di ciascuna operazione, e le operazioni che hanno solo senso per le directory si rifiuteranno di operare su altri tipi di file. Il primo campo delle operazioni sugli inode definisce le operazioni per agire sui file regolari: se invece l'inode si riferisce ad una FIFO, un socket oppure un dispositivo, allora verranno usate le operazioni specifiche per questi file.

struct inode_operations {struct file_operations * default_file_ops;int (*create) (struct inode *,const char *,int,int,struct inode **);int (*lookup) (struct inode *,const char *,int,struct inode **);int (*link) (struct inode *,struct inode *,const char *,int);int (*unlink) (struct inode *,const char *,int);int (*symlink) (struct inode *,const char *,int,const char *);int (*mkdir) (struct inode *,const char *,int,int);int (*rmdir) (struct inode *,const char *,int);int (*mknod) (struct inode *,const char *,int,int,int);int (*rename) (struct inode *,const char *,int, struct inode *, const char *,int, int); /* this from 2.0.1 onwards */

int (*readlink) (struct inode *,char *,int);int (*follow_link) (struct inode *,struct inode *,int,int,struct inode **);int (*readpage) (struct inode *, struct page *);

int (*writepage) (struct inode *, struct page *);int (*bmap) (struct inode *,int);void (*truncate) (struct inode *);int (*permission) (struct inode *, int);int (*smap) (struct inode *,int);

};

L’altra struttura d’interesse è la struttura dei files e contiene, tra le altre informazioni, il modo con cui il file è stato aperto (f_mod), la posizione all’interno del file alla quale si sta operando (f_pos), il puntatore all’inode del file (f_inode), il puntatore alle operazioni per il dispositivo al quale il file appartiene (f_op):

struct file mode_t f_mode;loff_t f_pos ;unsigned short f_flags ;unsigned short f_count;off_t f_reada;struct file * f_next, * f_prev;int f_owner; /*pid or –pdrp where SIGIO should be sent */

8

Page 9: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

struct inode * f_inode;struct file_operations * f_op;unsigned long f_version;void * private_data; /* needed for try driver, and maybe others *

 ;

In modo molto schematico un elenco delle operazioni sui file previste dal kernel è riportato nella tabella seguente:

Analizziamo ora singolarmente le operazioni:

open (struct inode*, struct file *)

La funzione più fondamentale implementata dal VFS è la system call open che permette di aprire un file. Dato un pathname viene eseguita una ricerca dentro la directory entry cache (in breve dcache), una tabella di hash che contiene tutte le directory entry (in breve dentry) che permette di associare in maniera rapida ed efficiente il pathname a una specifica dentro che contiene in genere il puntatore ad un inode. Le dentry "vivono" in memoria e non vengono mai salvate su disco, vengono usate per motivi di velocità.La dcache costituisce perciò una sorta di vista completa di tutto l'albero dei file, ovviamente per non riempire tutta la memoria questa vista è parziale (la dcache cioè contiene solo le dentry per i file per i quali è stato richiesto l'accesso), quando si vuole risolvere un nuovo pathname il VFS deve creare una nuova dentry e caricare l'inode corrispondente in memoria. Questo procedimento viene eseguito dal metodo lookup () dell'inode della directory che contiene il file; questo viene installato nelle relative strutture in memoria quando si effettua il montaggio lo specifico file system 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 open per aprire il file o la 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 file in cui viene inserito un puntatore alla dentry e una struttura 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.In questo modo per ciascun file diventano utilizzabili una serie di operazioni (non è detto che tutte siano disponibili), che costituiscono l’interfaccia astratta del VFS, e qualora se ne voglia eseguire

Funzione Operazioneopen apre il fileread legge dal filewrite scrive sul filellseek sposta la posizione corrente sul fileioctl accede alle operazioni di controllo (tramite la ioctl)readdir per leggere il contenuto di una directorymmap chiamata dalla system call mmap. mappa il file in memoriarelease chiamata quando l’ultima referenza a un file aperto è chiusafsync chiamata dalla system call fsyncfasync chiamate da fcntl quando `e abilitato il modo asincrono per l’I/O su

fileselect gestisce più accessi contemporanei ad un dispositivoinit registra la struttura file_operation per il gestore di dispositivo che si

vuole implementare

9

Page 10: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

una il kernel andrà ad utilizzare la opportuna routine dichiarata in 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 seek) non saranno disponibili, però con questo sistema l’utilizzo di diversi file system (come quelli usati da Windows o MacOs) è immediato e (relativamente) trasparente per l’utente ed il programmatore.La system call open serve anche per fare in modo che un solo processo alla volta possa accedere al dispositivo. Per bloccare l’accesso al dispositivo agli altri processi, bisogna che setti, ad esempio, un bit per indicare lo stato di BUSY del dispositivo. Allora la open () funzionerà come segue:

controlla se il dispositivo è occupato; se lo è, ritorna l’errore –EBUSY; se non lo è provvede a bloccarlo (bisogna prestare attenzione se si vuole permettere

l’accesso al dispositivo da parte di più processi problema dei lettori scrittori…); se il dispositivo non esiste si ritorna l’errore –ENODEV; infine se tutto va bene si esce ritornando uno 0.

release (struct inode *, struct file *)

E’ la funzione complementare della open () e viene invocata quando il processo che sta usando il dispositivo rilascia il descrittore dell’ultimo file che aveva aperto. La release (), tra l’altro, deve farsi carico di resettare il bit di BUSY. Infine, la release () può farsi carico di resettare il dispositivo e di liberare eventuali aree di memoria allocate con kmalloc ().

read (struct inode*, struct file*, char*, int)write(struct inode*, struct file*, char*, int)

Le due system call read () e write () servono rispettivamente per leggere una stringa di caratteri dal dispositivo oppure per scriverne una. Per le system entrambe non c’è nessuna azione default e l’assenza della codifica di una appropriata funzione produce un errore EINVAL. Le system calls sys_read() e sys_write() sono contenute nel file /usr/src/linux/fs/read_write.c.

asmlinkage int sys_read (unsigned int fd, char * buff, unsigned int count)int error;struct file * file;struct inode * inode;

if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode)) return –EBADF;if (!(file->f_mod & 1)) return –EBADF;if (!file ->f_op || !file ->f_op ->read) return –EINVAL;if (!count) return 0;error = verify_area(VERIFY_WRITE, buf, count);if (error) return error;return file->f_op->read (inode, file, buf, count);}Il significato dei parametric delle read () e write () scitte per il particolare dispositivo è:

10

Page 11: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

struct inode* è il dispositivo alla struttura degli inode del dispositivo;struct file* è il puntatore alla struttura dei files del dispositivo;char* è il puntatore al buffer che contiene che contiene i caratteri da scrivere sul

dispositivo o che dovrà contenere quelli letti; questo buffer è contenuto nello spazio di memoria dell’utente per cui, nel caso in cui il gestore utilizzi il meccanismo delle interruzioni, bisogna ricordarsi di copiare il contenuto del buffer in una coda;

int è la dimensione del buffer cioè il numero di caratteri da leggere o da scrivere.

Per accedere al buffer si usano le seguenti macro contenute nel file:/usr/src/linux/include/asm-i386/segment.h:

memcpy_fromfs (to, from, n), memcpy_tofs (to,from,n)

Servono per copiare dati dallo spazio di memoria dell’utente a quello del kernel e viceversa:to indirizzo dell’area destinazione;from indirizzo dell’area di provenienza;n numero di bytes da copiare.

get_fs_byte ( addr), get_fs_word (addr), get_fs_long (addr)

Permettono ad un gestore di dispositivi di accedere all’area di memoria dell’utente diversa da quella del nucleo per leggere rispettivamente un byte,due bytes o quattro:addr indirizzo dal quale leggere i dati.

put_fs_byte ( addr),put_fs_word (addr),put_fs_long (addr)

Permettono ad un gestore di dispositivi di accedere all’area di memoria dell’utente diversa da quella del nucleo per scrivere rispettivamente un byte,due bytes o quattro:x valore da scrivere;addr indirizzo dal quale scrivere i dati.

lseek(struct inode*, struct file*, off_t, int)

Sposta il punto di lettura/scrittura. Il significato dei parametri è struct inode * il puntatore alla struttura degli inode del dispositivo;struct file * il puntatore alla struttura dei files del dispositivo;off_t lo spostamento (offset) che si vuole eseguire a partire dall’origine specificata

dal parametro successivo;int l’origine che può assumere i valori:

0 = inizio del file;1 = posizione corrente;2 = fine.

Se lseek () termina con successo, restituisce la posizione (assoluta) alla quale è possibile eseguire un operazione, altrimenti un intero rappresentante il tipo di errore verificatosi.La system call per la seek è sys_lseek ed è definita nel file /usr/src/linux/fs/read_write.c;

asmlinkage int sys_lseek (unsigned int fd, off_t offset, unsigned int origin){struct file * file;int tmp = -1;

11

Page 12: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

if (fd >= NR_OPEN || !(file=current ->files->fd[fd]) || !(file ->f_inode)) 1return -EBADF;

if (origin>2) 2return –EINVAL;

if (file->f_op && file -> f_op -> lseek)3

return file-> f_op -> lseek(file->f_inode, file, offset, origin);/* this is the default handler if no lseek handler is present */

switch (origin) { 4case 0: tmp = offset; break;case 1: tmp = file -> f_pos +offset; break;case 2: if (!file->f_inode) return –EINVAL; tmp=file->f_inode->i_size +offset; break;}

if (tmp<0) 5 return –EINVAL;

if (tmp!=file->f_pos) { 6 file ->f_pos= tmp; file->f_reada=0; file->f_version=++event;}return file ->f_pos;

1. Se il file non è un file aperto dal processo corrente o se non c’è un inode corrispondente viene restituito il messaggio di errore EBADF – Numero di file errato;

2. Se l’origine è maggiore di 2 viene restituito il messaggio di errore EINVAL – Invalid argument.

3. Se sono state definite delle operazioni sul dispositivo al quale appartiene il file specificato e tra queste c’è un’operazione di lseek () viene invocata quest’ultima e il risultato prodotto viene ritornato all’esterno.

4. Se invece non c’è una definizione di operazioni specifiche, viene eseguita l’azione del gestore default contenuto in sys_lseek (). La posizione temporanea (tmp) sarà: Se l’origine è 0 (inizio file) l’offset Se l’origine è 1 (posizione attuale) posizione attuale + offset Se l’origine è 2 (fine file) si controlla se il file non ha un inode, nel qual caso si

ritorna EINVAL – Invalid argument; se invece c’è l’inode la posizione è data dalla dimensione del file + l’offset.

5. Se la posizione temporanea che è stata appena calcolata fosse negativa si ritorna EINVAL – Invalid argument.

6. Infine, se tutto è andato bene, si aggiorna la posizione effettiva del file.

12

Page 13: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

readdir (struct inode*, struct file *, struct dirent *, int)

Legge le informazioni relative alla directory. E’ propria del file system più che del dispositivo.

select (struct inode *, struct file *, int, select table *)

E’ importante perché permette di gestire più accessi contemporanei ad un dispositivo; una simile gestione è tipicamente abbinata al meccanismo delle interruzioni. Un tipico esempio d’uso è quando si vogliono gestire più letture contemporanee da dispositivo a caratteri e senza polling.Il codice relativo alla system call sys_select () si può trovare nel file /usr/src/linux/fs/select.cLa generica applicazione invoca la system call sys_select () su una lista di descrittori di files tra i quali essa dovrà selezionarne uno da attivare (svegliare); per compiere questa operazione è previsto l’uso di un timer per sospendere il processo quando i dispositivi non sono pronti. La sys_select () non richiama direttamente la file_operation select; quest’ultima è richiamata attraverso la sequenza: sys_select () – do_select () – check () – select ().La file operation select () riceve come parametri: struct inode* è il puntatore alla struttura degli inode del dispositivo;struct file* è il puntatore alla struttura dei files del dispositivo;int è il tipo di select richiesto:

SEL_IN=letturaSEL_OUT=scritturaSEL_EX=eccezione

select_table* è un puntatore ad una coda di attesa.

La coda di attesa è definita in: /usr/src/linux/include/linux/wait.h:

struct select_table_entry{struct wait_queue wait;struct wait_queue ** wait_address;

;

typedef struct select_table_struct{int nr;struct select_table_entry * entry;

} select_table;

Se il puntatore alla coda di attesa non è NULL e non si è verificato un errore la select () sospende il processo corrente disponendo che il suo risveglio avvenga non appena il dispositivo torna pronto, evento segnalato da un’interruzione. Se il puntatore alla coda di attesa risulta NULL la select () controlla se il dispositivo è pronto e ritorna in ogni caso. Se il processo che richiede tramite select () un’operazione non disponibile e vuole sospendersi, può farlo invocando la funzione:select_wait(struct wait_queue** wait_address, select_table* p)definite in /usr/src/linux/include/linux/sched.h.Questa funzione è simile alla interruptible_sleep_on (): il suo compito è di aggiungere un processo alla coda di attesa puntata da wait_address. Una volta eseguita la select_wait () il processo non è ancora sospeso ed il controllo ritorna alla file operation select () che a sua volta passa il controllo alla system call sys_select () che esegue la vera

13

Page 14: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

e propria sospensione del processo: a tal fine invoca la do_select () che porrà lo stato del processo a TASK_INTERRUPTIBLE e invocherà infine la schedule ().Il protocollo che bisogna eseguire quando si scrive una file_operation select () e si deve codificare l’azione conseguente alla disponibilità del dispositivo è:

1. se il dispositivo è pronto si ritorna 1;2. se il dispositivo non è pronto si invoca la select_wait () e successivamente si ritorna 0.

Un’accortezza da eseguire nella scrittura di una file_operation select () che utilizza dei time out consiste nel non utilizzare current ->timeout perché questa viene usata dalla sys_select () e dalle funzioni e quest’ultima invoca. Bisogna invece definire una struttura:

struct timer_list {struct timer_list * next;struct timer_list * prev;unsigned long expires;unsigned long data;void (*function ) (unsigned long);

};

(definita in /usr/src/linux/include/timer.h) e dopo averla inizializzata con init_timer () passarla ad add_timer () per “caricare” il time out. Timer_list contiene:

due puntatori (*next, *prev) ad elementi timer_list mediante i quali è possibile realizzare una struttura circolare ;

un intero long positivo expires che contiene il numero dei centesimi di secondo trascorsi i quali si vuole ricevere un segnale di time out;

un intero long positivo data che contiene l’argomento per la funzione *function: essendo della stessa dimensione di (void*) può essere anche un puntatore all’area di memoria dell’utente;

un puntatore ad una funzione sullo spazio del kernel che deve eseguita una volta ricevuto il time out.

ioctl (struct inode*, struct file*, unsigned int, unsigned long)

Un ioctl (I/O control) è un comando inviato ad un dispositivo per modificare lo stato sotto il qualedeve operare. La file operation ioctl () riceve come parametri:struct inode* è il puntatore alla struttura degli inode del dispositivo;struct file* è il puntatore alla struttura dei files del dispositivo;unsigned int è il comando ioctl;unsigned long è l’argomento per il comando ioctl; può essere un intero senza segno, ma

essendo della stessa dimensione di un (void*) può essere anche un puntatore ad un’area di memoria dell’utente.

La struttura della file_operation ioctl () prevede, solitamente, un iniziale controllo di condizioni di errore ed una successiva istruzione C switch i cui rami rappresentano tutti i possibili comandi ioctl: la variabile di switch è proprio l’intero senza segno passato come parametro alla funzione.La system call sys_ioctl () ha quattro ioctl predefiniti. Si veda ad esempio:

asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg){

struct file * filp;int on;if (fd >=NR_OPEN || !(filp=current ->files ->fd[fd]))

14

Page 15: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

return –EBADF;switch (cmd) {case FIOCLEX: /*=0x5451 setta il bit close–on-exec */……case FIONCLEX: /*=0x5450 resetta il bit close–on-exec */

……case FIONBIO: /*=0x5421 se arg=0 setta O_NONBLOCK, altrimenti lo resetta */……case FIOASYNC: /*=0x5452 se arg=0 setta O_SYNC, altrimenti lo resetta */……

default:if (filp->f_inode && S_ISREG (filp->f_inode->i_mode))

return file_ioctl (filp, cmd, arg);if (filp->f_op && filp->f_op->ioctl)

return filp->f_op-> ioctl(filp->f_inode, filp, cmd, arg);return –EINVAL;

}}

init() Pur non esistendo un puntatore ad una tale funzione nella struttura file_operations, è assolutamente necessario fornire una sua definizione: il motivo è che la init() esegue la registrazione nel VFS della struttura file_operations per il gestore di dispositivo che si vuole implementare e in assenza di questa registrazione il VFS non sarà in grado di smistare le chiamate alle corrette file_operations.L’invocazione della funzione init() avviene in fase di boot e configurazione del kernel: la funzione riceve l’indirizzo dell’area di memoria libera e configura i dispositivi, alloca la memoria necessaria per le code di attesa o per il funzionamento dei gestori basati su interruzioni e ritorna il nuovo indirizzo di memoria libera. La funzione nella quale si può inserire la fase di inizializzazione per dispositivi a carattere è chr_dev_init(), definita in:/usr/src/linux/driver/char/mem.c

long chr_dev_init(long mem_start, long mem_end){if (register_chrdev (MEM_MAJOR, “mem”, &memory_fops)) printk(“unable to get major %d for memory devs \ n”, MEM_MAJOR); mem_start = tty_init(mem_start); #ifdef CONFIG_PRINTER mem_start = lp_init(mem_start); #endif ……return mem_start}Questa funzione esegue la registrazione delle file operations direttamente invocando la funzione register_chrdev() e indirettamente richiamando le funzioni di init dei vari dispositivi.

long lp_init (long kmem_start) {int offset = 0;unsigned int testvalue = 0;int count = 0;

if (register_chrdev(LP_MAJOR, “lp”, &lp_fops)) { printk(“unable to get major %d for line printer \n”, LP_MAJOR); return kmem_start;

15

Page 16: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

}……}Ogni funzione di init esegue la registrazione delle file operations per il particolare dispositivo che viene inizializzato invocando sempre register_chrdev(): questa funzione è definita in:/usr/src/linux/fs/device.c

int register_chrdev(unsigned int major, const char * name, struct file_operations * fops){if (major==0) { for (major=MAX_CHRDEV-1; major>0; major--){

if (chrdevs[major].fops==NULL) { chrdevs[major].name=name; chrdevs[major].fops=fops; return major; }}

return –EBUSY;}if (major>=MAX_CHRDEV) return –EINVAL;if (chrdevs[major].fops && chrdevs[major].fops != fops) return –EBUSY;chrdevs[major].name=name;chrdevs[major].fops=fops;return 0;}La funzione register_chrdev () riceve in ingresso la classe del dispositivo, vale a dire il major number, una stringa rappresentante il nome del dispositivo e il puntatore alla struttura che definisce le file operations per quel dispositivo. Alla fine di questa registrazione, per ogni accesso al dispositivo il VFS devierà le chiamate alle file operations del dispositivo quando queste esistono o, altrimenti, eseguirà delle azioni di default.

16

Page 17: IL VIRTUAL FILESYSTEM DI LINUX - unisi.itmonica/seminari/lava.doc · Web viewCorso di Laurea in Ingegneria Informatica N.O. Indirizzo Reti e Sistemi Multimediali A.A. 2001-2002 “IL

SOMMARIO

1 CENNI INTRODUTTIVI SUL FILE SYSTEM.............................................................................2

1.1 L'organizzazione di file e directory................................................................................................21.2 I tipi di file......................................................................................................................................31.3 Caratteristiche specifiche dei file in Linux.....................................................................................3

2 IL VIRTUAL FILE SYSTEM DI LINUX.......................................................................................4

2.1 Architettura generale......................................................................................................................42.2 Oggetti e funzionamento del Virtual File System..........................................................................6

17