



Studia grazie alle numerose risorse presenti su Docsity
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Prepara i tuoi esami
Studia grazie alle numerose risorse presenti su Docsity
Prepara i tuoi esami con i documenti condivisi da studenti come te su Docsity
Trova i documenti specifici per gli esami della tua università
Preparati con lezioni e prove svolte basate sui programmi universitari!
Rispondi a reali domande d’esame e scopri la tua preparazione
Riassumi i tuoi documenti, fagli domande, convertili in quiz e mappe concettuali
Studia con prove svolte, tesine e consigli utili
Togliti ogni dubbio leggendo le risposte alle domande fatte da altri studenti come te
Esplora i documenti più scaricati per gli argomenti di studio più popolari
Ottieni i punti per scaricare
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Libro di Michael Dahlin, Thomas Anderson tradotto in italiano (13 capitoli).
Tipologia: Appunti
1 / 6
Questa pagina non è visibile nell’anteprima
Non perderti parti importanti!




- (08/05/18 - Professore)
Capitolo 11
File System: Introduzione ed Overview
I computer dovrebbero permettere di memorizzare i dati in modo affidabile. Foto, file musicali, e directory di email; i programmatori memorizzano documenti di design e file sorgenti; chi lavora in ufficio memorizza documenti ed altri. Infatti, per un computer che ha bisogno di memorizzare tutto ciò, ha bisogno di memorizzare programmi per eseguirli.
Per tutti questi casi, gli utenti richiedono molte cose per i loro dispositivi di memorizzazione:
Dispositivi di memorizzazione non volatili e file system.
I contenuti di una memoria DRAM principale potrebbe essere perso se un sistema smette di funzionare o non vi è più corrente, i dispositivi non volatili sono stabili e conservano i loro dati anche in presenza di crash o malfunzionamenti; tali dispositivi sono anche chiamati persistent storage o stable storage. I dispositivi non volatili possono avere molta più capacità a minor costo di una DRAM volatile che costituisce la maggior parte della memoria principale di gran parte del sistema.
Tuttavia, i dispositivi non volatili hanno le loro limitazioni. Per esempio, i correnti dispositivi non volatili come dischi magnetici e dispositivi flash non permettono l’accesso alle singole parole di archiviazione; invece, l’accesso deve essere fatto in modo grossolano – 512,2048, o più byte alla volta.
Inoltre, questi accessi possono essere più lenti rispetto ad una DRAM; per esempio, leggere un settore da un disco magnetico potrebbe richiedere l’attivazione di un motore per muovere il braccio del disco alla traccia desiderata e di aspettare fin quando tale traccia non sia sotto la testa del braccio rotante. Poiché gli accessi al disco richiedono motori e movimenti, il tempo per un accesso random potrebbe essere di circa 10 ms. Al contrario, la latenza di una DRAM potrebbe essere sotto i 100 nanosecondi. Questa enorme differenza indirizza il sistema operativo ad organizzare e usare i dispositivi di memorizzazione persistenti in modo differente rispetto alla memoria principale.
I file system sono un’astrazione del sistema operativo per permettere alle applicazioni di accedere ai dispositivi di memorizzazione non volatili. I file system usano un numero di tecniche per far fronte alle limitazione fisiche di tali dispositivi e forniscono un’astrazione migliore per gli utenti.
Per migliorare ulteriormente l’affidabilità, i file system memorizzano dei checksum con i dati per individuare i blocchi corrotti, e replicare i dati tra device di memorizzazioni multipli per recuperarli in caso di guasti.
Impatto sulla scrittura delle applicazioni. Capire le proprietà dell’affidabilità e di performance dell’hardware di memorizzazione e dei file system è importante anche se tu non stai costruendo un file system da zero. Poiché esistono delle fondamentali limitazione nei dispositivi di memorizzazione, le illusioni di affidabilità e di performance fornita ad alto livello sono imperfette.
Per esempio, supponiamo di editare un grande documento con molte immagini e che il processore salva periodicamente in modo automatico tale file in modo tale che di non perdere molte modiche se il sistema dovesse crashare. Se le applicazioni usassero il file system in modo semplice, potrebbero accadere cose inaspettate.
I programmi usano un range di tecniche per trattare questi tipi di problemi. Per esempio, molti strutturano il loro codice per avvantaggiare la semantica del sistema operativo. Per esempio, molti sistemi operativi garantiscono che quando un file è rinominato con un nome uguale ad uno già esistente, tale nome
Nella figura soprastante, poiché le directory possono includere nomi di altre directory, si può vedere come le directory possono essere organizzate in una gerarchia che differenzia insieme di file associati che possono essere raggruppati in directory differenti. La stringa che identifica un file o una directory è chiamata path. Il simbolo / separa i componenti del path, ciascuno dei quali rappresenta una directory.
Se pensassimo ad una directory come un albero, la radice dell’albero verrà chiamata root directory. I name path come /bin/ls che iniziano con / vengono definiti path assoluti i quali sono interpretati a partire dalla root directory. I path come Work/Class/OS che non iniziano con / sono definiti path relativi e sono interpretati a partire dalla directory corrente in cui il processo è in esecuzione.
Quando fai il log in, la shell inizialmente è settata alla home directory. I processori possono cambiare la directory corrente tramite la system call chdir(path). Così, per esempio, se tu stessi lavorando ad un progetto, potresti trovare conveniente far apparire il progetto in entrambe le directory “todo” e “OS” come nella figura sottostante.
Il mapping tra un nome e il file sottostante è chiamato hard link.
Se il sistema permette più hard link per lo stesso file, allora la gerarchia per le directory non può essere più un albero. La maggior parte dei file system che permettono hard link multipli per un file restringono tali link per evitare cicli, assicurando che venga creato un grafo aciclico (DAG). Evitare cicli può semplificare la gestione, per esempio, assicurandosi che gli attraversamenti ricorsivi di una directory terminano o usando il contatore delle reference per poter permettere al garbage collector di intervenire su di un file quando l’ultimo riferimento ad esso viene cancellato.
In aggiunta agli hard link, molti sistemi forniscono altre vie per usare nomi multipli per lo stesso file. Basta guardare la sidebar per un confronto di hard link, soft link, shortcut, ed alias.
Volume. Ogni istanza di un file system gestisce i file e le directory per un determinato volume. Un volume è una collezione di risorse di memorizzazione fisiche che formano un dispositivo di memorizzazione logico.
Un volume è un’astrazione che corrisponde al disco logico. Nel più semplice dei casi, un volume corrisponde ad un singolo disco fisico. Alternativamente, un singolo disco fisico può essere partizionato in volumi multipli o parecchi dischi fisici possono essere combinanti in un singolo volume disco fisico estendendosi in più dischi fisici.
Un computer singolo può usare file system multipli memorizzati su più dischi montando volumi multipli in una gerarchia logica singola. Montare un volume su un file system esistente crea un mapping di qualche path nel file system esistente alla root directory del file system del volume montato e lascia le mappature di controllo del file system montate per tutte le estensioni di tale path.
Per esempio, supponiamo che una chiavetta USB contiene un file system le cui cartelle sono /Movies e / Backup. Se Alice inserisse tale chiavette all’interno del laptop, il sistema operativo dovrebbe montare il file system del volume dell’USB con il path /Volumes/usb1. Se Alice facesse una open(/Volume/usb1/Movies/ vacation.mov), aprirà il file /Movies/vacation.mov dal file system del volume dell’USB. Allo stesso modo Bob aprirebbe media/disk1 per accedere al file system del volume montato attualmente e attraverso il path / Movies/vacation.mov aprirà il suo file.
Creare e cancellare file. I processi creano e distruggono i file con create() e unlink(). Create() fa due cose: crea un nuovo file e inizializza i suo metadati, e crea un nome per tale file nella directory.
link() crea un hard link – un nuovo path name per un file esistente. Dopo che la chiamata a link() termina correttamente, ci sono path name multipli che riferiscono lo stesso file sottostante.
unlink() rimuove un nome per un file dalla sua directory. Se un file ha nomi o link multipli, unlink() rimuove il nome specifico, lasciando che tale file sia accessibile da altri nomi. Se il nome specificato è l’unico link al file, allora l’unlink() cancellerebbe anche il file sottostante e le sue risorse.
Mkdir() e rmdir() creano e cancellano directory.
Linking ad un file vs linking ad una directory.
Sistemi come Linux supportano una system call link(), non consentono la creazione di nuovi hard link in una directory. Per esempio, existingPath non dovrebbe essere una directory. Perché Linux fa questo?
Per prevenire multipli hard link ad una directory prevenendo i cicli, assicurandosi che la struttura della directory sia sempre un grafico orientato aciclico (DAG).
In aggiunta, realizzare gli hard link per una directory confonderebbe la directory principale con la voce di una directory.
Aprire e chiudere. Per iniziare ad accedere un file, un processo chiama la open() per ottenere un file descriptor , tale riferimento può essere usato per riferire il file aperto. Il file descriptor è una terminologia Unix; in altri sistemi ed API gli oggetti svolgono regole simili chiamando tali file handles o file streams.
Il sistema operativo richiede ai processi di fare una open() esplicita ai file e di accedere a quest’ultimi tramite i file descriptor piuttosto che passare il path ogni qual volta si voglia fare una read() o write(). Per prima cosa, il path viene parsato e vengono controllati i diritti ogni qual volta viene aperto un file e questo non viene eseguito più ogni qual volta viene fatta una read() o write(). In secondo luogo, quando un processo apre un file, il sistema operativo crea una struttura dati che memorizza le informazioni sul processo che ha aperto il file come l’ID del file, se il processo può scrivere o solo leggere il file, e un puntatore alla posizione corrente del processo all'interno del file. Il file descriptor può anche essere pensato come una riferimento alla struttura dati che il sistema operativo usa per gestire gli accessi del processo al file per ogni file aperto nel sistema operativo.
Quando un’applicazione ha terminato di usare il file, chiama la close() , la quale rilascia il record del file aperto nel sistema operativo.
Accesso ai file. Quando un file è aperto, un’applicazione può accedere ai dati del file in due modi. Per prima cosa, può usare le normali procedure dell’interfaccia fornita, facendo delle chiamate di sistema come read() o write() su un file precedentemente aperto. Chiamare la read() e write() parte dalla posizione del file del processo corrente, e avanza di posizione in base ai byte che sono stati letti o scritti. Così, una sequenza di read() o write() si muove sequenzialmente all’interno di un file. Per supportare gli accessi random all’interno di un file, la chiamata di sistema seek() si posiziona in un punto preciso all’interno del file aperto.
Piuttosto che usare read() e write() per accedere ai dati del file, un’applicazione potrebbe usare mmap () per stabilire un mapping tra una regione della memoria virtuale del processo e qualche regione del file. Una volta che il file è stato mappato, ogni load o store per quella regione di memoria virtuale leggerà e scriverà i dati del file accedendo a pagine condivise nella cache del kernel, o segnalando un page fault in caso tale pagina non fosse presente in memoria. Quando un’applicazione ha finito con il file, può chiamare l’ munmap () per rimuovere tale mapping.
Infine, la chiamata fsync () è importante per la realizzazione dell’affidabilità. Quando un’applicazione aggiorna un file tramite write() o una memorizzazione in memoria per un file mappato, tali aggiornamenti sono memorizzati in un buffer e soltanto successivamente questi vengono effettivamente memorizzati. Le applicazioni usano questa funzione per due motivi. Per prima cosa, tale chiamata assicura che gli aggiornamenti sono duraturi e non saranno persi se il sistema andrebbe in crash o si staccasse al corrente. In secondo luogo, tale chiamata tra due aggiornamenti assicura che il primo aggiornamento verrà fatto sempre prima del secondo. Da notare che la chiamata ad fsync() non è sempre necessaria; il sistema