Kernel e MINIX
Nella pagina dedicata all'ingresso e all'uscita abbiamo seguito il sistema operativo mentre dialogava con le periferiche, abbiamo visto come ogni dispositivo, dal disco alla tastiera, parli una propria lingua elettrica fatta di registri e di comandi oscuri, e come il driver, il traduttore fidato inserito tra l'hardware e il resto del mondo, converta quella lingua in richieste ordinate che i programmi possano capire, con le interruzioni a fare da campanelli che la periferica suona quando ha portato a termine un lavoro. Tutto questo accadeva nella sala del ristorante, fra i tavoli, dove gli ospiti vedono i camerieri andare e venire, eppure ogni ristorante nasconde una porta socchiusa dietro cui il lavoro diventa più delicato, perché lì si maneggiano fuoco e coltelli, e in questa pagina attraversiamo proprio quella porta, entrando nella cucina del sistema operativo per conoscere il kernel, il cuore che batte in modo privilegiato, e per raccontare la storia di MINIX, il piccolo sistema nato per insegnare che per puro caso fece nascere Linux.
Il motivo per cui la visita merita una pagina a parte sta in una domanda che la pagina sui driver lasciava apposta in sospeso, e cioè chi controlla il controllore, visto che un driver incontra hardware delicato e memoria altrui e potrebbe, con un errore, travolgere l'intera casa; la risposta è che esiste una zona del sistema dove questo potere viene concesso con cautela e dove ogni gesto viene sorvegliato, mentre tutto il resto del software lavora con le mani legate per sua stessa protezione. Capire dove passi questo confine, e perché è stato disegnato così, è il passo che trasforma chi usa il computer in chi lo comprende, quindi immaginiamo il sistema operativo come un ristorante e cominciamo la visita dalla porta più stretta.
Il kernel, parola che in tedesco vuol dire nocciolo ed è parente stretta del nostro gheriglio, è la parte del sistema operativo che gira sempre in kernel mode, il modo privilegiato del processore in cui ogni istruzione è permessa, compresa quella di parlare direttamente con le porte di ingresso e uscita e quella di decidere chi userà la memoria, mentre i programmi comuni, quelli che tu lanci ogni giorno, girano in user mode, il modo utente in cui certe istruzioni semplicemente rifiutano di obbedire e sollevano una trappola. Nel paragone del ristorante, il kernel è la cucina, la stanza dove solo i cuochi possono entrare, perché lì ci sono i fornelli accesi e i coltelli affilati, e dove ogni ordine arriva dal salotto attraverso uno sportello, mentre i clienti stanno seduti al tavolo lontano da ogni fiamma viva, e la legge della casa, scritta nel silicio del processore, dice che chi ha il grembiule può tutto e chi ha la cravatta non può toccare il gas.
Quando un programma ha bisogno di un servizio del kernel, per esempio leggere un file o mandare qualcosa in rete, non entra in cucina di sua iniziativa, perché il confine fra i due modi è sorvegliato da un campanello speciale chiamato trap, trappola, detto anche chiamata di supervisore, che il programma suona depositando i parametri in registri o sulla pila e poi eseguendo quell'istruzione; il processore, al suono del campanello, cambia il grembiule al programma, passa in kernel mode e salta a un indirizzo fissato dove il kernel esamina la richiesta, svolge il lavoro vero e proprio e infine restituisce il controllo esattamente all'istruzione successiva, così che chi suona non si accorge quasi di aver varcato il confine. Le chiamate che attraversano questo sportello sono le system call, le chiamate di sistema, cioè il vocabolario ufficiale fra i programmi e il cuore, e tutto quanto il sistema operativo sa offrire, dai file alle finestre, arriva ai programmi soltanto passando per lì.
Veniamo ora alla domanda architettonica che per decenni ha diviso i progettisti, e cioè quanta roba debba stare dentro la cucina, perché esiste una scuola di pensiero che dice tutto, e il suo capostipite è il kernel monolitico, in cui l'intero sistema operativo vive nel cuore come un unico grande programma nel quale ogni procedura può chiamare qualunque altra quando le serve, senza zone vietate, con tutte le parti legate insieme in un singolo file eseguibile, e Tanenbaum, con la sua ironia garbata, la sottotitola il gran pasticcio, perché nascondere informazioni dentro una struttura simile è quasi impossibile. Nel ristorante monolitico gli stessi cuochi cucinano, portano i piatti, lavano le stoviglie e fanno la cassa, e quando il locale è piccolo e la brigata affiatata questo è rapidissimo, il che spiega perché la tradizione di UNIX, di Linux e di molti sistemi che usiamo davvero abbia scelto questa via, accettando il rischio che un cuoco stanco rovini la sera di tutti.
L'altra scuola risponde con il microkernel, il cuore ridotto all'osso, dove dentro la cucina resta soltanto il minimo indispensabile, cioè la gestione dei processi, il passaggio dei messaggi e l'accesso di basso livello all'hardware, mentre i servizi che nel monolite abitavano nel cuore, il file system, il gestore della memoria, i driver stessi, vengono spostati fuori e fatti girare come processi comuni in spazio utente, con le mani legate come quelli di ogni altro programma. Il ristorante così organizzato ha camerieri separati che vivono in sala, ognuno col suo compito, il maitre che solo parla con la cucina attraverso lo sportello, e se il cameriere dei dolli si rompe un piede il servizio continua, perché ne basta chiamare un altro, il che regala robustezza a scapito di qualche passaggio in più fra sala e cucina. La lezione didattica di questo paragone è che il confine fra cucina e sala è una scelta di progetto, e guardare dove un sistema l'ha disegnato ti dice immediatamente a quale scuola appartiene.
Per capire perché questa storia ci riguarda da vicino dobbiamo fare un salto negli anni ottanta, quando UNIX era giovane e il suo codice sorgente circolava libero nelle università sotto licenza AT&T, tanto che John Lions dell'università australiana del New South Wales ne pubblicò un libriccino che lo spiegava riga per riga e che i corsi usavano volentieri, finché AT&T, con la versione sette, capì di custodire un prodotto commerciale prezioso e cambiò licenza proibendo lo studio del codice nei corsi, cosicché molte università, spaventate, smisero di insegnare un sistema vero e si rifugiarono nella sola teoria, lasciando gli studenti con una visione storta del mestiere. Per riparare a questa mancanza Tanenbaum decise di scrivere da zero un sistema compatibile con UNIX agli occhi di chi lo usa e tuttavia completamente diverso dentro, con la cura maniacale di non impiegare una sola riga di codice AT&T, così da eludere ogni restrizione legale, e nel 1987 pubblicò MINIX, nome che sta per mini-UNIX perché era abbastanza piccolo da essere capito da chiunque, accompagnato dal codice sorgente completo che chiunque poteva studiare o modificare, esattamente come gli studenti di biologia sezionano le rane per capirne il funzionamento.
Il punto di partenza di MINIX è una scelta di stile che vale la pena enunciare a voce alta, e cioè che UNIX era stato progettato per essere efficiente mentre MINIX era stato progettato per essere leggibile, tant'è che il suo codice pullula di migliaia di commenti e che chi lo scrisse ammette candidamente che leggere qualcosa di lungo centinaia di pagine resta un esercizio faticoso; la modularità arrivò proprio da questa volontà didattica, perché fin dalla prima versione il file system e il gestore della memoria giravano come programmi utente anziché dentro il cuore, una decisione che dieci anni dopo sarebbe sembrata lungimirante. Il sistema fu scritto in linguaggio C, il cui rudere di pronuncia è il gradino successivo del percorso, fu pensato per viaggiare facilmente da un computer all'altro e, nella filosofia del piccolo è bello, all'inizio girava perfino senza disco fisso, un lusso costoso in quegli anni, e il testo che lo spiegava si trasformò nel libro Operating Systems Design and Implementation di Andrew S. Tanenbaum, che la terza edizione vede affiancato da Albert S. Woodhull e che usiamo come bussola in tutta la pagina.
Il progetto non restò fermo, perché MINIX 2 uscì nel 1997 insieme alla seconda edizione del libro, e nel 2004 Tanenbaum, convinto che il software stesse diventando troppo gonfio e inaffidabile, riprese in mano il filo ormai sopito insieme ai suoi studenti della Vrije Universiteit di Amsterdam e produsse MINIX 3, una riorganizzazione profonda che ridusse il kernel a una dimensione che il libro descrive come ben al di sotto delle quattromila righe di codice eseguibile, contro i milioni di righe di Windows, Linux e FreeBSD, e questa piccolezza è un punto tecnico serio, visto che gli studi citati nel libro contano dai sei ai sedici difetti ogni mille righe e che gli errori si concentrano soprattutto nei driver di dispositivo, il che spiega la mossa distintiva di MINIX 3 di cacciare anche i driver dal cuore e farli vivere in spazio utente, dove possono fare danni limitati e possono persino essere rimpiazzati al volo mentre il sistema è in piedi.
La storia però aveva in serbo una svolta che nessuno aveva previsto, perché poco dopo la pubblicazione di MINIX nacque su Usenet il gruppo di discussione dedicato, che raggiunse quarantamila iscritti in poche settimane, una folla che ogni giorno proponeva funzioni nuove e pezzi di codice per gonfiare il sistema, e che Tanenbaum respinse con pazienza ostinata per tenerlo abbastanza piccolo da star dentro i computer che gli studenti potevano permettersi. In quella folla c'era un giovane finlandese di nome Linus Torvalds, che installò MINIX sul proprio computer, ne studiò a fondo i sorgenti, scoprì che per leggere le discussioni da casa gli mancava prima un driver per il terminale e poi uno per il disco, se ne scrisse di suoi, e arrivò così a un kernel primitivo che il venticinque agosto del millenovecentonovantuno annunciò proprio sul gruppo dedicato a MINIX; da quell'annuncio nacque una collaborazione che portò il tredici marzo del millenovecentonovantaquattro alla versione uno zero di Linux, e dagli scambi accesi fra Tanenbaum, che difendeva la via del microkernel, e Torvalds, che difendeva il monolite pratico, nacque una delle discussioni più famose e più educate della storia di internet, che puoi ancora leggere oggi come un dialogo fra due generazioni di artigiani.
Con MINIX 3 davanti agli occhi possiamo finalmente disegnare la piantina della casa, perché il sistema è organizzato in quattro strati sovrapposti, e la regola d'oro, scritta nella definizione stessa dell'architettura, dice che soltanto il processi dello strato più basso possono usare le istruzioni privilegiate, mentre tutti gli altri vivono in spazio utente con le mani legate e vengono fatti girare dal kernel uno alla volta. Lo strato zero è il kernel vero e proprio, che programma i turni dei processi fra gli stati di pronto, in esecuzione e in attesa, gestisce tutti i messaggi che viaggiano fra i processi verificando che le destinazioni siano lecite e copiando i byte dal mittente al destinatario, e custodisce l'accesso alle porte di ingresso e uscita e alle interruzioni, che sui processori moderni richiedono appunto il modo privilegiato, e dentro lo stesso strato vivono due commensali speciali compilati nello spazio d'indirizzi del kernel, il task dell'orologio e il task di sistema, che sono schedulati come processi a parte con le loro pile, mentre il pizzico di codice scritto in assembler si limita ai gesti più bassi, i salvataggi dei registri durante i cambi di contesto e l'accoglienza delle interruzioni.
Sopra il kernel troviamo gli strati dei servizi, e la prima famiglia è quella dei driver di dispositivo, un processo separato per ogni tipo di apparecchio, il disco, la stampante, il terminale, la scheda di rete, ognuno dei quali vive in modo utente, non può toccare direttamente le porte dell'hardware e deve chiedere al task di sistema, con chiamate di kernel riservate, di leggere o scrivere al suo posto, come un cameriere che per accendere il forno deve passare la richiesta al cuoco attraverso lo sportello. Sopra i driver stanno i server, i processi che prestano i servizi veri, e due di essi sono essenziali in ogni sistema, il gestore dei processi, che esegue le chiamate per creare e far terminare i processi e regola la memoria, e il file system, che esegue le chiamate sui file, mentre attorno a loro orbitano il server delle informazioni per le diagnostiche, il server della reincarnazione che avvia e, se serve, riavvia i driver caduti, e il server opzionale della rete, e conviene tenere presente la distinzione fra chiamate di kernel, di basso livello e riservate ai servizi interni, e chiamate di sistema POSIX, quelle che i programmi comuni usano ogni istante, perché confonderle rende la lettura dei sorgenti assai più faticosa.
In cima alla pila abitano i processi utente, le shell, gli editor, i compilatori e i programmi che scrivi tu, tutti figli di un unico albero la cui radice è il processo speciale chiamato init, e attorno a loro girano anche i daemon, i processi di fondo che attendono eventi o scadenzano compiti, un po' come il cameriere che resta appostato vicino alla porta per accogliere le consegne. I processi di sistema, driver e server, sono processi come gli altri dal punto di vista del kernel, che li tratta tutti con le stesse regole, ma si distinguono perché non appartengono ad alcun utente, partono prima di chiunque abbia fatto il login e ricevono priorità di esecuzione più alte, cosicché un processo utente riceve il turno soltanto quando ogni servizio ha finito i propri lavori e si è messo in attesa, e questa gerarchia di priorità è il motivo per cui il tuo computer resta reattivo anche mentre qualcosa gli chiede molto.
Per tenere ordine fra tanti processi il kernel si affida alla tabella dei processi, che nel paragone del ristorante è il registro aperto sul banco del capocuoco, dove ogni cliente in sala ha la sua riga con lo stato del servizio, e ogni volta che un processo viene sospeso per far posto a un altro il sistema annota tutto ciò che serve per riprenderlo esattamente da dove l'aveva lasciato, i registri compreso il contatore di programma, le posizioni nei file aperti e ogni altro particolare, perché un processo che riparte deve ritrovare il suo mondo intatto. Una curiosità elegante di MINIX è che questa tabella è spartita fra kernel, gestore dei processi e file system, ciascuno dei quali custodisce i campi che gli servono, e quando un processo nasce o muore il gestore dei processi aggiorna la sua parte e poi manda messaggi agli altri due perché facciano altrettanto, così che tre commensali tengano aggiornato lo stesso registro senza mai accapigliarsi; il processo stesso, nella sua sospensione, è fatto della sua immagine in memoria, che i libri chiamano core image in onore delle vecchie memorie a nuclei magnetici, più la sua riga del registro.
I processi, però, non vivono soli, e qui arriviamo al cuore del progetto, perché la comunicazione fra processi, detta interprocess communication nei documenti tecnici, può avvenire in due grandi modi, e la scelta fra loro decide il carattere dell'intero sistema. Il primo modo è la memoria condivisa, in cui due processi leggono e scrivono la stessa zona di memoria, ed è rapidissima ma trabocchevole, come quando due camerieri gridano comandi in cucina e si afferrano lo stesso tegame nello stesso istante; il libro racconta la storia dello spooler di stampa, dove due processi leggono entrambi la variabile che indica il prossimo vano libero, trovano entrambi il vano numero sette, e il secondo sovrascrive il nome del file del primo, che aspetterà invano la sua stampa per anni, un guaio chiamato condizione di gara, perché il risultato finale dipende da chi corre più veloce, ed è tra i difetti più ostinati da rintracciare, dato che la maggior parte delle prove funziona e ogni tanto, a ciel sereno, succede qualcosa di inspiegabile.
Il secondo modo è il passaggio di messaggi, e MINIX lo adotta come unico gesto possibile,
sostituendo alla memoria condivisa un servizio postale affidato gestito dal kernel, con due
chiamate fondamentali che si chiamano send e receive, e ciascuna copia
un messaggio dalla memoria del mittente alla memoria del destinatario sotto lo sguardo del
cuore, così che nessun tegame venga mai condiviso. Torna alla scena del ristorante e
immagina che i camerieri smettano di gridare e si passino gli ordini su bigliettini attraverso lo
sportello, con la regola che un bigliettino viaggia solo quando chi lo riceve è lì pronto a
prenderlo, un abbinamento che gli informatici chiamano rendezvous, appuntamento,
perché se chi manda arriva prima si ferma pazientemente ad aspettare chi riceve, e in questo
modo non serve alcun cestino intermedio dove parcheggiare i bigliettini in attesa; a chi serve
una domanda con risposta la libreria offre sendrec, che invia il messaggio e resta in
attesa della risposta, che ricoprirà il messaggio originale come una risposta scritta sul
retro dello stesso biglietto.
Il meccanismo si completa con un gesto più leggero, la chiamata notify, che serve
quando un processo deve solo svegliarne un altro per avvisarlo che qualcosa è accaduto, per
esempio quando un tasto funzione preme e deve scatenare una diagnostica, e questo avviso non
blocca chi lo manda, il che lo rende immune dagli abbracci mortali in cui due processi
restano in attesa l'uno dell'altro per sempre; quando chi riceve non è pronto, il kernel si limita
ad accendere un singolo bit in una mappa di notifiche pendenti, un campanello miniaturizzato
che basta a rigenerare l'avviso, con l'identità del mittente e il momento esatto, il momento
in cui arriva la ricezione successiva. A rendere il tutto sicuro concorre anche una regola
architettonica ferrea, e cioè che i messaggi scorrono soltanto verso il basso attraverso gli
strati, dai programmi ai server, dai server ai driver, e mai in orizzontale fra programmi utente,
perché due processi che si mandassero messaggi l'un l'altro potrebbero entrambi restare
bloccati in attesa di una ricezione che nessuno dei due farà mai, l'abbraccio mortale di cui
dicevamo, che il disegno a strati rende strutturalmente impossibile.
Resta da chiedersi perché Tanenbaum abbia pagato il prezzo di questo disegno, perché il prezzo esiste ed è onesto ammetterlo, visto che copiare un messaggio da una memoria all'altra richiede sempre qualche passaggio in più rispetto a sfiorare una variabile condivisa, e la risposta del libro è che si acquistano semplicità e robustezza, dato che il meccanismo dell'appuntamento elimina la gestione dei cestini intermedi e il pericolo di esaurirli, e che i messaggi di lunghezza fissa decisa al momento della compilazione rendono impossibili per costruzione gli errori di trabocco dei buffer, una delle fonti più comuni di guai nei sistemi reali; ogni servizio, inoltre, è un processo indipendente che può essere sostituito da un altro scritto meglio senza nemmeno ricompilare il kernel, come cambiare il maitre senza riscrivere le ricette, e quando perfino l'arrivo di un'interruzione hardware viene consegnato al driver come un messaggio, il mondo intero del sistema assume una forma unica, regolare e leggibile, che è esattamente ciò che ci vuole per imparare.
Chiudiamo il cerchio tornando al momento più solenne della giornata del computer, che avevamo lasciato sospeso nella pagina sull'architettura con il caricatore al testimone pronto a raccogliere il testimone, e ora possiamo seguirlo fino in fondo; quando premi il bottone di accensione, il primo a svegliarsi è il firmware, il piccolo programma custodito in un chip della scheda madre, chiamato BIOS o, nelle macchine recenti, UEFI, che esegue l'autodiagnosi all'accensione, verifica memoria e dischi, e poi consulta una gerarchia di dispositivi da cui è lecito partire, che di solito guarda prima il lettore ottico e poi il primo disco fisso, un ordine che puoi cambiare entrando nelle impostazioni del firmware con il tasto indicato dal manuale del computer. Il firmware non sa avviare un intero sistema operativo, compito troppo grande per le sue spalle, quindi esegue soltanto la mossa successiva, che consiste nel leggere dal dispositivo scelto il primo settore, un pacchetto di cinquecentododici byte, caricarlo in memoria e saltare a eseguirlo.
Su un disco fisso quel primo settore custodisce il registro di avvio principale, che contiene un minuscolo programma e la tabella delle partizioni, e il programma consulta la tabella per trovare la partizione segnata come attiva, carica dal suo primo settore un secondo caricatore, che a sua volta va a cercare sul disco il programma di avvio vero e proprio, quello che finalmente legge l'immagine di avvio, il file multipartito che raccoglie i pezzi del sistema; nell'immagine trovano posto il kernel con dentro i task dell'orologio e di sistema, il gestore dei processi, il file system, almeno un driver per il disco, il server della reincarnazione, i driver del disco in RAM, della console e del registro, e infine init, e ognuno di questi pezzi è un programma separato, non un mattone saldato agli altri, il che è la dimostrazione pratica che la modularità di cui abbiamo parlato non è una favola. Quando il caricamento è completo il kernel comincia a girare, e durante la sua inizializzazione sveglia i due task interni, poi il gestore dei processi e il file system, che cooperano per far partire gli altri servizi dell'immagine, e solo quando tutti questi si sono sistemati e sono andati in attesa, cioè quando la cucina è allestita e ogni cameriere è al proprio posto, tocca finalmente a init, il primo processo utente, con il numero di identificazione uno riservato per tradizione.
Da quel momento in poi la vita del sistema si costruisce per gradi con pazienza artigianale, perché init esegue uno script di configurazione che avvia i servizi rimasti fuori dall'immagine di avvio attraverso l'interfaccia del server della reincarnazione, monta i dischi aggiuntivi, verifica che la macchina sia stata spenta per bene controllando un file di storico, e se trova la traccia di uno spegnimento brusco invoca il controllo dei file system, poi legge la tabella dei terminali e genera per ciascuno un processo che stampa l'invito e aspetta il tuo nome, il quale, una volta digitato, chiama il programma di accesso che verifica la parola d'ordine e, se tutto regge, esegue la shell, la conchiglia, il programma che aspetta i tuoi comandi e genera un processo nuovo per ciascuno, cosicché ogni programma che lanci è un discendente di init e l'intera famiglia forma un unico albero, con la differenza affascinante che, come ci ricorda il libro, la radice di quel ramo è il server della reincarnazione anziché init, un dettaglio che racconta quanto sia seria qui la cura dei guasti.
Tutta questa architettura, con i suoi strati, i suoi bigliettini e il suo registro del capocuoco, potrebbe sembrare un esercizio accademico lontano dal tuo computer, e invece è proprio il contrario, perché ogni volta che apri il coperchio di un programma, che aspetti una risposta dal disco o che il sistema sopravviva a un programma impazzito, dentro quella scatola accadono esattamente le cose che abbiamo raccontato, ed è qui che sta il dono più grande di MINIX, il cui codice sorgente è abbastanza piccolo da poter essere letto e capito da una persona sola con calma e una buona lampada, cosa che nessuno può dire dei milioni di righe di Linux o di Windows; Tanenbaum lo scrisse perché i suoi studenti potessero smontare un sistema vero come si smonta un motorino, e noi siamo gli studenti di quella scuola che non chiude mai, e prima o poi arriverà il giorno in cui vorrai costruirti il tuo sistema da solo, e scoprirai che i libri ti aspettano aperti.
La teoria, per quanto raccontata con calma, resta fumo finché non la tocchi, quindi adesso
vieni a provare con le tue mani, al computer, sei esperimenti ordinati dal più semplice al più
ambizioso, e ciascuno ti lascerà addosso una certezza che la sola lettura non regala. Per il
primo esperimento saluta il kernel del tuo computer, che è un ospite che lavora per te da anni
senza mai presentarsi, aprendo il terminale e scrivendo uname -a, che stampa nome
e versione del kernel e il tipo di macchina, e poi cat /proc/version, che racconta
chi lo compilò e quando; osserva che quasi certamente il cuore della tua macchina è Linux,
un discendente diretto del piccolo annuncio su Usenet, oppure il kernel di BSD o quello di
Windows, e rifletti sul fatto che quel numero di versione è il battito di un cuore che
qualcuno, da qualche parte, ha scritto riga per riga.
Per il secondo esperimento riguarda la cerimonia dell'avvio dal vivo, perché ogni sistema
conserva un diario di ciò che accade quando premi il bottone, e su Linux lo puoi rileggere con
dmesg | less, oppure con journalctl -b sulle macchine più recenti,
scorrendo pagina dopo pagina i messaggi in cui il firmware passa il testimone al caricatore, il
caricatore sveglia il kernel, e il kernel presenta uno per uno i driver che riconoscono dischi,
schede e porte; trova le righe in cui il tuo disco viene accolto e quelle in cui la scheda di rete
prende il suo nome, e pensa che ogni riga è un bigliettino passato attraverso lo sportello,
con la stessa forma esatta delle pagine che hai letto fin qui.
Per il terzo esperimento entra nella stanza del registro, perché Linux espone la tabella dei
processi come se fosse un cassetto di cartelle a disposizione degli ospiti, e basta scrivere
ls /proc per vedere una directory con un nome numerico per ogni processo vivo, poi
cat /proc/cpuinfo e cat /proc/meminfo per interrogare il kernel sul
processore e sulla memoria come un cameriere consulta il registro del capocuoco; apri con calma
qualche cartella numerica e guarda i file che descrivono lo stato di quel processo, e nota che
questo è esattamente il registro di cui parlavamo, con la differenza che qui la sala può
sbirciarlo, il che è comodissimo per imparare ma impensabile nelle cucine più riservate.
Per il quarto esperimento osserva l'albero dei processi fiorire davanti a te con ps axf, che disegna con i caratteri della tastiera l'albero genealogico intero, dal primo
processo fino al programma che hai aperto adesso, e poi lancia htop oppure
top per vedere il cambio di turno in tempo reale, con le colonne di priorità che
ricordano da lontano le sedici code di MINIX; sposta un processo in fondo o in cima con il
comando nice, che gli cambia la gentilezza con cui compete per il turno, e avrai
toccato con un dito la stessa manopola che il kernel gira migliaia di volte al secondo.
Per il quinto esperimento suona tu il campanello dello sportello e conti quante volte un
programma comune attraversa il confine fra sala e cucina, scrivendo strace -c ls in
una directory piena di file, così da vedere il resoconto delle chiamate di sistema usate dal
semplice comando di elenco, la lettura delle directory, l'apertura dei file, la scrittura sul
terminale; rifai l'esperimento con un comando più grasso, per esempio strace -c find /usr, e resta sorpreso di quanti bigliettini attraversino lo sportello per un lavoro che
a te sembra istantaneo, perché questo è il vero prezzo e la vera meraviglia dell'estesa
macchina di cui il libro parla all'inizio.
Per il sesto esperimento, quello vero, installa MINIX in una macchina virtuale, scaricando l'immagine dal sito minix3.org e affidandola a VirtualBox o a un altro hypervisor, poi segui la procedura guidata che impagina il disco e copia l'immagine di avvio, e quando il sistema si sveglia osserva con occhi nuovi i messaggi di avvio, perché ora sai che lì dentro un caricatore sta consegnando il testimone a un kernel minuscolo, il quale sveglia i suoi due task interni, poi il gestore dei processi e il file system, e infine init; prova a premere qualche tasto funzione sulla finestra della console, quelli che il server delle informazioni usa per stampare le diagnostiche, e osserva un driver cadere e rinascere per mano del server della reincarnazione, e capirai perché questa architettura si chiama tollerante ai guasti, un'espressione che sul tuo sistema quotidiano suona ancora come una promessa lontana.
Per chiudere la pratica, e forse per aprire una porta, vai a leggere i sorgenti di MINIX con il
browser, perché il sito del progetto offre la consultazione del codice directory per directory,
e comincia dal file principale del kernel, quello dove tutto ha inizio, percorrendolo riga per
riga con la calma di chi visita una città piccola, fermandoti sui commenti e sulle chiamate
send e receive che ora riconosci, e confronta la sua lunghezza, di poche
migliaia di righe, con quella dei sorgenti di Linux, che occupano come una biblioteca intera;
il libro che hai tra le mani in queste pagine nasce proprio perché quella lettura fosse possibile,
e scoprire che un cuore di sistema è qualcosa che una persona sola può tenere intero nella
propria testa è forse la certezza più bella che questo modulo possa regalarti.
Il modulo attinge soprattutto al capitolo introduttivo, con la sua storia di UNIX e di MINIX e la sua rassegna delle strutture dei sistemi operativi, e al capitolo sui processi, con le nozioni sulla comunicazione fra processi e la panoramica sull'organizzazione e sull'implementazione dei processi in MINIX 3, del libro Operating Systems Design and Implementation di Andrew S. Tanenbaum e Albert S. Woodhull, con un occhio all'appendice sull'installazione per la sequenza di avvio del sistema, che il lettore curioso può inseguire riga per riga nei sorgenti veri.
Ora che hai visto cosa accade nel cuore della macchina mentre lavora per te, il percorso scende un gradino più in basso, là dove il kernel e i suoi servizi sono stati scritti, e ti invito a proseguire verso la pagina successiva, dove imparerai a conoscere il linguaggio C, la lingua in cui sono scritti praticamente tutti i sistemi di cui abbiamo parlato, dal piccolo MINIX al gigante Linux, e dove scoprirai perché un linguaggio nato negli anni settanta è ancora oggi il cantiere in cui si costruisce il mondo.