UNIX. Di tutto un file Nel caso di una macchina VMS, questo e` un tipico elenco di devices Dischi locali Dischi remoti Nastri Come si giunge ad un file? disk$xx:[directory]file.txt disco directory L’equivalente in UNIX: • Un file su di un disco locale: /home/user/file.txt • Un file su di un disco remoto /afs/infn.it/system/file.txt • una unita` nastro /dev/st0 file Elementi sintatticamente distinti individuano oggetti diversi La sintassi non e` di alcun aiuto Una visione d’insieme user level kernel level file subsystem process control subsystem filesystem buff. cache char. block device drivers kernel level hardware level Da questo schema ricaviamo due elementi utili: • Due soli blocchi funzionali: la gestione dei files e quella dei processi • I device sono mascherati dietro il filesystem: ne discende che ad ogni device si accede come ad un file. Il principio fondamentale di UNIX: semplificare. Ad ogni cosa si accede tramite: open, close, read, write, ioctl Una lettura consigliabile: Maurice Bach, THE DESIGN OF THE UNIX OPERATING SYSTEM, Prentice Hall International Editions Cos’e` un file? A questo siamo abituati con il VMS: accesso: diretto/sequenziale formato del record: fisso/variabile lunghezza del record Secondo il principio della semplificazione, in UNIX un file e` una sequenza di bytes. Cio` ha conseguenze molto importanti • Il sistema operativo non conosce la differenza tra un record di lunghezza fissa ed uno di lunghezza variabile, ne` sa quanto sia lungo un record. La gestione del filesystem e` piu` semplice. • In questo contesto, la conoscenza di come il file e` organizzato e` demandata all’applicazione. E` lecito chiedersi, a questo punto, cosa siano gli oggetti piu` comuni, come un disco o un terminale alfanumerico Due directory interessanti /dev /proc Notare il flag. In VMS conosciamo i flag r,w,x (file permissions). Qui ne aggiungiamo un altro: la lettera d indica che il file deve essere interpretato come directory. • A seconda del valore di quel flag, il sistema e` in grado di sapere COME un certo file deve essere trattato. • Nel caso di un file di tipo directory, il flag dice al sistema che in quel file trovera` un elenco di altri file, ed i relativi puntatori per potervi accedere. Vediamo piu` in dettaglio /dev e /proc, perche` illustrano bene il principio “di tutto un file”. /dev ovvero: dove sono finiti i dischi??? dischi locali: /dev/sda, ... unita` nastro: /dev/st0, ... terminali: /dev/tty0, ... Notare ancora il flag: • b significa block special file: file da leggere a blocchi attraverso la buffer cache. Questo e`, tipicamente, un disco: un oggetto i cui blocchi sono raggiungibili direttamente, ed il cui contenuto e` utile mantenere - per quanto possibile - in una cache. • c significa character special file: file da leggere sequenzialmente un carattere alla volta, senza bufferizzare i byte. Tradizionalmente si tratta di un terminale (da cui la definizione character): un file da cui si legge in sequenza un carattere (byte) alla volta. Questa definizione si applica anche ad una unita` nastro. /proc ovvero: spiare il sistema Ogni processo e`identificato da un numero (PID); ognuna di queste directory contiene dati statistici sui processi Questi files contengono informazioni e statistiche generali sul sistema Statistica di uso della memoria Informazioni sulla configurazione di interrupt /proc e` visto come directory sulla quale e` montato un filesystem; in realta` e` una finestra aperta sulla memoria di sistema, dalla quale e` possibile ricavare lo stato attuale del kernel ed informazioni statistiche sui device e sui processi. --> Nota: non tutti i sistemi UNIX usano /proc: lo si puo` trovare, ad esempio, su Linux e su Digital Unix, ma non su AIX. Conseguenze del paradigma “di tutto un file”. Ovvero: un digressione istruttiva ... Supponiamo che il disco /dev/sda1 contenga un filesystem. Vedremo meglio in seguito cosa sia; qui ci basta dire che e` un albero di files e directories. 1) /dev/sda1 e` un file = sequenza di byte. Su di esso possiamo usare open-close-read-write. Ammetteremo che il piu` delle volte non sia molto utile. Ricordiamo tuttavia che la creazione di un filesystem avviene accedendo al disco proprio in questo modo. 2) Per accedere al filesystem eseguiamo: mount /dev/sda1 /home (spiegheremo meglio questo comando tra poco) Cosa abbiamo fatto? Semplice: agganciare il filesystem di /dev/sda1 su quello principale (cioe` sul root filesystem) • Il file /home (=directory) non contiene piu` un elenco di files, ma gli viene assegnato un flag di mount point. Transitando su di un mount point, il sistema sapra` di doverlo interpretare come punto di accesso ad un altro filesystem, residente su di un altro disco. • D’ora in poi il contenuto di /dev/sda1 e` agganciato al tronco principale, e sintatticamente indistinguibile da esso. non trascurabile corollario: Siccome anche i dischi remoti sono agganciati con lo stesso meccanismo, si forma un unico albero, come se si trattasse di un unico immenso disco. !! Semplicita`, eleganza !! Cio` che forse non e` evidente a tutta prima e` che, dopo aver eseguito il mount (ed ammesso che si abbiano i privilegi necessari per fare la seguente, piratesca, operazione), siamo in grado di vedere il disco - contemporaneamente - in due modi. /dev/sda1: stream di bytes /home: un ramo del filesystem E` meglio discuterne solo in modo accademico: potemmo infatti usare open-close-read-write per accedere a /dev/sda1, al posto che passare attraverso il filesystem... ma sconsiglio caldamente di farlo se volete ritrovare intatti i vostri file !!! pipes ovvero l’idraulica fatta con i file. Una delle caratteristiche peculiari di UNIX sono le PIPES, altrimenti detti pipe special files. Ne esistono di due tipi • Named pipes: residenti sul filesystem ed accessibili a qualunque processo, ammesso che la protezione del file lo permetta • Unnamed pipes: sono volatili ed accessibili solo al processo creatore ed ai suoi discendenti. Le pipes sono un potente strumento di comunicazione tra processi Un processo scrive da un lato Un processo legge dall’altro Oltre a creare un canale di comunicazione di tipo FIFO tra due processi, la pipe provvede anche alla loro sincronizzazione. • Il processo (o i processi) in lettura sono costretti in stato di wait fino a quando quello (o quelli) in scrittura non cominciano a riempire la pipe. • Viceversa, il processo in scrittura viene bloccato quando quello in lettura non estrae i dati dalla pipe ad una velocita` sufficiente. • Tutto cio` e` controllato dal sistema: l’applicazione esegue solo open-close-read-write. Vedremo piu` avanti molti esempi di uso delle pipe. Cosa succede in pratica quando si usa una pipe? Per la seguente discussione, prepariamo una rappresentazione di un processo e delle sue unita` logiche di I/O unita` 2: standard error 2 unita` 1: standard output 1 0 unita` 0: standard input Ad esempio, il comando cat file fa si` che la shell crei un sottoprocesso in cui viene eseguito cat, e collega il file file all’unita` 0; le unita` 1 e 2 restano agganciate alla shell principale. filesystem shell 2 file 0 1 Vediamo ora cosa succede con il comando: cat file | grep -i string | more La shell crea tre sottoprocessi ed aggancia le unita` di I/O a delle pipe, in modo che i 3 processi possano leggere e scrivere l’uno dall’altro con la corretta sincronia. Durante l’esecuzione, dopo aver usato ctrl-Z, possiamo usare ps per sorvegliare i sottoprocessi della nostra shell: [1]+ Stopped bach2:~$ ps PID TTY STAT 1759 p1 S 1783 p1 T 1784 p1 T 1785 p1 T 1786 p1 R cat file | grep -i string | more TIME COMMAND 0:00 -bash 0:00 cat file 0:00 grep -i string 0:00 more 0:00 ps A cio` corrisponde lo schema seguente: Qui riceviamo il risultato finale shell 2 file filesystem 0 1 cat pipe 2 0 pipe 1 grep 2 0 more 1