Implementazione del File System nel Sistema Operativo Unix (Bach: the Design of the Unix Operating System (cap: 4, da 5.1 a 5.7, 5.12) 1 Argomenti • • • • • • • • Index node (I-NODE) gestione dei file mediante i-node struttura dei file regolari e delle directory accesso ai file mediante directory layout di un file system Unix strutture dati per la gestione dei file system call per la gestione dei file pipes 2 Punto di vista dell’utente: • • • • eseguibili File regolari: testo …….. Directory: Per organizzare il file system file speciali: device fisici (tty, disks, printers...) Pipe: (per la comunicazione fra processi) Punto di vista del kernel: i-nodes + data 3 INDEX-NODE (I-NODE) • i-node: rappresentazione interna di un file unix (blocco indice) • Gli i-node risiedono su disco • vengono copiati in memoria se necessario (in-core i-node) pippo link: pluto file i-node ........ 4 un i-node su disco contiene: • • • • • • • Proprietario del file tipo di file diritti di accesso data di accesso (al file, all’i-node) numero di link al file (link counter) indirizzi dei blocchi che contengono il file dimensioni del file 5 un i-node in memoria contiene: • • • • numero dell’inode su disco file system di appartenenza numero di istanze attive del file (es. compilatore) STATO DELL’I-NODE: – – – – locked? c’é un processo in attesa? l’i-node in memoria é stato modificato? il file in memoria é stato modificato? 6 i-node su disco: 1 2 3 4 ... ... in core i-node list :::: :::: :::: free list (cache di i-node inattivi) Se un file acceduto non é in memoria, il s.o. cerca prima l’i-node nella free list 7 accesso ad un i-node (iget) • Cerca l’i-node in memoria primaria • se non lo trovi, copia l’i-node da disco su un inode della free list (possibilmente libero) • se free-list = empty ERRORE • incrementa il numero di istanze attive del file (reference count + 1) • se i-node = locked WAIT • N.B.: un i-node é locked solo nella system call che lo usa. 8 rilascio un i-node (iput) • Decrementa il numero di istanze attive del file (reference count := reference count -1) • se reference count == 0 – metti l’i-node nella free list – se l’i-node é stato modificato, copialo su disco – 3 casi: • il file é cambiato • l’access-time é cambiato • il proprietario / i permessi sono cambiati. • Questa gestione é valida per qualsiasi tipo di file 9 Struttura dei file regolari • Ogni blocco del disco é identificato da un numero • Ogni i-node contiene un elenco dei blocchi che memorizzano il file • Unix adotta l’allocazione gerarchica indicizzata dei blocchi del file 10 i puntatori ai blocchi del file nell’i-node data blocks direct 0 1024 B direct 1 ... 1024 B 256 indirizzi ... 1024 B direct 9 1024 B single indirect double indir. 1024 B triple indirect 1024 B 1024 B (1KB x 10)+(1KB x 256)+(1KB x 2562)+(1KB x 2563)=16GB 11 Esempio 0 4096 1 278 2 0 3 2720 ---- 8 data block n. 4096 un puntatore a 0 non fa riferimento ad alcun blocco 0 9 76 10 0 11 0 12 0 data block n. 76 12 Allocazione gerarchica indicizzata • I puntatori con valore 0 non allocano blocchi, e quindi non si spreca spazio • L’accesso é molto veloce per i file piccoli (< 10k) • Si puó aumentare la dimensione dei blocchi per velocizzare l’accesso ai file grandi ma AUMENTA LA FRAMMENTAZIONE INTERNA • Si puó usare un blocco per contenere l’ultimo frammento di piú file (implementazione BSD) • Si puó anche mettere l’i-node nel 1o blocco del file (per file piccolissimi si risparmia molto spazio) 13 Struttura delle directory (semplificata) • DIRECTORY: file organizzato ad array. Ogni entry ha due campi di 2 e 14 byte i-node number file name (216 file) (attualmente il file name puó essere lungo 512 chars) • il kernel alloca spazio per ogni directory come per i file ordinari • Solo il kernel puó scrivere direttamente in una directory, in modo da garantirne la struttura. 14 struttura della directory /etc byte offset in directory i-node number symbolic name of files 0 83 . 16 2 .. 32 1798 init 48 1276 motd 64 0 crash 80 2114 passwd ... ... ... 15 Conversione da path-name a i-node • L’accesso ai file avviene mediante path-names • il kernel lavora con i-node • Ci vuole un ALGORITMO di CONVERSIONE dal path-name all’i-node • Directory di partenza: – root (/) : i-node memorizzato in una var. globale – current directory: i-node memorizzato nella u-area 16 Algoritmo di conversione algoritm namei /* conversione pathname - inode input pathname; output inode; { if (pathname starts from root) working inode = root inode else working inode = current directory inode; while (there is no more pathname) { read next path name; verify that working inode is of directory, and access permission ok; read directory (working inode); if (component matches an entry in directory (working inode)) { get inode number for matched component; release working inode; working inode = inode of matched component } else /* component not in directory */ return (no inode)} return (working inode); } 17 Struttura interna del File System boot block • • • • super block i-node list data blocks.... boot block (blocco 0) super block (blocco 1) i-node list data blocks 18 Super Block • • • • • • • Il super block é una struttura che contiene le informazioni fondamentali su file system: dimensioni del file system numero di blocchi liberi indice del 1o blocco libero della lista dimensioni della lista di i-node numero di i-node liberi indice del 1o i-node libero nella lista di i-node lib. flag per indicare che il super block é stato modif. 19 Altri tipi di file Unix • PIPE: – file di contenuto transitorio – i dati possono essere letti solo nell’ordine in cui sono stati scritti (FIFO) – dimensione massima: 10 blocchi (i 10 blocchi ad indirizzamento diretto dell’i-node) – servono per le comunicazioni veloci tra processi 20 Altri tipi di file Unix • File speciali: – corrispondo ai device fisici – l’i-node non fa riferimento a blocchi di memoria – una subroutine di sistema usa il device nel modo giusto attraverso due valori registrati nell’i-node: • MAJOR NUMBER = tipo di device (tty, disco, ...) • MINOR NUMBER = numero di unitá del device – i file speciali possono essere usati dai programmi come si usano i file regolari 21 Strutture dati per gestire i file aperti: • User file table: – lista dei file aperti per processo (memoriz. nella u-area) – di solito ha 20 entry (max 20 file aperti contemp.) – le entry puntano alla File table • File table: – – – – lista dei file aperti globale sempre in memoria centrale ogni entry contiene l’offset della prossima read/write ogni entry punta ad un in-core i-node • Lista degli in-core i-node 22 System call “OPEN” • fd = open(“file”,MODE) (fd: user file descriptor) • il kernel: – recupera l’i-node di “file” e controlla i permessi – alloca una nuova entry nella FILE TABLE che punterá all’in-core i-node – setta a zero l’offset del puntatore in lettura/scrittura – alloca una nuova entry nella USER FILE TABLE che punta alla entry corrispondente nella FILE TABLE – il file descriptor fd ha come valore l’indice della entry nella USER FILE TABLE 23 File tables del kernel file table user file table 0 1 2 3 4 5 6 7 ... count 1 .... .... count 1 ... ... count 1 ... ... read ..... rd-wrt .... write ... in-core i-node list count /etc/passwd 2 .... ..... .... count myfile 1 ... .... ... ... ... ... ... ... ... • un file aperto in scrittura (fd = 5), ed uno aperto sia in lettura (fd = 3) che in scrittura (fd = 4) 24 Perché la open: • L’utente usa un file system simbolico • il s.o. lavora solo in termini di i-node e blocchi • la open stabilisce un collegamento efficiente tra un file e la sua organizzazione fisica • la “close”distrugge il collegamento 25 User file descriptor 0, 1, 2 • • • • 0 = Standard Input (STDIN) 1 = Standard Output (STDOUT) 2 = Standard Error (SDTERR) stdin, stdout, stderr sono assunti di default da tutti i processi • la convenzione é utile per la redirezione dell’input/output e per l’uso delle pipe • possono essere gestiti come normali file (cioé chiusi, riassegnati, riaperti, ...) 26 Esempio di redirezione dell’output file table user file table 0 1 2 3 4 5 6 7 ... count 1 count 1 count 1 ... ... count 1 ... ... read write rd-wrt .... write – ls > myfile – – – – – close(stdout) fd=open(“myfile”,w) exec(“ls”) close(fd) fd=open(stdout) ... 27 System call “READ” • • • • n = read (fd, where_to_put_chars, how_many) n = numero di caratteri letti OFFSET = OFFSET + n read é implementata in modo da poter richiedere la lettura di piú blocchi in anticipo per aumentare l’efficienza 28 System call “WRITE” • • • • n = write (fd, where_to_get_chars, how_many) n = numero di caratteri scritti OFFSET = OFFSET + n write puó richiedere l’allocazione di blocchi indiretti • puó non avere effetto immediato sul file su disco a causa del buffer cache Unix 29 System call “LSEEK” • position = lseek (fd, offset, from_where) • from_where: –0 –1 –2 OFFSET ATTUALE = inizio file OFFSET ATTUALE = OFFSET ATT. + offset OFFSET ATTUALE = File size + offset 30 System call “CREATE” • • • • • fd = create(“file”,MODE) (fd: user file descriptor) crea “file” se non esiste, o lo azzera se esiste crea una entry per “file” nella directory specificata crea un nuovo i-node e lo copia su disco “file” é aperto in scrittura 31 system call “CLOSE” • close(3) • close(5) count := count -1 file table user file table 0 1 2 3 4 5 6 7 ... NULL NULL rilasciato .... ..... .... count rd-wrt 1 ... .... ... rilasciato ... ... ... i-node table count /etc/passwd 1 .... ..... .... in-core i-node ritorna in free list ... .... ... ... ... ... ... ... ... 32 System call “LINK” • link(existing_file_name, new_file_name) • Associa ad un file un nuovo nome • il kernel: – recupera l’i-node di “file” e controlla i permessi – link-counter = link-counter + 1 – alloca una nuova entry nella directory di new_file_name con nuovo nome, e l’i-node di existing_file_name – (é cosí che funziona anche il comando Unix ln) – questo tipo di link si chiama hard link 33 System call “UNLINK” • unlink(file_name) • rimuove un link a file_name • il kernel: – – – – recupera l’i-node di “file” e controlla i permessi link-counter = link-counter - 1 rimuove l’entry dalla directory di file_name se link-counter = 0, rimuovi l’i-node (é cosí che funziona anche il comando Unix rm) 34 Symbolic Link • il numero di un i-node é unico solo nell’ambito di un file system • un link fisico (hard link) non puó attraversare i file system • per fare ció si usano i link simbolici – ln -s /usr/local/bin/prolog myprolog – nella entry della directory per myprolog non viene registrato l’-inode del file prolog, ma il suo pathname assoluto. 35 – il link counter non viene modificato File di tipo “PIPE” • Permettono la comunicazione tra processi con un meccanismo di tipo FIFO • UNNAMED PIPE – vengono riferite solo mediante file descriptor – solo processi in relazione padre/figlio possono usare una unnamed pipe – sono automaticamente rimosse alla morte dei processi • NAMED PIPE – esistono nel file system 36 – qualsiasi processo puó usarne una (se ha i permessi) Unnamed PIPE • Risiedono solo in memoria primaria, per cui sono meccanismi di comunicazione velocissimi • Vengono implementate come file normali usando un i-node. • Solo i blocchi indirizzati direttamente vengono usati per lettura/scrittura, e sono gestiti in modo circolare. • ad ogni pipe sono associati un file descriptor in lettura ed uno in scrittura 37 pipe(fds) fds[0] = lettura int fds[2] fds[1] = scrittura write pointer read pointer offset 0 0 offset 10239 1 2 3 4 5 6 7 8 9 blocchi diretti dell’i-node 38 funzionamento della pipe • i dati vengono letti nell’ordine in cui sono stati scritti (no lseek) • i dati possono essere letti una sola volta (vengono “consumati”) • un dato non puó essere sovrascritto prima che sia stato letto (il processo scrittore é messo in wait) • la lettura di una pipe vuota provoca la sospensione del processo lettore 39 Esempio di uso di unnamed pipe char string[] = “hello” main() { char buf[1024] char *cp1, *cp2; int fds[2] cp1 = string; cp2 = buf; while (*cp1) *cp2++ = *cp1++; pipe(fds); for (;;) { write(fds[1], buf, 6); read (fds[0], buf, 6): } } Il processo scrive e legge sulla pipe all’infinito. Che succede se si scambiano i due comandi di scrittura/lettura? 40 named pipes • mknod(file_name, PIPE, 0); • crea il file file_name che viene gestito come una pipe • file_name é un file del file system, quindi ogni processo lo vede e puó usarlo, se ha i permessi. • anche questa pipe é sospensiva 41 esempio di uso di named pipe #include <fcntl.h> char string[] = ‘” hello”; main(argc,argv) int argc; char *argv[]; { int fd; char buf[256]; /* creazione di una named pipe con permessi di lettura/scrittura per tutti gli utenti */ mknod(“fifo”, 010777,0); if (argc == 2 ) fd = open(“fifo”, O_WRONLY); else fd = open(“fifo”, O_RDONLY); for(;;) if (argc == 2) write(fd, string, 6); else read(fd, string, 6); } 42