File system
Il file system è quel componente del SO che realizza i servizi di gestione dei file. File: unità di archiviazione in memoria di massa (costituito da una sequenza di byte). Rappresenta la struttura logica della memoria di massa (organizzazione e memorizzazione dei file) ed è costituito da un insieme di funzioni di SO e relative strutture dati di supporto.
Virtual file system
Il virtual file system è un'interfaccia unica per gestire diversi FS che mette a disposizione le API che consentono l'utilizzo indipendente delle chiamate di sistema. Le astrazioni fornite dal file system sono:
- File, link e operazioni su file
- Directory e file speciali
- Attributi e politiche di accesso
- Mapping del file system logico sui dispositivi fisici
Volume
I dispositivi di memorizzazione non volatile possono essere suddivisi in porzioni dette partizioni; ogni partizione può essere funzionalmente considerata come un dispositivo logico indipendente. L'indirizzamento dei dati si basa sul concetto di LBA. LBA è uno schema di indirizzamento nel quale l'intero dispositivo è rappresentato come un vettore lineare di blocchi.
Volume: partizione di qualsiasi dispositivo di memorizzazione non volatile di massa dotato di uno schema d'indirizzamento LBA. Il blocco costituisce l'unità fondamentale di informazione che viene trasferita con una sola operazione tra il disco e la memoria centrale.
Interazione con la memoria
L'accesso ai dati su disco è un'operazione misurata in millisecondi, i tempi di esecuzione delle istruzioni in nanosecondi. FS e gestione memoria (in particolare page cache) devono collaborare. Quando il VGS o un FS vuole leggere un blocco su un volume, lo richiede in realtà alla Page Cache:
- Se il blocco è già in page cache, questa restituisce immediatamente l'indirizzo di memoria del blocco;
- In caso contrario, la Page Cache alloca lo spazio necessario in una pagina e richiede al gestore a blocchi di leggere il blocco dal dispositivo nella pagina allocata.
File
Un file è costituito da:
- Sequenza di byte
- Nome simbolico e univoco all'interno della directory
- Insieme di attributi (tipo, locazione nel dispositivo, dimensione, protezione, time stamp, proprietario)
Un file in LINUX può:
- Contenere dati o programmi (file normale)
- Contenere riferimenti ad altri file (file direttore)
- Rappresentare l'astrazione di un dispositivo periferico (file speciale)
Tutte le operazioni sul contenuto del file richiedono il trasferimento in/da memoria di byte e partono dalla posizione corrente nel file, aggiornandola.
Operazioni
- Creazione, apertura, chiusura, cancellazione
- Lettura, scrittura
- Riposizionamento
Modello d'utente
L'accesso al singolo file avviene tramite funzioni utilizzate per scrivere e leggere informazioni sui singoli file (creat, open, close, fsync, read, write, lseek, ecc.). Organizzazione della struttura complessiva dei file: funzioni utilizzate per organizzare i file nei directory (folder, cartelle) e volumi (unlink, link, mkdir, mknod, dup, ecc.).
Modello per l'accesso al singolo file
L'accesso a un file può avvenire secondo due modalità:
- La mappatura di una VMA sul file tramite mmap()
- Le system calls classiche di accesso ai file, simile alle funzioni della libreria C standard. (Il VFS dovrà implementare entrambe le modalità).
Funzioni per la gestione dei file
Per operare su un file è necessario:
- Aprirlo, se il file esiste già
- Crearlo (inserirlo nel file system) se il fine non esiste
Le funzioni di apertura e creazione richiedono come parametro il nome del file e restituiscono un intero non negativo chiamato descrittore del file. Le funzioni di lettura e scrittura (e altre ancora) utilizzano come riferimento il descrittore del file e operano su sequenza di byte a partire da un byte specifico, la cui posizione è contenuta dall'indicatore di posizione corrente.
Gestione dei file
L'immagine del file system è memorizzata in memoria di massa. Il SO mantiene in memoria centrale una parte delle strutture dati per gestire i file. A questo livello interessa mettere in evidenza:
- Tabella dei file aperti per ogni processo: vettore che contiene informazioni sui file aperti da un certo processo (l'indice del vettore rappresenta il descrittore del file).
- Tabella globale dei file aperti nel sistema: vettore che contiene un elemento per ogni file aperto nel sistema. Ogni elemento viene referenziato tramite gli elementi delle tabelle dei file aperti dei processi e contiene, tra le altre, l'indicatore di posizione corrente nel file, il numero di processi in accesso (link) al file e un riferimento (i-node) al file su disco.
Funzioni di creazione e apertura
La funzione creat viene aggiunto un nuovo file al file system.
int creat(char *nomefile, int permessi)
Operazioni richieste:
- Allocazione dello spazio su disco
- Aggiunta dei riferimenti al file nel file system
Restituisce un intero che rappresenta il descrittore del file.
La funzione open individua la posizione del file sul disco sulla base del nome.
int open(char *nomefile, int tipo, int permessi)
Permessi:
- Gestisce i permessi di accesso
- Tipo: O_RDONLY (solo lettura)
- O_WRONLY (solo scrittura)
- O_RDWR (lettura e scrittura)
Entrambe le funzioni implicano l'aggiornamento della tabella globale dei file aperti e della tabella dei file aperti dal processo.
Lettura e scrittura
int read(int fd, char buffer[], int numero_byte) int write(int fd, char buffer[], int numero_byte)
Accedono al file tramite descrittore (ottenuto da create o open) e restituiscono il numero di caratteri effettivamente letti/scritti, aggiornando l'indicatore di posizione corrente. Buffer; variabile per dati che devono essere scritti su file o che venIl file system mantiene un indicatore alla posizione in cui deve essere effettuata la scrittura successiva.
Riposizionamento
int lseek(int fd, long offset, int riferimento)
Sposta la posizione degli indicatori di lettura e di scrittura. Le operazioni consentite dipendono dal tipo di accesso al file, e spesso viene mantenuto dal file system un solo indicatore di posizione, valido sia per scrittura che per lettura.
Riferimento: 0 p=inizio file + offset, 1 p=pos. prec. + offset, 2 p=fine file + offset.
Duplicazione
Crea un nuovo descrittore associato ad un file già aperto con open.
fd2 = dup(fd1)
Fork() condivide automaticamente tutti i file aperti dal padre con il figlio.
close() e fsync()
Elimina il legame tra descrittore e file.
int close(int fd)
Restituisce l'esito dell'operazione di chiusura. Questo però non garantisce che i dati scritti in memoria vengano trasferiti immediatamente su disco. Per far sì che questo accada è necessario invocare la fsync.
int fsync(int fd)
Questa operazione scrive su disco tutti i dati del file che sono stati modificati in memoria.
link() e unlink()
Per associare un nuovo nome ad un file esistente:
int link(char *nome, char *nuovo_nome)
Crea una nuova entry nel direttore che contiene nuovo_nome. Non crea un nuovo descrittore, ma incrementa di 1 i riferimenti (link) a quel file. Per eliminare un link ad un file specificato tramite nome (decrementare di 1 il numero di riferimenti), si usa:
int unlink(char *nome)
Se esiste un solo link, il file viene eliminato dal file system. In questo caso le azioni necessarie sono:
- Deallocazione dello spazio sul dispositivo fisico
- Aggiunta dello spazio deallocato alla lista dello spazio disponibile sul dispositivo
- Rimozione del descrittore del file dal file system
Organizzazione complessiva dei file
I file sono organizzati in una struttura ad albero i cui nodi sono detti cataloghi (directory, cartelle, folder ecc.). Per semplificare la gestione dei file, i nomi dei file sono inseriti nei cataloghi. Un catalogo non è altro che un file dedicato a contenere i nomi di altri file e le informazioni necessarie al sistema per accedere tali file. I file dedicati a servire come catalogo sono detti di tipo catalogo, mentre i file che contengono normali informazioni sono detti di tipo normale.
La struttura dei cataloghi si basa sull'esistenza di un unico catalogo principale, detto radice (root) che il sistema è in grado di identificare e accedere autonomamente, e che può contenere riferimenti sia a file normali che file catalogo, e così via. Il nome completo (pathname) di un file di qualsiasi tipo è costituito dal concatenamento dei nomi di tutti i cataloghi sul percorso che porta dalla radice al file stesso, separati dal simbolo "/".
Directory
I direttori sono dei file e quindi sono caratterizzati da nome e diritti d'accesso. Contengono informazioni sui file e direttori in essi contenuti (nome, attributi, proprietario). Forniscono una corrispondenza tra nomi di file e file stessi. Il loro contenuto può essere visto come una tabella, con una entry per ogni file/directory contenuto. Ogni entry è del tipo: <riferimento al file/directory su disco, nome file/directory>.
Operazioni sulle directory
Sulle directory, così come sui file, è possibile compiere alcune operazioni:
- Ricerca di un file: sulla base di un nome o una espressione regolare consente di recuperare le informazioni su uno o più file.
- Creazione di un file: alla directory viene aggiunto un elemento che raccoglie informazioni sul nuovo file.
- Rimozione di un file: eliminazione di un descrittore di file. Il descrittore deve essere prima individuato tramite una operazione di ricerca.
- Elenco dei file: produce l'elenco dei nomi ed eventualmente altre informazioni relative ai file memorizzati.
- Rinomina un file: modifica il nome di un file già presente nella directory. Il descrittore deve essere prima individuato tramite una operazione di ricerca.
Per creare una directory si usa la funzione mknod().
Periferiche e file speciali
In unix le periferiche sono viste come file speciali, simulate come file nel direttorio /dev. Sulle periferiche è possibile eseguire open, read e write (purché ne abbia senso), close ma non creat (per crearli si usa mknod). Ogni programma, all'esecuzione, dispone già di 3 descrittori di file: stdin, stdout, stderr associati rispettivamente alla tastiera e al video. Nella tabella dei file aperti di un processo, i primi 3 elementi sono associati a questi descrittori.
Area buffer/cache e gestori dei dispositivi a blocchi
Il file system svolge le sue funzioni appoggiandosi a due componenti:
- Il gestore dei buffer/cache gestisce le zone di memoria destinate a contenere blocchi dei file che sono stati letti da disco.
- Il gestore del disco (disk driver) che disaccoppia il dispositivo logico da quello fisico.
Il gestore dei buffer/cache gestisce la Page cache (zona di memoria centrale) con il principio di tenere in memoria il più a lungo possibile i blocchi letti da disco. Quando il file system richiede la lettura di un blocco di un file passa la richiesta al gestore dei buffer/cache. Se il blocco del file è già nella page cache, il gestore dei buffer lo fornisce al file system, in caso contrario il file system identifica l'LBA del blocco da caricare e richiede al gestore di allocare spazio e di caricare il blocco. Il gestore dei buffer interagisce con il disk driver, accodando la richiesta di trasferimento che sarà soddisfatta in maniera ottimale dal driver in funzione delle operazioni fisiche su disco.
Volumi
In LINUX esiste un unico albero di directory con un'unica radice (indicata da /). Diversi volumi, cioè partizioni dotate del loro FS, sono rappresentati da nodi dell'albero detti mount_point. Il sottoalbero la cui radice è un mount_point descrive la struttura interna del volume, mentre la posizione del mount_point nell'albero generale lo rende raggiungibile partendo da /. Per inserire un nuovo volume è necessario compiere due operazioni:
- Associare un FS al volume (device)
- Montare il volume in un opportuno mount_point.
Modello del virtual file system
Il modello del VFS deve rappresentare due tipi di informazioni:
- Le informazioni relativamente statiche contenute nei file e cataloghi memorizzati nei diversi volumi. Queste informazioni sono presenti sui dischi e quindi permangono anche dopo lo spegnimento del sistema. Vengono caricate in memoria in base alle esigenze.
- Le informazioni dinamiche associate ai file e cataloghi aperti durante il funzionamento del sistema, ad esempio la posizione corrente di un file.
Strutture dati
Il modello del VFS si basa su un certo numero di strutture dati:
- Struct dentry: ogni istanza di questa struttura rappresenta una directory nel VFS.
- Struct inode: ogni istanza di questa struttura rappresenta uno e un solo file fisicamente esistente nei volumi, e contiene i cosiddetti metadati del file.
- Struct file: ogni istanza di questa struttura rappresenta un file aperto nel sistema.
Queste strutture dati contengono, oltre ai campi utilizzati per rappresentare i contenuti specifici, dei puntatori che permettono di collegarle in modo da rappresentare in forma di strutture dati le informazioni necessarie a caratterizzare lo stato corrente dei file nel modello del VFS. Le principali tra tali strutture dati sono:
- La struttura dei cataloghi (directory)
- La struttura di accesso dai processi ai file aperti
La struttura dei cataloghi del modello del VFS è costituita esclusivamente da istanza di struct dentry che rappresentano i nodi dell'albero dei direttori.
Struttura di accesso dai processi ai file aperti
Ogni descrittore di processo contiene 2 puntatori rilevanti ai file dell'accesso ai file. Il descrittore di processo è definito dalla struct task_struct. Le istanze di struct fs_struct contengono i parametri che caratterizzano i singoli FS registrati nel sistema. Il campo fs è un puntatore alla lista dei FS utilizzati dal processo e permette quindi di reperire, nei momenti in cui servono, le informazioni specifiche relative al FS. Il campo files è un puntatore a una struttura files_struct. Il componente fondamentale di questa struttura è un array di dimensione fissa fd_array (che chiameremo anche tabella dei file aperti dal processo). Questo array contiene un elemento per ogni file aperto dal processo. Ogni elemento è un puntatore a un'istanza di struct file. Il primo puntatore serve a collegare tutti i file aperti in una lista e non è utilizzato dalla struttura di accesso. F_pos rappresenta la posizione corrente del file che viene aggiornata dinamicamente. Il puntatore rilevante per costruire la struttura di accesso è f_dentry, che punta all'istanza di dentry che è stato utilizzato per aprire il file.
Numero di aperture contemporanee e contatore f_count
Diverse righe nell'ambito di uno stesso processo o di più processi possono puntare allo stesso file; in tal caso tutte le operazioni sui relativi descrittori condividono la posizione corrente. Il contatore dei riferimenti f_count indica i descrittori che puntano contemporaneamente sul file. Il modo più comune per generare due descrittori che puntano allo stesso file è costituito da dup o fork. L'operazione fork, creando un processo figlio identico al padre, duplica la tabella dei file aperti del processo e quindi determina l'esistenza di descrittori in diversi processi che puntano allo stesso file. L'apertura indipendente da parte di un processo R in un file già aperto da parte di P crea una nuova istanza di struct file, con posizione corrente indipendente. L'i-node è tuttavia condiviso, perché si tratta dello stesso file fisico. La struttura a valle dei descrittori, costituita da istanze di dentry e inode, è considerata eliminabile solo quando il contatore di f_count diventa 0.
Struct inode
Un file è rappresentato nel VFS da un'istanza della struct inode. La corrispondenza tra file e (istanze di) inode è rigorosamente biunivoca. Un inode contiene tutte le metainformazioni necessarie a caratterizzare il file. L'esistenza di una lista dei dentry (i_dentry) che fanno riferimento al file è dovuta al fatto che un file può avere più di un nome nella struttura dei cataloghi. Il superblocco di un FS è una struttura dati che contiene la definizione delle caratteristiche generali del FS stesso. Quindi il puntatore i_sb permette di risalire da un file al FS che lo gestisce. Le strutture di tipo _operations i_op e i_fop contengono i puntatori alle funzioni dello specifico FS che implementano le funzioni generali del VFS. Queste 2 strutture sono rese accessibili direttamente dal inode di ogni file per rendere più immediata la loro invocazione. Le 2 strutture danno accesso rispettivamente alle funzioni del FS specifico per la gestione dell'organizzazione complessiva dei file e cataloghi e alle operazioni di accesso al singolo file:
- La struct inode_operations contiene puntatori alle implementazioni specifiche di operazioni quali create, mknod, link, unlink, mkdir, rmdir, …
- La struct file_operations contiene puntatori alle implementazioni specifiche di operazioni quali open, read, write, lseek…
L'implementazione di queste funzioni appartiene allo specifico FS, ma deve ovviamente rispettare la struttura dei prototipi indicata in questa struct. Il campo struct address_space *i_mapping contiene alcune informazioni fondamentali per realizzare le operazioni di trasferimento dei dati dal volume alla memoria.
Accesso ai dati di un file
Nel modello di utente l'accesso ai dati di un file avviene tramite le operazioni read e write.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Calcolatori elettronici
-
Calcolatori Elettronici - Sistemi Operativi
-
Architettura dei calcolatori
-
Calcolatori elettronici e sistemi operativi