La versione attuale è la 0.07 (faccio un salto alla 0.10 direttamente se mi funziona il live) e
spero di aver finito i controlli di init per initramfs.
Ho praticamente dovuto ristudiarmi daccapo il sistema di boot di un sistema GNU/Linux a partire dal kernel (anzi: a partire da GrUB) il che, combinato con il lavoro e le altre cose, mi ha portato via un sacco di tempo e la prima beta è slittata paurosamente.
Adesso scrivo un po' di cose solo per fare lo sborone (
) e per ripassare insieme a voi la teoria del boot di GNU/Linux. Tralascerò gli eventuali messaggi d'errore.
In sostanza abbiamo:
(saltando la parte del BIOS)
--GrUB (MBR) (stage 1)--
In MBR ci sono scritti degli offset che fanno riferimento a delle posizioni del disco rigido di sistema, che GrUB deve andarsi a leggere.
Nei primi 30k del disco immediatamente successivi all'MBR sta lo stage 1.5 di GrUB, che carica lo stage 2. Ed ecco che salta all'occhio l'interfaccia di GrUB. Quella non sta nell'MBR: sta sul disco di sistema.
Le opzioni di GrUB sono modificabili, oppure è possibile accedere alla shell di GrUB con un primitivo command line editing.
Una volta selezionato il sistema operativo GrUB confronta la selezione operata e va a vedersi la tabella degli offset delle partizioni per sapere dove andare a pescare il kernel da caricare. Dopodichè GrUB carica il kernel selezionato in memoria e successivamente passa il controllo ad esso.
--linux--
Quando Linux è caricato "si accorge" di essere caricato (perchè è GrUB che gli dice 'Ehi, sei tu che comandi adesso') e si sposta in un'altra area di memoria (non ricordo quale ma ricordo che è per un motivo preciso... che non ricordo qual è
). Poi comincia il lavoro del kernel.
Inizializza l'hardware, il processore, i bus, le porte (USB, COM, IEEE vari, PS/2 eccetera) e le periferiche (scheda video, scheda audio, monitor, controllori vari sulla scheda madre, e tutto quello che è supportato da Linux internamente. I moduli
sono una cosa a parte ed è a questo che serve initramfs.
Ad un certo punto, quando ha finito di inizializzare il sistema, il kernel si chiede 'Dov'è init? Dov'è il processo 1, padre di tutti i processi?'
Nota: init può non essere un eseguibile, ma piuttosto uno script bash avviabile (quindi con flag +x per amministratore).
Ed ecco che cominciano le magagne. Ecco dove finisce il lavoro del kernel e comincia quello dell'amministratore del sistema.
--initramfs--
Nei sistemi con initramfs, al kernel è
sempre accoppiato un file compresso (solitamente) che viene caricato in memoria: il kernel alloca dello spazio in memoria come tmpfs e ci carica l'archivio compresso
initramfs. Il bello è che all'interno il kernel deve trovare tutto quello che gli serve per trovare la vera partizione di root, ovvero:
1-i moduli che, caricati, diano l'accesso in lettura a periferiche supportate dai moduli stessi;
2-eventuali moduli che danno l'accesso in lettura a determinati filesystem;
3-ulteriori moduli per attivare periferiche non direttamente supportate dal kernel;
4-un file eseguibile (script o binario) che generi processi, o che faccia qualcosa che porta avanti il processo di boot. Solitamente questo ruolo spetta ad init.
Poi l'eseguibile di boot deve:
0-creare i mountpoint per il montaggio dei filesystem (e qui c'è un altro discorso di udev, periferiche, ricerche e quant'altro)
1-montare il (o i) filesystem root reali;
2-liberare spazio in memoria (eventualmente, ma normalmente viene fatto);
3-spostare l'ambiente operativo dall'initramfs alla root reale;
4-cambiare i riferimenti (se necessario) delle librerie e/o dell'ambiente dalla initramfs alla vera root;
5-passare il controllo alla vera root;
6-passare il controllo al vero init, quello che sta nella vera root, con gli argomenti dell'init in initramfs, se ce n'erano.
A quel punto initramfs ha terminato il suo compito. Ma in mezzo c'è tutta una serie di verifiche e confronti. E per un liveCD è ancora peggio, perchè se hai un lettore ottico esterno devi fargli caricare moduli per la lettura di periferiche USB (come sd la stragrande maggioranza delle volte, ma anche sg o sr nel caso di masterizzatori). Poi devi dire al kernel di andare a cercare l'immagine del filesystem da caricare, e se non c'è, o se il blocco device non esiste, o se il blocco device non è un filesystem iso9660, o se il blocco device è un iso9660 ma non ha il filesystem compresso bisogna dire "aspetta, aspetta, come non detto", smontare il blocco (se è montato) e fargli rimontare il prossimo.
Un lavoro 'grosso' di riconoscimento, che è ingigantito dal fatto di voler far riconoscere al sistema un liveCD piuttosto che un'installazione su disco rigido.
Allora ho pensato ad una cosa furba. Ho messo tutti i moduli script all'interno di uno solo (init) e ho previsto la presenza di un flag kernel chiamato "live". Se è a 0 non è una live e procede il boot da disco. Se il flag è 1 è una live e deve fare tutta la serie di verifiche a cui ho accennato prima. Prendono due strade diverse.
Alla fine, qualsiasi sia stato il caso preso in considerazione, c'è una root montata ed un initramfs che viene messo da parte. C'è una rilocazione delle librerie e lo spostamento dell'ambiente operativo.
Per quanto riguarda il liveCD ho previsto due casi.
Caso 1: il computer ha più di 512 MB di memoria RAM.
"Semplicemente" vengono creati dei mountpoint tmpfs in RAM dove viene ricopiata l'immagine del filesystem in squashfs e, tramite unionfs, si viene a creare un ambiente di sola lettura che fa riferimento alla squash (bin, sbin, usr, lib, eccetera) e un ambiente in lettura e scrittura che riporta tutte le altre cartelle (tra cui home, var, tmp e altre).
Caso 2: il computer ha meno di 512 MB di memoria RAM.
In questo caso è appena un po' più semplice. Non viene creato mountpoint per la gestione della squash e il filesystem in sola lettura viene montato direttamente da CDROM. Il procedimento di creazione della root è analogo. Il risultato finale è esattamente equivalente, solo che non c'è il vantaggio della velocità della RAM nella lettura dei file di sistema.
Beh, ora non mi resta che provarla... e incrociare le dita.