🌽 Compilare e installare il kernel Linux da sorgente
Da OctoSpacc
Non di frequente mi saltano in mente nuove idee, ma, quando lo fanno, sono sempre bizzarre al punto giusto… Senza divagare troppo, è proprio per questo svilupparsi delle cose che anni fa, per la prima volta, mi sono trovata a compilare da sorgente il kernel Linux — su un Raspberry Pi 3 tra l’altro, cosa che col senno di poi non consiglierei — e adesso, precisamente ieri, di nuovo un’altra volta, dopo anni (stavolta sul PC, per fortuna), per un fine che non spoilero.
Perché ricompilare il kernel? 😯️
Linux si basa su un’architettura monolitica, cosa che ci riguarderebbe poco… se non fosse per il fatto che ha un’implicazione scomoda: qualsiasi funzione che lavora a livello di kernel deve essere compilata nello stesso. Anche il meccanismo dei cosiddetti moduli non copre tutte le situazioni possibili (almeno non a livello pratico), e ci si può quindi trovare in delle situazioni in cui il kernel fornito dalla distribuzione (o dal produttore hardware), anche se aggiornato, non ha qualcosa che serve, e quindi va sostituito.
Considerando le varietà infinite di distribuzioni Linux, repository che distribuiscono kernel compatibili o meno, e unendo ciò alla diversità di singole opzioni abilitabili nella compilazione del kernel… non solo in rari casi la compilazione da sorgente è l’unica via, ma spesso e volentieri è quella più pratica. Per fortuna, compilare il kernel Linux è molto semplice — decisamente di più dell’applicazione media per il desktop Linucs, con tremila dipendenze sempre in conflitto — ma ci sono dettagli magari non proprio immediati nel procedimento, quindi… ecco la mia nota!
0. Partenza e prerequisiti 📦️
Questa nota è sulla più generica ricompilazione del kernel Linux, cioè la variante ufficiale (quella di Linus Torvalds) su e per un PC desktop x86_64 (o AMD64). Niente fork o varianti patchate, niente versioni di OEM scassacose, niente installazione su dispositivi embedded o in qualunque altra misura esoterici, niente compilazione distribuita o cross-platform. Per casi più o meno limite, le cose da fare possono più o meno variare, anche se l’andazzo rimane lo stesso… vedete voi dai, il punto è che io assumo questa situazione. Nel mio caso, non ci sono nemmeno moduli kernel proprietari.
A parte eventuali strumenti preferiti per ottenere il kernel, nel caso scompattarlo, e qualche distrazione per passare il non poco tempo, i prerequisiti sono abbastanza ridotti. Come minimo serve un compilatore C (gcc) per motivi ovvi, GNU Make per eseguire i makefile, qualche tool assortito dal nome dimenticabile e qualche libreria super-specifica dall’interesse limitato. Poi, credo possano servire altri pacchetti, dipendentemente dalla distribuzione Linux in uso o dalle funzionalità che si devono compilare.
Non è in realtà chiarissimo sul web quali sono i pacchetti davvero necessari, e quali quelli che qualcuno che ha fatto altre guide ha detto “boh, mettiamo per sicurezza”… aggiorno qui con dei listini precisi man mano che verifico, includendo se necessario anche dei listoni eccessivi (per chi è a posto con il perdere lo spazio ma non il tempo). Per me è in genere una strategia installare il minimo possibile quando qualche requisito non è chiaro, avviare la compilazione, ed installare le altre eventuali dipendenze man mano che escono errori a riguardo.
Debian e derivate (apt
):
build-essential
libelf-dev
(obbligatori)ncurses-dev
(per usare la configurazione a menu TUI)
1. Ottenimento del codice ⛓️
Il codice sorgente di Linux si può prendere ufficialmente dal Git principale, dal GitHub di Torvalds, o dal sito Kernel.org. Preferisco l’ultima opzione, che ha le ultime versioni effettivamente necessarie messe belle in vista, fino al più vecchio branch supportato.
Di versioni supportate in un dato momento, appunto, ce ne sono diverse. In genere, se non c’è motivo di installare un kernel vecchio, uno più nuovo è meglio; non troppo nuovo, però, perché a vivere sul bleeding edge si perde sangue, con gli occasionali problemi anche gravi che possono saltare fuori! Quindi, evito il branch mainline
, aggiornato giornalmente o quasi, come la peste, e in genere scelgo stable
, aggiornato con un po’ di giorni in più. Altri utenti potrebbero legittimamente preferire la longterm
più recente (quella più in alto), per avere ancora più stabilità, ma io lo trovo inutile.
Per la riga di ogni versione, a parte la data, quello che interessa è il link [tarball], quindi si scarica quel file lì. Lo si può poi estrarre in una cartella comoda, tra i tanti modi, con il comando tar xvf <file>
. Tutti i comandi da eseguire da questo punto saranno all’interno della cartella preparata.
…Bisognerebbe scaricare poi anche il file PGP, quando presente, che serve a verificare che il download sia integro e che provenga dai distributori legittimi. Realisticamente, però, già non tutte le versioni lo hanno (stando a sottintendere che sotto sotto…), il download si fa dal sito ufficiale con HTTPS (quindi difficile sarà alterato), è un archivio compresso (quindi se corrotto dovrebbe fallire l’estrazione), e fare la verifica è una rottura di scatole allucinante. Altri enti avranno requisiti di sicurezza più alti, ma per gli utenti privati francamente è solo noia, io non lo faccio, non mi freca.
2. Configurazione del kernel 🔩️
Configurare il kernel è la parte più intricata… perché non si può eseguire comandi per inerzia, ma bisogna scegliere quello che effettivamente serve per, beh, avere il kernel con le opzioni bone. Ci sono, come accennato, infinite configurazioni, e diversi modi di usarle. Il punto principale è che c’è un file di testo enorme, con varie opzioni (principalmente booleane), che si può modificare.
Modificare quel coso a mano è da pazzi, però, perché esistono degli strumenti basati su menu ordinati, previsti nel processo di build, per gestire le varie possibilità. Anche qui, mezzo casino, ma la scelta è principalmente tra due programmi rispettivamente TUI e GUI, invocabili rispettivamente con make menuconfig
e make xconfig
. Io preferisco xconfig
per comodità e, seppure ho usato in passato la configurazione a menu curses, ed è per me abbastanza intuitiva, a quanto pare per molti non lo è… e il come si usi quel menu non è argomento di oggi.
menuconfig vs xconfig |
---|
Se non si fa altro, la configurazione che si va a modificare è quella predefinita del kernel come sta venendo sviluppato. Io non ho mai avuto problemi usando quella come base, ma, qualora ce ne fosse bisogno, oltre che quelle condivise da altri utenti, si può usare quella della distribuzione corrente. In base a come il sistema espone la configurazione del kernel, la si può (in genere) leggere con cat /boot/config-$(uname r)
, cat /boot/config
, o zcat /proc/config.gz
. Basta scriverla (>
) sul file ./.config
per applicarla alla configurazione corrente.
Io in questo caso volevo attivare il supporto al nuovo driver NTFS, quindi in xconfig premo CTRL+F, cerco “ntfs3” e imposto su (Y)es l’opzione che parla di “NTFS Read/Write something something”; poi, salvo la configurazione, chiudo, e… ho finito, incredibile.
3. Compilazione del codice 🧑🍳️
Con la compilazione, è il momento della noia, perché bisogna dare al computer semplicemente il tempo di macinare milioni (!) di righe di codice. Bene non va, ma almeno non è la fine del mondo: su qualunque PC non proprio scassone bisognerebbe farcela in meno di 4 ore. Sul Raspberry Pi 3 mi impiegò una giornata e mezza con la CPU a temperature nucleari (e anche per questo lo sconsiglio…), ma sul mio PC (Ryzen 3 3200G, 16 GB di RAM di cui pochi occupati dalla compilazione, su un SSD di fascia bassa) mi pare di aver impiegato 3 ore scarse a compilare la versione 6.10.7.
Bisogna semplicemente eseguire make
, e Linucs si farà da solo per l’architettura CPU corrente, viva la magia! Meglio ancora, make -j$(nproc)
per usare tutti i core CPU disponibili… La manciata di ore, dovrei precisare, è misurata rispetto a questo, altrimenti sarebbe mezza giornata, dato che il mio PC ha 4 core; potrebbe fare molto prima avendo più core, perché può compilare ancora più file in parallelo efficacemente.
Almeno nei primi minuti, è bene tenere d’occhio il terminale, per vedere se si verifica qualche errore e la compilazione si arresta. A me errori assurdi non sono mai capitati, ma, come ho detto, se non si installano tutte le dipendenze, potrebbe uscire qualcosa di relativo a quelle… in tal caso, si installa quello che manca (facendo riferimento alla propria distribuzione), e si continua con il comando di prima.
Dipendentemente dalla configurazione, servirà avere più o meno spazio su disco per completare la compilazione. Mentre il kernel di per sé non pesa mai più di qualche MB (e i moduli qualche decina di MB), la cartella alla fine della fiera mi è arrivata a pesare ~22 GB, quindi è bene o assicurarsi di avere abbastanza spazio, oppure controllare di tanto in tanto che questo non stia finendo. E se finisce durante il processo, semplicemente si libera spazio e si continua, anche in questo caso, come prima. (A patto che non vi si corrompa il file system solo per l’essersi riempito al 100%, vero BTRFS?)
4. Installazione di kernel e moduli 🏗️
Per compilare il kernel non sono necessari permessi di root, il che è comodo nel caso si voglia temporaneamente rubare il computer più potente di qualcun altro per fare il lavoro… ma, per installare, quelli ovviamente servono sul sistema corrente.
Qui è non solo semplice, ma anche abbastanza veloce (massimo qualche minuto): eseguendo (come root) prima make install
, e poi INSTALL_MOD_STRIP=1 make install_modules
, verranno installati nei percorsi appropriati, rispettivamente (ammesso di non aver cambiato i nomi): l’immagine kernel (vmlinuz-x.x.x-...
), di solito in /boot
e i moduli, di solito in /lib/modules/linux-x.x.x-...
. I moduli sono file separati dal kernel, perché vengono caricati dal sistema quando necessario… il punto è che alcuni tra i tanti servono a completare il boot, quindi vanno installati.
Bisogna anche preparare il cosiddetto file system di init, o ramdisk iniziale; normalmente finisce anch’esso in /boot
, sotto il nome di initrd.img-x.x.x-...
. E questo passaggio sarebbe abbastanza scomodo se non esistesse kernel-install
, script incluso con systemd, o la sua alternativa installkernel
. Avendo systemd, almeno il primo dovrebbe già essere installato, e in quel caso (assumendo i nomi di prima) basta fare (come root) kernel-install add x.x.x-... /boot/vmlinuz-x.x.x-...
; alternativamente, con il secondo, installkernel x.x.x-... /boot/vmlinuz-x.x.x-...
.1
Nota: chi sviluppa col kernel (non chi sta leggendo questo post, temo) potrebbe voler non settare la variabile d’ambiente INSTALL_MOD_STRIP=1
, perché debuggare senza i simboli di debug, che quella flag toglie, è difficile… ma per utenti comuni mortali come me sprecano solo spazio (tant’è che le distribuzioni li pacchettizzano a parte). Inoltre, chi non necessariamente sviluppa col kernel, ma deve compilare moduli esterni o fare altri magheggi, dovrebbe realisticamente anche installare i file header corrispondenti sul sistema: make headers_install
(sempre come root).
Almeno su distribuzioni “complete”, make install
e poi kernel-install
dovrebbero aver già configurato anche il bootloader, almeno avendo GRUB. Nel caso di distribuzioni, per così dire, “più fai-da-te” (che io non uso, btw), così come configurazioni di avvio più insolite… non è il caso mio, quindi non so nulla, e non dirò nulla. Le configurazioni di avvio sono una bestia per un’altra volta.
5. Prova del nocciolo 💥️
Se il kernel è installato, e il bootloader è stato correttamente configurato, allora basta semplicemente riavviare il computer per godere. È bene ricordare che, nel caso si stia usando GRUB, certi dicono che il nuovo kernel non viene selezionato come predefinito, e bisogna sceglierlo a mano dal menu di avvio… ma nel mio caso, su Linux Mint Debian Edition, mi è sembrato che invece fosse stato proprio impostato come default.
Ancora, se, ringraziando il pinguinone, il sistema alla fine parte, basta usare il comando uname -r
per controllare la versione del kernel in esecuzione. A questo punto, è bene verificare che la configurazione desiderata stia effettivamente funzionando… ma il come fare ciò cambia da funzionalità a funzionalità, caso per caso. Per confermare quantomeno che la configurazione specificata ci sia, basta controllare la configurazione del kernel corrente come al punto 2.
A questo punto, nel caso funzioni tutto e non servano altre modifiche, si può anche cancellare l’enorme cartella di compilazione, e il kernel rimarrà. Nel caso si dovesse in futuro anche disinstallare il kernel, basta cancellare normalmente i file installati prima, e riconfigurare il bootloader in modo appropriato.
Conclusione 🎊️
Poter compilare il kernel dal codice sorgente è un’altra di quelle cose che noi utenti Linux possiamo fare, e che gli utenti Windows francamente non ci invidiano. Ma è perché a loro piace vincere facile, col loro NT, basato su un design a microkernel… proprio così, ora che il punto dell’articolo è finito posso dire scemenze a ruota come piace a me.
Copium a parte, compilare Linux può essere qualcosa di divertente da fare almeno una volta. Se si capita in situazioni scomode come me va fatto, c’è poco da fare; però, se ci si vuole semplicemente divertire in modo stupido, magari una delle tante cose da poter fare è compilare un kernel ultra-specifico e super-ottimizzato per la propria macchina, o per scopi di virtualizzazione. E, certo, consuma un po’ di energia, ma mai quanto minare criptovalute o renderizzare missili nucleari in Blender!
🏷️ Note e Riferimenti
L’immagine decorativa di copertina è ottenuta tramite l’intelligenza artificiale generativa di Microsoft.
- A Guide to Compiling the Linux Kernel All By Yourself: https://itsfoss.com/compile-linux-kernel/
- Building and Installing Custom Linux Kernels (Rocky Linux): https://docs.rockylinux.org/guides/custom-linux-kernel/
- How to Build Linux Kernel From Scratch {Step-By-Step Guide}: https://phoenixnap.com/kb/build-linux-kernel
- The kernel build system: https://www.kernel.org/doc/html/v6.6/kbuild/index.html
- Installkernel e kernel-install (Gentoo Linux): https://wiki.gentoo.org/wiki/Installkernel ↩