Inode

Versione del 15 nov 2008 alle 01:32 di Dracul (discussione | contributi) (Nuova pagina: ==INODE== === Nota === Spero di fare cosa gradita nel condividere alcuni appunti del filesystem del kernel 0.0.1 che sto studiando. Tuttavia, non posso garantire che quanto scritto qu...)
(diff) ← Versione meno recente | Versione attuale (diff) | Versione più recente → (diff)

INODE

Nota

Spero di fare cosa gradita nel condividere alcuni appunti del filesystem del kernel 0.0.1 che sto studiando. Tuttavia, non posso garantire che quanto scritto qui sia perfettamente esatto, ma solo deduzioni da quanto ho appreso fino ad oggi. La guida è in fase di creazione e di correzione nel tempo . Grazie per la vostra comprensione e per le correzioni che chiunque potrà fare.

Che cos'è un inode?

Un inode è una struttura che viene utilizzata quando un filesystem viene creato per salvare le informazioni dei file, quali i diritti di accesso, la dimensione, l'uid e il gid ecc... (http://en.wikipedia.org/wiki/Inode) quando questi vengono aperti .

Nel kernel un inode è una struttura (una tabella di inode) che si trova a livello del virtual file system (http://www.makelinux.info/system/poster). Il virtual file system arriverà in Linux dalla versione 0.96b, tuttavia per dimostrare l'uso degli inode, ho riportato in seguito per semplificare (non conosco il codice del VFS) il codice del fs minix usato da Linus in Linux-0.0.1 .

Un esempio pratico

Linux 0.0.1 e minixfs

Bisogna premettere che il filesystem di minix che Linus ha utilizzato per il suo primo os (che ancora non era maturo ma funzionante come kernel, bisognerà aspettare qualche versione successiva) è molto diverso dalla versione 3 dei nostri giorni (Lo stesso Linus cambierà in seguito codice per adattarlo alla versione del fs minix 1.6 ), per questo nel paragrafo che segue alcuni campi che troveremo andando ad anaalizzare il contenuto nel disco non saranno esatti.

Il filesystem minix (ma non solo questo, ext2 è nato in un certo senso da minix fs e quindi non sorprendentemente ha gli stessi concetti di base) è diviso in blocchi da 1k (come vedremo in seguito quando lo creeremo).

Il primo blocco è vuoto (per salvare le informazioni di boot immagino). Il secondo blocco contiene il super block (vedere più avanti). Il terzo contiene la bit map dei blocchi allocati mentre il quarto la bit map degli inode e dal quinto in poi (il campo unsigned short s_firstdatazone del super block contiene questa informazione) iniziano i dati del filesystem che includono i file, le tabelle dei file (cioè il contenuto delle directory).

Seppur abbastanza semplice come kernel rispetto a quello dei giorni nostri è impossibile purtroppo evitare di presentare anche altre strutture o di sconfinare in campi che non appartengono all'oggetto di questa piccola guida poiché altrimenti si perderebbe di vista lo scopo ultimo del codice e cioè come tutto lavora in armonia con gli altri componenti per far si che alla fine determinate cose vengano eseguite in un sistema che è realmente multitasking (vedere le release note di Linus).

Da un punto di vista pratico dando una lettura al codice sorgente di Linux 0.0.1 (www.kernel.org/pub/kernel/historic/) nella directory degli include, l'header che definisce le strutture del fs include/linux/fs.h contiene la struttura dell'inode salvata su disco è questa:

struct d_inode {
        unsigned short i_mode; /* mask bit della modalita' d'accesso */
        unsigned short i_uid; /* user id */
        unsigned long i_size; /* dimensione */
        unsigned long i_time; /* time stamp */
        unsigned char i_gid; /* group id */
        unsigned char i_nlinks; /* hardware link, se questo valore non è zero non potrete rimuovere il file */
        unsigned short i_zone[9]; /* blocchi occupati dai dati */
};

oltre a questa struttura possiamo subito introdurre quella più completa ed utilizata dal kernel (non ho ripetuto le note già dette) :

struct m_inode {
        unsigned short i_mode;
        unsigned short i_uid;
        unsigned long i_size;
        unsigned long i_mtime;
        unsigned char i_gid;
        unsigned char i_nlinks;
        unsigned short i_zone[9];
/* these are in memory also */
        struct task_struct * i_wait; /* la task che ha in carico questo inode*/
        unsigned long i_atime; /* access time */
        unsigned long i_ctime; /* change time ? */
        unsigned short i_dev; /* device su cui punta questo inode */
        unsigned short i_num;
        unsigned short i_count;
        unsigned char i_lock; /* accesso esclusivo, solitamente quando si ha un accesso esclusivo la task viene messa in TASKINTERRUPTABLE mode */
        unsigned char i_dirt; /* i valori contenuti nella struttura sono cambiati */
        unsigned char i_pipe; /* pipe ? */
        unsigned char i_mount;
        unsigned char i_seek;
        unsigned char i_update;
};

come si può vedere ci sono due diverse strutture . La prima è quella che fisicamente viene salvata su disco e identifica le entry che esistono fisicamente nel fs (filesystem), mentre la seconda ha una parte in comune con la prima e inoltre avrà altri campi che si troveranno solamente in memoria organizzati in una tabella di inode. Prima di spiegare meglio alcuni campi che possono essere poco chiari e legati strettamente al funzionamento di minixfs, tutte le operazioni che il nostro filesystem esegue sugli inode si trovano nel file sorgente fs/inode.c . Meglio quindi dargli una lettura almeno parziale .

Il superblock

Il superblock è il blocco che descrive il nostro filesystem. Per sicurezza viene copiato su più blocchi in modo da essere ridondante (il filesystem di cui parliamo è un insieme di blocchi). Contiene informazioni legate al tipo di filesystem.

struct super_block {
        unsigned short s_ninodes; /* qui dentro vengono salvati il numero massimo di inode alla creazione del filesystem. Nei filesystem moderni e' possibile cambiarlo quando si va in run out of inodes. */
        unsigned short s_nzones;
        unsigned short s_imap_blocks;
        unsigned short s_zmap_blocks;
        unsigned short s_firstdatazone; /* E' il primo blocco che contiene i dati */
        unsigned short s_log_zone_size;
        unsigned long s_max_size;
        unsigned short s_magic;
/* These are only in memory */
        struct buffer_head * s_imap[8]; /* buffers che contengono i blocchi degli inode */
        struct buffer_head * s_zmap[8]; /* buffers che contengono i blocchi delle zone */
        unsigned short s_dev;
        struct m_inode * s_isup;
        struct m_inode * s_imount;
        unsigned long s_time;
        unsigned char s_rd_only; /* il filesystem è in read only mode */
        unsigned char s_dirt; /* il super block è stato cambiato */
};

's_inodes'

il numero di inode è fisso ma solitamente nei filesystem è possibile cambiare questo numero nel superblock. Alcuni si saranno accorti che su alcuni filesystem quando gli inode sono esauriti bisogna aumentarne il numero e riservare ulteriore spazio altrimenti non sarà più possibile salvare alcun file anche se c'è dello spazio disponibile.

Per curiosità il campo s_magic definisce il tipo di filesystem . Un altra cosa a cui dobbiamo dare attenzione sono i campi unsigned short s_imap_blocks : questo campo definisce il blocco che contiene la bitmap degli inode e cioè dove si trovano i blocchi che contengono delle strutture di inode .

Creiamo il nostro filesystem minix e spiamolo

Per mettere in pratica i concetti appena esposti creaimo un piccolo filesystem minix (serve mkfs.minix e che si abbia almeno un device di loop). Come detto prima il fs in questione non è esattamente come quello che Linus ha usato, ma la versione 3, per cui alcune cose saranno lievemente diverse .

Gli inode ai giorni nostri

Come acennato prima all'inizio di questa guida, gli inode oggi si trovano a livello di astrazione del VFS (fate riferimento alla mappa del kernel), guardando nel codice di un kernel moderno, ovviamente saranno un po' più complicati e dipendono anche dal tipo di fs che il vostro kernel supporterà in fase di configurazione .