Fra il pulsante di accensione e il prompt di login ci sono quattro consegne del testimone: firmware, bootloader, kernel, init. Capire quale si è interrotta distingue un ripristino di dieci minuti da una reinstallazione, e l’esame lo verifica chiedendo file e parametri esatti.

BIOS o UEFI: due modi di trovare il bootloader

I due firmware iniziano uguale: il POST cerca guasti hardware evidenti appena la macchina riceve corrente, poi vengono attivati i componenti minimi, cioè video, tastiera e dischi. Da lì divergono.

Il BIOS legge i primi 440 byte del primo dispositivo nell’ordine di avvio configurato: lì c’è la prima fase del bootloader. Quei 440 byte stanno dentro i primi 512, cioè l’MBR, che nello schema DOS ospita anche la tabella delle partizioni. Se l’MBR non contiene i dati giusti la macchina non parte. La prima fase chiama poi la seconda, che mostra il menù e carica il kernel.

L’UEFI ignora l’MBR. Riconosce le partizioni, legge diversi filesystem e tiene nella NVRAM della scheda madre l’elenco delle applicazioni EFI da eseguire: bootloader, selettori di sistema operativo, strumenti di diagnostica. Queste applicazioni vivono nella EFI System Partition, la ESP: una partizione dedicata in FAT12, FAT16 o FAT32 (ISO-9660 sui supporti ottici), da non condividere con la radice o con i dati utente. Il Secure Boot esegue solo applicazioni EFI firmate: protegge, ma può ostacolare l’installazione di sistemi non previsti dal produttore.

Conseguenza pratica: su BIOS un sistema installato dopo può sovrascrivere l’MBR e far sparire il menù degli altri; su UEFI no, perché ogni bootloader ha un file nella ESP e una voce in NVRAM.

GRUB e i parametri passati al kernel

Su x86 il bootloader più diffuso è GRUB. Se il menù non compare da solo si tiene premuto Maiusc quando lo chiama il BIOS, Esc quando lo chiama l’UEFI. Da lì si sceglie il kernel e si modifica la riga dei parametri, quasi tutti in forma opzione=valore. Quelli da avere in memoria:

  • root=/dev/sda3 indica la partizione radice, ro e rw decidono se montarla in sola lettura o in scrittura, rootflags= passa le opzioni di mount;
  • init=/bin/bash sostituisce il primo programma eseguito dal kernel: è la via di fuga classica quando non si arriva al login;
  • systemd.unit=graphical.target sceglie il target; systemd accetta anche i vecchi numeri di runlevel, e 1 o S portano al monoutente;
  • quiet nasconde i messaggi, acpi=off disattiva l’ACPI, mem=512M limita la RAM visibile, maxcpus=2 limita i processori (0 equivale a nosmp), vga=ask fa scegliere il modo video.

Per renderli permanenti si modifica la riga GRUB_CMDLINE_LINUX in /etc/default/grub e si rigenera la configurazione. Qui la famiglia conta:

# Debian e derivate
grub-mkconfig -o /boot/grub/grub.cfg
# Red Hat, Fedora e derivate
grub2-mkconfig -o /boot/grub2/grub.cfg

I parametri della sessione in corso si rileggono con cat /proc/cmdline.

Dal kernel a init, passando per initramfs

Il bootloader carica il kernel in RAM. Il kernel prende il controllo della CPU, imposta memoria e hardware di base, poi apre l’initramfs: un archivio montato come radice temporanea, con i moduli indispensabili per raggiungere la radice vera, tipicamente i driver del controller disco e del filesystem. Da qui l’incidente ricorrente di chi compila un kernel su misura senza initramfs adeguato: la radice diventa irraggiungibile perché manca il modulo del suo filesystem.

Trovata la radice, il kernel monta i filesystem elencati in /etc/fstab ed esegue il primo programma, init, che ha sempre PID 1; l’initramfs viene poi liberato dalla RAM. Se qualcosa si rompe qui il messaggio è inconfondibile:

ALERT! /dev/sda3 does not exist. Dropping to a shell!

Il kernel non ha trovato il dispositivo dichiarato come radice: parametro root sbagliato o modulo mancante nell’initramfs.

Chi fa da init: SysVinit, systemd, Upstart

Le implementazioni da conoscere sono tre. SysVinit, la tradizionale, esegue script di shell e organizza i servizi in runlevel numerati da 0 a 6. systemd è oggi il predefinito quasi ovunque: avvia i servizi in parallelo, li attiva su richiesta tramite socket e D-Bus, sorveglia i processi con i cgroup, gestisce le dipendenze e mantiene uno strato di compatibilità con comandi e runlevel SysV. Upstart, nato in Ubuntu proprio per parallelizzare l’avvio, va conosciuto solo di nome: Ubuntu lo ha sostituito con systemd nel 2015, e l’obiettivo LPI ne chiede consapevolezza, non pratica.

Leggere i messaggi di avvio

Il kernel scrive i propri messaggi nel ring buffer, una zona di memoria che si svuota allo spegnimento o con dmesg --clear. dmesg senza opzioni la stampa: i numeri fra parentesi quadre sono i secondi dall’inizio del caricamento del kernel. dmesg -H, o --human, attiva da solo il paginatore.

Dove c’è systemd si usa journalctl, che legge anche gli avvii passati:

journalctl -k                          # solo messaggi del kernel (uguale a --dmesg)
journalctl -b 0                        # avvio corrente (anche -b o --boot)
journalctl -b -1                       # avvio precedente
journalctl --list-boots                # elenco degli avvii
journalctl -D /mnt/hd/var/log/journal  # journal di un disco esterno

L’ultima riga è la più utile sul campo: quando una macchina non parte si collega il suo disco a una sana e si legge il journal da lì, che non essendo testo semplice richiede journalctl. I log testuali stanno invece in /var/log/, e anche qui la famiglia conta: su Debian e Ubuntu il file generale è /var/log/syslog, su Red Hat e Fedora è /var/log/messages.