









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
Appunti del corso Sistemi Operativi 2 sul file system basati sulle slide del professor Anglano, anno accademimo 25/26. Comprende implementazione di file e directory, gestione dello spazio libero, gestione file condivisi, backup, consistenza del file system
Tipologia: Appunti
1 / 15
Questa pagina non è visibile nell’anteprima
Non perderti parti importanti!










I requisiti essenziali per il salvataggio delle informazioni a lungo termine sono quindi tre:
Un file è un’astrazione logica e indipendente creata dal SO, per nascondere l’interfaccia complessa del disco.
È l'approccio di gran lunga più comune oggi, adottato da sistemi operativi come UNIX, Linux e Windows.
● Il concetto: Il sistema operativo vede il file semplicemente come un contenitore di byte in fila indiana, senza alcun significato intrinseco. Non sa, e non gli interessa, se quel file contiene testo, un'immagine JPEG o codice eseguibile. ● Ruolo del Sistema Operativo: Si limita a tenere traccia delle dimensioni del file (quanti byte contiene) e di dove questi byte sono memorizzati fisicamente. ● Chi dà un senso ai dati? Esclusivamente l'applicazione dell'utente. Quando apri un file di testo, è l'editor (es. Blocco Note o Vim) che interpreta il byte 0x0A come "vai a capo". ● Vantaggio principale: Flessibilità assoluta. Il SO non deve essere modificato o aggiornato per supportare nuovi formati di file; ci pensano i programmi a livello utente (user space).
Questo approccio ha radici storiche profonde, risalenti all'epoca delle schede perforate, ma è fondamentale per capire l'evoluzione dei file system.
● Il concetto: Il file è concepito come una sequenza di elementi a lunghezza fissa chiamati record. ● Ruolo del Sistema Operativo: Il SO sa cos'è un record. Le operazioni di lettura e scrittura non avvengono specificando "leggi 100 byte", ma "leggi il prossimo record". Il sistema operativo forza questa struttura e impedisce, ad esempio, di sovrascrivere metà di un record o di inserirne uno di lunghezza anomala. ● Contesto d'uso: Era tipico dei vecchi sistemi mainframe. L'idea derivava dalle schede perforate a 80 colonne: ogni riga di codice o di dati era esattamente di 80 caratteri (un record).
● Svantaggio: Estrema rigidità. Se devi salvare una stringa di 10 caratteri in un record da 80, gli altri 70 vanno sprecati (padding).
Questo è il livello di astrazione più alto per un file system puro, tipico dei grandi mainframe commerciali usati per l'elaborazione dati (es. sistemi z/OS di IBM).
● Il concetto: I record non sono più in sequenza lineare e possono avere lunghezze variabili. Ognuno è identificato da una chiave (es. il numero di matricola di uno studente) in un campo specifico del record. I record sono organizzati ad albero (spesso un B-tree ) ordinato in base a questa chiave. ● Ruolo del Sistema Operativo: Il file system diventa molto più sofisticato. Invece di chiedere "leggi il record numero 4", l'applicazione chiede al SO: "Cerca e restituiscimi il record con la chiave 'Matricola 12345'". Il SO si occupa della ricerca rapida all'interno dell'albero. ● Oggi dove si trova? Nei sistemi moderni generici, questa logica è stata rimossa dal file system ed è stata "spostata in alto", diventando il compito fondamentale dei Database Management System (DBMS) (come MySQL, PostgreSQL). Oggi usiamo database su SO basati su file a "sequenza di byte" per ottenere le stesse funzionalità di ricerca rapida.
I file normali o regolari sono quelli contenenti informazioni utente sono generalmente ASCII o binari.
Le directory sono file di sistema usati per mantenere la struttura del file system.
I file speciali a caratteri sono relativi all’I/O e usati per modellare i dispositivi seriali di I/O, come terminali, stampanti e network.
I file speciali a blocchi sono usati per modellare i dischi
Magic number → sequenza di byte che identifica il formato del file
Entry Point → L'indirizzo logico esatto della primissima istruzione da far eseguire alla CPU
Sizes → indica le dimensioni del vari campi
Text → codice macchina del programma
Data → Contiene tutte le variabili globali e statiche che sono state esplicitamente inizializzate dal programmatore nel codice sorgente con un valore diverso da zero
SO. Per uniformità ogni partizione inizia con un blocco di boot, anche se non contiene un SO avviabile
Schema della partizione:
Blocco di boot
superblocco gestione spazio libero
i - nodes directory principale (root)
files e directory
Superblocco contiene tutti i parametri chiave riguardanti il fs ed è letto in memoria all’avvio del computer o quando il fs viene usato per la prima volta. Le informazioni tipiche del superblocco includono un numero magico che identifica il tipo di fs, il numero di blocchi nel fs e altre informazioni chiave. Di seguito potrebbero venire le info sui blocchi liberi nel fs, per esempio sotto forma di bitmap o di una lista di puntatori. Queste potrebbero essere eseguite dagli i-node, un array di strutture dati, una per file, che raccontano tutto riguardante i file. Dopo di che potrebbe esserci la directory principale che contiene la cima dell’albero del fs. Infine, il resto della partizione contiene tutte le altre directory e i file.
La tabella delle partizioni alloca 32 bit per indicare il settore di inizio e la lunghezza della partizione, di conseguenza 2^32 * 512 byte / settore = 2 TB di capacità massima. Con un disco di capacità maggiore di 2 TB, se viene inizializzato in mbr, lo spazio restante oltre i 2 TB viene buttato perché il SO non ha i bit necessari per indirizzare quello spazio.
Lo standard moderno prevede UEFI (evoluzione del BIOS) e GPT, gpt utilizza 64 bit per indirizzare i settore di conseguenza la capacità massima è circa 9.4 Zettabyte. Le partizioni diventano almeno 128 e non più solo 4.
I campi sono:
MBR protettivo: situato nel settore zero, Funge da "scudo". Se inserisci il disco in un vecchio computer o usi un software obsoleto che cerca un MBR, il programma leggerà questo settore e crederà che l'intero disco sia occupato da un'unica partizione sconosciuta. In questo modo si rifiuterà di toccarlo e non sovrascriverà i tuoi dati per sbaglio.
MBR protettivo
Intestazione primaria
Array delle partizioni
Partizioni
copia array partizioni
copia intestazione primaria
le due copie servono per il backup
Allocazione contigua
Lo schema di allocazione più semplice è quello di memorizzare ciascun file come una sequenza contigua di blocchi del disco. Ogni file parte dall'inizio di un nuovo blocco, tutti i settori allocati di un file sono sullo stesso cilindro e produce un minor numero di seek.
Pro : semplice da implementare poiché la entry della directory deve memorizzare solo due numeri: l’indirizzo del disco del primo blocco e il numero di blocchi del file. Poiché l’intero file può essere letto da disco con una singola operazione le prestazioni in lettura sono eccellenti. Serve una solo ricerca ( del primo blocco ). Dopo di che non servono altre ricerche o ritardi di rotazione del disco, per cui i dati vengono letti alla massima velocità del disco.
Contro: frammentazione esterna: quando viene eliminato un file vengono naturalmente liberati i suoi blocchi, ciò significa che il disco ora è costituito da blocchi occupati e buchi (blocchi liberi) intervallati. Il problema è che magari un nuovo file è troppo grande per riempire i buchi lasciati, finchè c’è spazio, i blocchi vengono allocati sempre in fondo alla coda, quando esaurisco lo spazio su disco devo trovare un modo per riutilizzare i blocchi liberi dei buchi. Una soluzione sarebbe ricompattare il disco per eliminare questi buchi, ma è un'operazione molto costosa perché devo copiare sui nuovi blocchi tutti i dati che sposto. Un’altra soluzione potrebbe essere tenere una lista dei buchi disponibili con la loro dimensione, chiedere all’utente le dimensioni del file da creare e trovare un buco adatto, ma se poi il file cresce di dimensione si rompe tutto.
Allocazione a liste concatenate
Il file viene considerato come una linked list di blocchi del disco. La prima parte di un blocco contiene un puntatore a quello successivo, il resto sono dati. La entry della directory deve memorizzare solo l’indirizzo del primo blocco, gli altri si trovano seguendo i puntatori.
Pro : si possono utilizzare tutti i blocchi del disco quindi no frammentazione esteerna, la dimensione di un file non è limitata.
Il limite matematico del file invece è dato → blocchi massimi indirizzabili * dim blocco disco
Dove vivono gli attributi? Due strutture possibili
nome attributi indirizzo disco
In questo semplice modello una directory è composta da una lista di voci a dimensione fissa, una per file, contenente un nome di file ( a lunghezza fissa ), una struttura degli attributi del file e uno o più indirizzi del disco ( sino a un certo massimo ) che indicano dove sono i blocchi del disco
nome indirizzo i-node
memorizzare gli attributi negli i-node e nelle directory entry posizionare il puntatore a quell’I-NODE.
I moderni SO consentono nomi di file fino a 255 caratteri → riservare 255 byte fissi solo per la entry della directory spreca molto spazio, soprattutto perché spesso i nomi sono corti
La prima soluzione è la struttura in-line, ogni voce della directory inizia con un’intestazione a lunghezza fissa contenente la lunghezza della voce e alcuni attributi, seguita dal nome a lunghezza variabile, il nome del file termina con un carattere speciale. Questo metodo causa però frammentazione esterna, proprio come l’allocazione contigua. La differenza rispetto ai
file, è che le voci delle directory non sono molte tendenzialmente quindi si potrebbe ricompattarle.
Un’altro modo per gestire i nomi di lunghezza variabile è di fare tutte le voci di directory a lunghezza fissa e tenere i nomi dei file in un heap alla fine della directory. Ogni voce ha un puntatore per accedere al nome del file. Questo metodo ha il vantaggio che quando è rimossa una voce, il successivo file inserito ci starà sempre. Si verifica però frammentazione interna quando elimino un file, nella heap si crea un buco in cui magari non sta il nome di un nuovo file, anche qua si possono compattare i nomi perché tendenzialmente non sono molti però si devono poi aggiornare i puntatori.
Come organizzare le directory per velocizzare la ricerca di file?
Ricerca lineare: le voci della directory sono organizzati in una lista, ls ricerca del nome del file avviene dall’inizio della lista alla fine. Complessità: O(n). Facile da implementare ma inefficiente
Ricerca tramite hash table: ogni directory contiene una hash table contenente i nomi dei file e i corrispondenti puntatori alle voci della directory. Complessità: O(1). Difficili da implementare ma veloce
Hard link: i blocchi del disco non sono elencati nelle directory, ma in un i-node. Le voci delle directory punterebbero poi all’i-node. Con un contatore (che indica quante volte compare nel file system quel file) si tiene conto di tutti i puntatori allo stesso file per esempio per le delete di un file. Quindi, mi potrebbe servire per gestire opportunamente le cancellazioni. Infatti, se il contatore è a 0 allora posso cancellare l’I-NODE.
L'Utente B decide di creare un hard link al file dell'Utente A.
Il Sistema Operativo crea una nuova voce nella directory dell'Utente B che punta allo stesso i-node e incrementa il Link Count a 2.
Qui sorge la prima anomalia: poiché l'i-node (e quindi l'UID) rimane intestato all'Utente A, il sistema non addebita nulla all'Utente B. L'Utente B sta usufruendo di 1 GB di spazio in modo totalmente gratuito. L'Utente A ha la quota disco piena e decide di fare pulizia. Elimina il file originale tramite il comando di rimozione (che a livello di sistema si chiama unlink).
Ecco cosa fa fisicamente il Sistema Operativo:
● Rimuove il nome del file dalla directory dell'Utente A. ● Decrementa il Link Count dell'i-node da 2 a 1. ● Poiché il Link Count è > 0, il file system non libera i blocchi fisici.
Inizializzazione con vfs : il file system si registra, fornisce al vfs una tabella che contiene gli indirizzi delle proprie funzioni. Viene eseguito il mount per collegare il nuovo fs all’albero.
l'operazione di mount (montaggio) è il meccanismo attraverso il quale il Sistema Operativo rende accessibile un file system (residente su un volume logico o dispositivo fisico) integrandolo nel namespace gerarchico globale del sistema.
Avviene associando la directory radice del nuovo file system a una directory preesistente dell'albero principale, chiamata Mount Point. A livello di kernel, questo si traduce nella creazione di una struttura dati all'interno della Tabella di Mount globale gestita dal VFS.
Successivamente, il sistema operativo verifica che il dispositivo contenga un file system valido. Lo fa chiedendo al driver del dispositivo di leggere la directory del dispositivo e verificare che la directory abbia il formato previsto. Infine, il sistema operativo rileva nella sua struttura di directory che un file system è montato sul punto di mount specificato. Questo schema consente al sistema operativo di attraversare la sua struttura di directory, passare tra i file system e persino i file system di tipi diversi.
Tipicamente il punto di mount è una directory vuota, ma cosa succede se invece contenesse qualche file? I file preesistenti diventano logicamente inaccessibili perché il VFS reindirizza tutto il traffico prima di poterli raggiungere. Torneranno immediatamente accessibili (e visibili)
non appena verrà invocata la system call umount(), che distruggerà la struttura vfsmount disattivando il reindirizzamento.
Per tenere traccia dei blocchi liberi e occupati si utilizza una struttura dati, due strade: lista linkata e BitMap.
Lista linkata
ogni nodo della lista è un blocco del disco che contiene i puntatori degli altri blocchi liberi e in fondo un puntatore al nodo successivo.
● Overhead spaziale nullo: È il suo più grande pregio architetturale. Non consuma spazio dedicato sul disco per mantenere le sue strutture dati (a differenza della Bitmap che richiede un vettore fisso). I puntatori vengono scritti all'interno dei blocchi vuoti stessi. Di conseguenza, se il disco si riempie al 100%, la lista cessa di esistere senza sprecare un singolo byte. ● Allocazione fulminea per singoli blocchi: Se il sistema operativo deve allocare un file minuscolo che occupa 1 solo blocco, l'operazione è immediata. Il kernel prende
l'indirizzo del primo blocco libero direttamente dal Superblocco in RAM, usa quel blocco, e aggiorna il Superblocco con il puntatore al nodo successivo.
● Collo di bottiglia dell'I/O (Lentezza disastrosa): È il difetto mortale che ne impedisce l'uso pratico nei sistemi moderni. Se il sistema deve allocare un file da 100 blocchi, deve eseguire 100 operazioni di lettura fisica sul disco per seguire la catena dei puntatori. Poiché le letture su disco sono le operazioni più lente dell'intero sistema informatico, le prestazioni crollano verticalmente. ● Impossibilità pratica di garantire la contiguità: La lista non offre alcuna mappa globale dello spazio. Se un file richiede 5 blocchi fisicamente adiacenti (fondamentale per la velocità di lettura sequenziale), il sistema operativo è costretto a percorrere ciecamente la catena, sperando di incappare in blocchi vicini. Nella pratica, i file si frammentano pesantemente.
BitMap
Il sistema operativo crea un lungo array (vettore) in cui ogni singolo bit rappresenta un blocco fisico sul disco.
● Se il bit è 0 , il blocco corrispondente è libero. ● Se il bit è 1 , il blocco corrispondente è occupato. O viceversa.
Vantaggi (PRO):
Svantaggi (CONTRO):
Dump fisico : comincia al blocco 0 del disco, scrive in ordine tutti i blocchi del disco sul nastro in output e termina quando ha copiato l’ultimo elemento quindi faccio una copia fisica incluse le strutture del file system. Non copio i singoli file ma tutto in massa. Vantaggi: veloce, senza il dump fisico dovremmo parsificare il file system e se il file è frammentato devo fare tanti seek. C’è una semplicità maggiore nella fase di creazione del backup ma vi è una complessità maggiore nella fase di uso del backup per ripristinare Problemi: Se faccio il dump di tutti i cluster, non ha senso inserire i cluster non allocati. Essi non appartengono a nessun file system ma non posso saltarli perchè dovrei leggerli e perdere il vantaggio della velocità.
Dump logico: alla fine è un algoritmo di visita dell’albero del file system, partendo dalla radice fino alle foglie. Gli si associa un parametro.
Dipende dal parametro passato al comando. L'architettura del dump logico si basa sull'attraversamento dell'albero delle directory e sulla lettura degli i-node. Questo gli conferisce la flessibilità di eseguire sia un Dump Completo (copiando tutti i file attivati trovati nell'albero), sia un Dump Incrementale/Differenziale (confrontando la data di ultima
modifica, mtime, presente nell'i-node con la data dell'ultimo backup eseguito).
Per efficienza il file system tiene i blocchi modificati in cache, e li scrive in un secondo momento su disco, per minimizzare i movimenti del braccio. Se capita un crash durante questo periodo di tempo, i dati vengono corrotti, tutti i puntatori sono sballati e si ha la rottura dell’intero file system. FSCK controlla sia la consistenza dei blocchi che quella dei file. Per verificare quella dei blocchi usa due tabelle (array di contatori, un contatore per ogni blocco):
Il file system si dice consistente a livello di blocchi quando ogni blocco ha il contatore corrispondente in una tabella a 1 e quello nell’altra tabella a 0. O viceversa, insomma se il blocco è segnato in uso nella tabella d’uso (quindi vale 1), nella tabella dei liberi deve essere segnato come 0. Se vale 1 nella tabella dei liberi, deve valere zero nell’altra.
L’inconsistenza si verifica quando: un blocco ha entrambi i contatori settati allo stesso valore, oppure in una tabella il contatore è > di 1.
Missing blocks (blocco mancante) : il blocco ha entrambi contatori che valgono zero, non c’è alcun danno ma si ha spreco di spazio. Fsck allora aggiunge il blocco alla struttura dati dei blocchi liberi, modificando il contatore da zero a uno nella tabella dei liberi.
Blocco usato e libero: entrambi i contatori valgono uno. Fsck rimuove il blocco dalla lista dei blocchi liberi (lo rende in uso), oppure dall’i-node (lo rende libero) e aggiorna la tabella corrispondente.
Blocco duplicato libero: il contatore nella tabella dei liberi è > 1, fsck ricostruisce la lista
Blocco dupplicato usato : il contatore nella tabella degli usati è > 1, è il caso peggiore, lo stesso blocco è allocato per più file. Fsck allocato tanti blocchi liberi quanti sono i duplicati; copia il contenuto del blocco duplicato in questi blocchi liberi; di seguito per ogni file a cui il blocco originale è associato, alloca questi nuovi blocchi. In questo modo il contatore del blocco originale torna a 1, il file system è integro.
Per la consistenza dei file, fsck utilizza una sola tabella (array di contatori, un contatore per ogni i-node) per tenere traccia delle corrispondenze di ogni file nell’albero delle directory. Fsck parte dalla radice e percorre tutto l’albero, per ogni directory, per ogni i-node dentro la directory, fsck incrementa il contatore corrispondente nella tabella. In questo caso il contatore può essere > 1 a causa degli hard link.
Si ha consistenza quando il contatore nella tabella è uguale al contatore dei link dell’i-node corrispondente. Se i due contatori non combaciano, si possono avere due errori:
contatore link > contatore tabella , può capitare che quando si eliminano tutte le voci di una directory riferite a un i-node, il link counter sia ancora > 0 e quindi l’i-node non viene eliminato sprecando spazio (dovuto ad un errore durante la sottrazione del link counter da parte dell’hard link). Fsck aggiorna il valore del link counter al valore corretto presente nella tabella.
contatore link < contatore tabella, può capitare che ci sia un guasto durante l’incremento del link counter con la creazione di un nuovo hard link e questo non venga incrementato. Questo fa sì che eliminando una voce della directory, il link counter dell’i-node diventa 0 quando in realtà dovrebbe essere 1 se i conti sono stati fatti bene, link counter = 0 vuol dire che elimino l’i-node rilasciando i blocchi. Il problema è che ho ancora una voce della directory che punta a quell’i-node, che ora è considerato libero e potrebbe essere usato per un nuovo file → si rompe tutto. Fsck aggiorna il valore del link counter al valore corretto presente nella tabella.
L’accesso alle memorie di massa è molto lento rispetto a quello in Ram, è il collo di bottiglia dell’intero SO, di conseguenza l’obiettivo diventa ridurre gli accessi ai dischi.
Caching : si utilizza una buffer cache che contiene i blocchi del disco usati più frequentemente. Per ogni richiesta di read/write, si controlla se il blocco è in cache, se è