Kernel (de) Kernel (it) Books Shell Sed/awk RAM/swap P35

Configurare, compilare e installare un kernel 2.6 per (open)SUSE Linux

Autore e copyright: Dr. Thomas Hertweck
Versione: 0.8; aggiornamento al: 6 Agosto 2006

Correzioni, proposte, miglioramenti, modifiche per la parte italiana a:
Luigi di Lazzaro

Sommario

  1. Come utilizzare questo documento
  2. Prefazione
  3. Il codice sorgente del kernel
  4. Scompattare il codice sorgente
  5. Patching del codice sorgente
  6. Clonare una configurazione esistente
  7. Adattare la release del kernel
  8. Configurare il kernel
  9. Compilare il kernel
  10. Installare il kernel
  11. Riconfigurazione del bootloader
  12. Problemi in fase di boot con il nuovo kernel
  13. Varie
  14. Installazione di più di un kernel con RPM
  15. Installazione di moduli kernel esterni
  16. Il processo di boot
  17. Il kernel Linux: l'inizio di tutto
  18. Links
  19. Ringraziamenti
  20. Disclaimer

Come utilizzare questo documento

La versione più recente di questo documento si trova sempre al seguente indirizzo: https://www.thomashertweck.de/kernel26i.html.
In questo howto si farà riferimento sempre a SUSE e non a Novell o al progetto openSuse per non complicare inutilmente la materia. Questo documento non è collegato nè direttamente nè indirettamente con Novell/SUSE, per cui, qualora dovessero insorgere dei problemi con esso, non si potrà fare uso del modulo di feedback. Tutti i marchi protetti qui citati appartengono al rispettivo proprietario.

Configurare, compilare e installare un kernel è un'operazione complessa. Parte delle operazioni deve essere eseguita da root ed errori o sviste possono avere conseguenze fatali per il sistema, per cui si raccomanda la massima cura e attenzione. Per cortesia, si leggano le varie sezioni sempre completamente prima di passare all'azione. Questo documento si riferisce principalmente all'installazione di un kernel della serie 2.6 su architettura x86. Ovviamente non si possono prevedere tutte le eventualità o situazioni in cui è dato incorrere, da qui la necessità di comprendere perfettamente i singoli comandi prima di eseguirli o di adattarli alle proprie esigenze. Il testo è organizzato secondo la sequenza logica delle singole operazioni e può quindi servire da traccia. Informazioni aggiuntive, spiegazioni più dettagliate o altre cose importanti di cui occorre tenere conto, sono presentate su sfondo colorato ed evidenziate da icone specifiche :

info Annotazioni
expl Spiegazioni
warn Importante

Nomifile o simili (in particolare quando riportano codici di versione) hanno funzione d'esempio e vanno sostituiti con i nomifile, patches, ecc... effettivamente presenti. Ovviamente le istruzioni non possono essere adattate ogni volta che viene rilasciata una nuova versione del kernel. Il manoscritto non ha pretese di infallibilità o completezza e ovviamente non si danno garanzie nè si assumono responsabilità per eventuali danni diretti o indiretti. Vedasi l'avviso (Disclaimer al termine del documento).

Ritorna al sommario

Prefazione

L'installazione del codice sorgente del kernel, la configurazione e la compilazione di un kernel personalizzato può servire a tante cose. Forse si vuole iniziare a programmare sul kernel oppure si vuole avere accesso alla documentazione, che viene installata assieme al codice sorgente. O forse si vuole semplicemente imparare qualcosa di nuovo divertendosi. Si fa notare che l'installazione di un kernel autocostruito non è un'operazione priva di rischi, per cui sarà bene premunirsi eseguendo un backup prima di inziare e procurandosi anche un CD di salvataggio. Per questo scopo si può ricorrere al CD/DVD di SUSE, ma anche il CD/DVD di Knoppix https://www.knopper.net/knoppix/ si presta allo scopo.

Questo testo si riferisce principalmete alla distribuzione (open)SUSE Linux ; tuttavia le istruzioni fornite dovrebbero essere facilmente adattabili anche ad altre distribuzioni.

Questo howto si propone di facilitare il compito ai principianti. Esperti non scopriranno nulla di nuovo. Esistono molti testi in lingua inglese riguardo al kernel di Linux, poca documentazione aggiornata in lingua tedesca e probabilmente altrettanto poca in lingua italiana. Sollecitato da diverse parti, l'autore ha deciso di raccogliere in un documento in lingua tedesca le informazioni fondamentali riguardanti l'argomento kernel; la successiva traduzione in lingua italiana rende le informazioni disponibili ad un fetta più ampia di appassionati.

Il documento non va scambiato con una ricetta e meno che mai va interpretato come un invito a manipolare un software fondamentale per la macchina senza farsi troppi pensieri. Taluni kernel possono richiedere l'update di altri pacchetti (per es. mkinitrd, udev, o simili) per funzionare correttamente. Il kernel 2.6 viene rielaborato con continuità e al momento non esiste una linea di sviluppo separata. Ciò significa che nuove versioni non devono necessariamente essere compatibili con le versioni precedenti. L'abitudine abbastanza diffusa di voler sempre avere la versione più nuova del software ("versionitis") è, almeno nel caso del kernel, sicuramente controproducente! Su sistemi SUSE i security-updates possono essere installati con YOU (YaST Online Update) e questo vale anche per il kernel. Neanche i KOTD devono venire installati partendo dal codice sorgente in quanto è sufficiente aggiungere con YAST agli archivi la relativa directory del server ftp (o di un mirror). Chi decide di mettere in pratica le istruzioni contenute in questo documento deve rendersi conto di quello che sta facendo e deve aver compreso perfettamente concetti e istruzioni esposte. Chi decide di affrontare spensieratamente la materia non troverà alcuna compassione.

Due parole sul kernel 2.6: nel Dicembre 2003 veniva dichiarato stabile e rilasciato ufficialmente il kernel 2.6.0. La serie 2.6, mantenuta e curata da Andrew Morton, sostituisce la vecchia versione stabile 2.4. Con il kernel 2.6 fanno il loro ingresso nel mondo linux diversi tra ampliamenti e miglioramenti, che avrebbero anche giustificato una serie 3.0. Questa possibilità era anche stata oggetto di discussione per diverso tempo. A differenza dei problemi insorti con la serie 2.4 (tra l'altro di stabilità, per la sostituzione del Virtual-Memory-Management all'interno di una serie dichiarata stabile, per problemi con il filesystem ReiserFS e NFS - in ultima istanza la release 2.4.11 era stata ufficialmente dichiarata "don't use"), si è fatta attenzione al funzionamento stabile e affidabile delle features più importanti. Di seguito alcune delle modifiche principali:

Queste sono solo alcune delle modifiche rispetto al kernel della serie 2.4. Il progresso maggiore viene sicuramente dai miglioramenti riguardanti le caratteristiche di latenza e scalabilità, che riducono sensibilmente le distanze rispetto ai sistemi unix commerciali.

Viene chiesto molto di frequente quanto spazio sia necessario avere sul disco e il tempo occorrente per compilare un kernel. Di seguito alcune indicazioni approssimative: la directory con i codici sorgente decompressi del kernel 2.6.16 richiede circa 275MB (kernel SuSE). Durante il processo di compilazione vengono creati diversi, ulteriori files (object-files, header- files, ecc.), per cui per un kernel attuale si dovranno mettere in preventivo circa 400MB di spazio su disco (con quasi tutte le funzioni del kernel attivate). Kernel più recenti abbisognano per solito di uno spazio maggiore. Il tempo impiegato per la compilazione dipende in larga misura da CPU e RAM, ma anche dalla configurazione impostata. Si va da pochi minuti (PC moderni con abbondante RAM) fino ad alcune ore per macchine vecchie o molto vecchie. Per fortuna è possibile compilare un kernel per una macchina anche molto datata su di un sistema nuovo, più veloce.

Ritorna al sommario

Il codice sorgente del kernel

Prima di poter iniziare con la configurazione bisogna ovviamente procurarsi il codice sorgente del kernel desiderato. Vi sono diverse possibilità al riguardo:

Per orientarsi rapidamente tra le varie versioni attuali del kernel si può ricorrere al programma "finger" contenuto nell'omonimo rpm. Un "finger @finger.kernel.org" riporta le informazioni richieste in un terminal. Tuttavia questa funzionalità non va assolutamente utilizzata per eseguire un monitoraggio automatico. L'informazione cercata può essere ottenuta anche direttamente da https://www.kernel.org/kdist/finger_banner

I kernel SUSE sono diversi dai kernel Vanilla. Il kernel SUSE deriva dal kernel Vanilla, ma vengono applicati dei patches specifici di SUSE. Se alcuni di questi patches risultano collegati con features rilevanti del sistema SUSE, può capitare che un kernel vanilla non funzioni correttamente su di un sistema SUSE.

Per il download del codice sorgente si raccomanda di utilizzare sempre un mirror locale, per non sovraccaricare i server FTP originali di kernel.org ovvero suse.com/opensuse.org. Un elenco di mirror è riportato sul sito web di openSUSE https://de.opensuse.org/Mirror_della_versione_stabile oppure https://www.novell.com/de-de/products/linuxprofessional/downloads/ftp/index.html. Un elenco di mirrors di kernel.org si trova presso https://www.kernel.org/mirrors/. Inoltre anche parecchi server dell'elenco SUSE fanno da mirror parziali per kernel.org.

Kernel diversi come per esempio il kernel SUSE-default o il kernel SUSE-SMP vengono creati sempre partendo dal medesimo codice sorgente, questo significa che esiste un unico RPM con il codice sorgente del kernel SUSE. I kernel sono diversi solo per la configurazione.

I pacchetti kernel-source-2.6.xx-y.rpm e kernel-source-2.6.xx-y.src.rpm sono diversi e servono per scopi diversi. Se si vuole installare il codice sorgente del kernel si dovrà ricorrere al primo citato. Il secondo è un cosiddetto source-RPM che può venire compilato con il comando "rpmbuild --rebuild" (rpm v4, a partire da SUSE Linux 9.0). Se si installa questo source-RPM si cercherà inutilmente il codice sorgente nella directory /usr/src! Infatti, in questo caso il codice sorgente (raccolto in un archivio tar con eventuali patches) è stato scompattato assieme ad un cosiddetto spec-file in /usr/src/packages nei sottoindirizzari SOURCES ovvero SPECS. È così possibile in teoria adattare il file spec, applicare un patch al codice sorgente del kernel o assemblare un nuovo source-RPM. questo procedimenteo non verrà approfondito in questa sede. Dettagli si trovano nel manuale "Maximum RPM" (in inglese) o "Das RPM Buch" (in tedesco).

Esistono alcune persone che mettono a disposizioni o mantengono patches ufficiali per i kernel Vanilla. Al momento il patchset più importante viene curato da Andrew Morton, il manutentore dello stable tree per il kernel della serie 2.6. È marcato con un "-mm" e riporta bugfixes, estensioni o integrazioni per il kernel 2.6. Patches denominati "-rc" sono cosiddetti "release candidates", cioè precursori della prossima versione.

A partire dal kernel 2.6.11 la versione può essere indicata con quattro cifre. Si è ricorsi a questa possibilità con il kernel 2.6.8 per poter rilasciare un bug-fix urgente. Le versioni a quattro cifre correggono solamente errori reali e confermati e non contengono innovazioni! La quarta cifra integra l'indicazione classica costituida da Major-Version ("2"), Minor-Version ("6") e Patch-Level (z.B. "11").

Ritorna al sommario

Scompattare il codice sorgente

Il codice sorgente del kernel della distribuzione SUSE può essere facilmente installato con YAST2 (o con un altro software di management dei pacchetti). Ad installazione completata si dovrà trovare un sottoindirizzario nella directory /usr/src con il codice sorgente. Probabilmente anche il link /usr/src/linux punterà verso questo nuovo indirizzario. Qualora il codice sorgente venga installato successivamente e al kernel effettivo sia già stato applicato un patch con YOU, si dovrà riavviare YOU per aggiornare il codice sorgente alla versione attuale.

Se il codice sorgente scaricato dal server ftp di (open)SUSE è presente in formato RPM e lo si vuole installare in parallelo con il codice sorgente del kernel della distribuzione non si potrà ricorrere al comando "rpm -Uhv kernel-source.nuovo.rpm", perchè così si sovrascriverebbe il codice sorgente già installato. A seconda di come è stato assemblato il pacchetto RPM si potrà installarlo semplicemente con il comando

$> su
$> rpm -ihv /percorso/verso/kernel-source.nuovo.rpm

Se RPM dovesse segnalare conflitti si potrà eventualmente ricorrere alle opzioni RPM "--nodeps --force". Un

$> su
$> rpm -ihv --force --nodeps /percorso/al/kernel-source.nuovo.rpm

dovrebbe anch'esso installare correttamente il codice sorgente nella directory /usr/src in una propria sottodirectory. questa è probabilemnte una delle poche occasioni in cui è consentito ricorrere alle opzioni RPM citate. In linea generale i conflitti segnalati da RPM non vanno ignorati! Con "rpm -qpl /percorso/al/kernel-source.nuovo.rpm" si può verificare prima il punto in cui verrà installato il codice sorgente e,qualora fosse già presente una directory con lo stesso nome, si potrà spostarla o rinominarla. Inoltre è possibile verificare se il pacchetto RPM installerà files in directory diverse da /usr/src. Probabilmente, come osservato in precedenza, il link /usr/src/linux punterà punterà verso la nuova directory.

Se il codice sorgente è presente sotto forma di archivio tar (p.e. il codice sorgente del kernel Vanilla), lo si dovrà prima scompattare con il progarmma tar. Per evitare conflitti si dovranno verificare prima i nomi delle directory kernel presenti in /usr/src (la directory in cui per solito si installa il codice sorgente del kernel) modificandoli (temporaneamente).

$> su
$> cd /usr/src/
$> tar xvjf /percorso/verso/linux-2.6.17.tar.bz2

ovvero

$> tar xvzf /percorso/verso/linux-2.6.17.tar.gz

Esiste la possibilità di assegnare la directory così creata e i file in essa contenuti ad un semplice user (e al gruppo relativo) per eseguire la compilazione (per eseguire la compilazione basta uno user; solo per l'installazione bisogna ricorrere a root). Eseguire dunque un:

$> chown -R myuser. linux-2.6.17
$> exit

(ci troviamo sempre ancora nella directory /usr/src). L'indicazione "myuser" va ovviamente adattata. Lo stesso dicasi per il nome della directory con il codice sorgente. Chi fa ricorso alla nuova funzionalità build-directory del kernel 2.6 può fare a meno di modificare il proprietario della directory.

Il kernel 2.6 offre la possibilità di indicare una build-directory. Per solito si salvano i files di configurazione e i files generati dal compilatore nella stessa directory in cui si trova il codice sorgente. Se si ricorre ad una build-directory i files vengono inseriti in una directory diversa da quella contenente il codice sorgente. In questo modo diventa possibile separare completamente la directory di assemblaggio dalla directory contenente il codice sorgente, con un incremento notevole in termini di flessibilità. Questa funzionalità non era presente nei kernel della serie 2.4. L'impiego della build-directory viene reso possibile dall'indicazione "O=/percorso/verso-la/build/directory" al make - la variabile KBUILD_OUTPUT del makefile viene impostata e tutti i files prodotti vengono deviati. La directory da passare a make deve naturalmente esistere altrimenti viene segnalato un errore!

Si osservi che nel caso in cui si ricorra ad una build-directory l'indicazione "O=/percorso/verso-la/build/directory" deve essere fornita per ognuno dei comandi make (di seguito descritti più da vicino) oppure (ed è il metodo più comodo), dopo aver clonato o creato una configurazione, ci si sposta nella directory di build e si immettono i comandi di make da lì!

A questo punto si raccomanda un'ispezione in veste di semplice user della directory ./Documentation, che si trova subito sotto la directory con il codice sorgente e la lettura del file "Changes". Di particolare interesse in questo file è il capitoletto "Current Minimal Requirements": in esso sono descritti i requisiti minimi per l'impiego del nuovo kernel. Eventualmente si dovranno rinnovare alcuni pacchetti di software prima di poter impiegare il kernel nuovo. Nel caso in cui si installasse il codice sorgente del kernel fornito assieme alla distribuzione non occorrerà ovviamente verificare nulla, poichè i requisiti minimi devono essere soddisfatti in ogni caso. Naturalmente andranno rinnovati solo i pacchetti che vengono effettivamente utilizzati: se, per esempio, non si impiega il filesystem XFS non sarà necessario rinnovare i pacchetti relativi, anche se i "Current Minimal Requirements" non dovessero risultare soddisfatti.

Il link /usr/src/linux è una fonte continua di equivoci. In linea di principio questo link non serve più (Linus Torvalds va anche oltre e ne propone l'abolizione). Per configurare, compilare e installare un nuovo kernel esso non è più necessario. Se però il link esiste, sarebbe opportuno se puntasse sempre verso il codice sorgente del kernel attivo. Il link viene utilizzato spesso (purtroppo) per compilare moduli esterni al kernel; il makefile corrispondente non esamina sempre il link /lib/modules/`uname -r`/build (come sarebbe corretto), ma confida in una corretta impostazione del link /usr/src/linux. Il modulo però verrà caricato e funzionerà solamente se per la sua compilazione sono stati adoperati gli header-files corretti. Quindi, se esiste un link /usr/src/linux che punta verso una directory contenente il codice sorgente di un kernel (meno recente) attualmente in funzione, durante l'installazione del nuovo codice sorgente esso può rimanere inalterato. Solo dopo che si è compilato, installato e avviato con successo il kernel nuovo conviene adattare il link /usr/src/linux alla nuova situazione. Se il codice sorgente viene installato con un pacchetto RPM può capitare che il link venga aggiornato automaticamente. Verificare e intervenire manualmente (o cancellare completeamente il link).

Ogni distribuzione arriva con due sets di kernel-headers: i "System Kernel Headers" e i "Kernel Source Headers". I "System Kernel Headers" sono gli header-files che vengono effettivamente utilizzati dal sistema. Ogni programma presente nel cosiddetto user-space (cioè ogni applicazione) viene compilato con l'aiuto di questi header-files. Di solito questi header-files si trovano in /usr/include/asm e /usr/include/linux. Questi files non dovrebbero mai essere sostituiti salvo il caso di un update della C library (un'operazione che si sconsiglia, perchè difficile anche per chi è già molto esperto!). Questi header-files possono essere utilizzati con molti kernel diversi (esiste un codice di compatibilità) e fanno parte del pacchetto glibc. Per solito vengono installati con il pacchetto glibc-devel. Ogni utente intenzionato a compilare programmi propri dovrebbe avere installato questo pacchetto di development! I "Kernel Source Headers" fanno parte del codice sorgente del kernel. Questi header-files non dovrebbero mai venire inclusi direttamente nel codice sorgente degli applicativi (salvo poche eccezioni). Nelle distribuzioni più vecchie /usr/include/linux e /usr/include/asm erano links simbolici verso le sottodirectory del codice sorgente in /usr/src/linux e non contenevano quindi un proprio set di header-files, ragion per cui il link /usr/src/linux era assolutamente necessario. Difficile imbattersi attualmente ancora in una simile costellazione. I "Kernel Source Headers" vengono utilizzati per compilare il kernel stesso o i suoi moduli. Il makefile per la compilazione di moduli esterni al kernel non dovrebbe dunque fare affidamento sull'esistenza di un link /usr/src/linux nè sulla sua corretta impostazione (per permettere l'individuazione degli header-files nella sottodirectory ./include del codice sorgente del kernel), ma invece dovrebbe esaminare il link /lib/modules/`uname -r`/build.

La directory con il codice sorgente non deve necessariamente trovarsi sotto /usr/src, anche se si tratta di un'abitudine consolidata. In linea di principio è possibile spostarla dove si vuole. In quest'ultimo caso vanno verificati i links /usr/src/linux e /lib/modules/`uname -r`/build se presenti, qualora si voglia spostare il codice sorgente dopo l'installazione dei moduli. In caso di dubbio si consiglia di eliminare completamente il link /usr/src/linux!

Ritorna al sommario

Patching del codice sorgente

Può essere necessario eseguire un patch sul codice sorgente, ad esempio per utilizzare una preversione di un nuovo kernel o per aggiornare il codice sorgente presente.

Fare attenzione con il patching: il patch deve corrispondere esattamente con il codice sorgente installato, in caso contrario il patch non funzionerà oppure sarà richiesto un intervento manuale. Per fare ancora un esempio, volendo passare dal codice sorgente del Vanilla 2.6.17 alla versione 2.6.18-rc3, andrà applicato il patch patch-2.6.18-rc3.bz2 (eventualmente anche compresso con gzip, nel qual caso esso presenterà il suffisso .gz) al codice sorgente della versione 2.6.17. Patches nella forma patch-2.6.x-yy.bz2 vengono applicati esclusivamente alla versione stabile del codice del kernel. Questo significa che per eseguire il patch patch-2.6.x-rc3.bz2 non è necessario eseguire prima i patches -rc1 e -rc2, ma il patch va applicato direttamente al codice sorgente del kernel 2.6.(x-1). Vanno tenuti distinti i patches incrementali, che hanno la denominazione patch-2.6.x-yy-zz.bz2; e, come suggerito dalla denominazione, andrebbero eseguiti sul codice sorgente del kernel 2.6.x-yy (si tratta di un kernel sul quale è già stato eseguito un patch), che verrebbe trasformato in un kernel 2.6.x-zz. Sembra un po' complicato, ma una volta comprese le convenzioni diventa abbastanza semplice.

Per eseguire il patch vero e proprio si ricorre al programma "patch" presente sotto forma di RPM sui CD o sul DVD della distribuzione. Di regola i patches sono compressi, per cui occorrerà procedere come segue:

$> cd /usr/src/linux-2.6.x
$> gunzip -c /percorso/verso/patch.gz | patch -p1 --dry-run

ovvero

$> bunzip2 -c /percorso/verso/patch.bz2 | patch -p1 --dry-run

a seconda che il patch sia stato compresso con gzip o con bzip. L'opzione "--dry-run" non modifica i files e verifica solo l'applicazione del patch. Nel caso di un patch correttamente eseguito non verranno rivolte domande all'operatore e dovranno apparire sul terminale solamente i nomi dei files ai quali il patch è stato applicato con successo. Se il sistema segnala un errore già con il primo file, probabilmente è sbagliata l'opzione -p1 del comando patch. Questa opzione indica il numero di / da togliere nei nomi file elencati nel patch. Ciò dipende dal modo in cui è stato creato il patch e, in caso di problemi, si potrà adattare l'opzione -p (eventualmente -p0 oppure anche -p2). Se il test ha successo si potrà eseguire il patch cancellando l'opzione "--dry-run":

$> gunzip -c /percorso/verso/patch.gz | patch -p1

ovvero

$> bunzip2 -c /percorso/verso/patch.bz2 | patch -p1

Se il patch non corrisponde esattamente con il codice sorgente del kernel può capitare che alcuni files non vengano lavorati. In questo caso vengono generati files con il suffisso .rej. Chi è abbastanza progredito può tentare di eseguire a mano il patch rifiutato, ma si tratta di un'operazione che si raccomanda solo a chi ha esperienza. Per un'analisi precisa si consiglia di protocollare l'output del comando patch; vedasi al riguardo il capitolo "Varie". Eseguito con successo il patch, andrebbero modificati i nomi delle directory con il codice sorgente. Per esempio, se si è eseguito un patch patch-2.6.18-rc3 sul codice sorgenti del kernel 2.6.17, si dovrebbe rinominare la directory:

$> su
$> cd /usr/src/
$> mv linux-2.6.17 linux-2.6.18-rc3
$> exit

Anche se non assolutamente necessario, serve per mantenere l'orientamento. Come sempre si tratta di un esempio e le denominazioni da utilizzare vanno adattate alla propria realtà.

Per il patch si può anche utilizzare lo script ./scripts/patch-kernel presente nella directory del codice sorgente. Un breve messaggio di aiuto si ottiene con un:

$> ./scripts/patch-kernel -h

nella directory principale del codice sorgente. Per ulteriori informazioni sul patching vedasi anche

$> man patch

come intuibile :-). Recentemente è stato presentato un programma sulla Linux Kernel Mailinglist (LKML) in grado di eseguire un patch anche su codici sorgente anche un po' diversi. Il programma di Neil Brown si chiama "Wiggle" e può essere scaricato da https://cgi.cse.unsw.edu.au/~neilb/source/wiggle/. Ovviamente il programma ha i suoi limiti, ma in certi casi può risultare molto utile.

Un patch eseguito con successo può essere annullato con l'identico comando e con l'opzione "-R"! Purtroppo la pagina del manuale non è molto chiara al riguardo, per cui si riporta esplicitamente questa possibilità.

Per prudenza e per avere un kernel-tree sicuramente pulito si potrà utilizzare il seguente comando. Esso cancellerà tutti i files generati, una eventuale configurazione presente ed anche tutti i files di backup eventualemente presenti riportando il codice sorgente allo stato primitivo:
$> cd /usr/src/linux-2.6.x
$> make mrproper
Vedasi al riguardo il capitolo "Varie".

Ritorna al sommario

Clonare una configurazione esistente

Può capitare di dover trasferire una configurazione esistente su codici sorgente nuovi. Poter partire con una configurazione funzionante da adattare poi alle proprie esigenze può essere particolarmente utile per chi inizia. Vi sono diversi metodi per clonare una configurazione esistente. Dipende tra l'altro dalla presenza o meno della build-directory. Come accennato in precedenza, la build-directory serve a tenere separati i files generati durante configurazione e compilazione del kernel dai files del codice sorgente.

Se il codice sorgente da configurare appartiene al kernel attivo non vengono rivolte domande all'operatore. Se il codice sorgente è più recente del kernel attivo verranno probabilmente poste alcune domande riguardanti l'attivazione di funzioni nuove o diverse. Alle domande si risponderà di conseguenza.

Utilizzando il codice sorgente (open)SUSE con il kernel SUSE attivo ovvero con l'opzione "Kernel .config support" (cioè lo pseudo-file /proc/config.gz viene generato), si può clonare una configurazione con i comandi:
$> cd /usr/src/linux-2.6.x
$> make cloneconfig && make modules_prepare
Questo procedimento non funziona con i kernel Vanilla provenienti da kernel.org. Se si impiega una build-directory:
$> cd /usr/src/linux-2.6.x
$> make O=/percorso/alla/build/directory cloneconfig && \
make O=/percorso/alla/build/directory modules_prepare

Ritorna al sommario

Adattare la release del kernel

Prima di compilare un nuovo kernel si dovranno porre in essere accorgimenti per evitare conflitti. Questo significa che al nuovo kernel dovrà essere data una denominazione univoca diversa per evitare conflitti con kernel e moduli già installati sul sistema.Questo passo non solo non si rende necessario, ma si rivela addirittura controproducente se si è clonata una configurazione per, ad esempio, compilare moduli esterni per il kernel attivo. I problemi qui esposti si presentano solamente se sivuole compilare es installre un kernel accatno ad un kernel già presente nel sistema.

Perchè è importante? Per esempio: si supponga che sul sistema sia installato solamente il kernel 2.6.16-default (file vmlinuz nella directory /boot) e i relativi moduli si trovino in /lib/modules/2.6.16-default/. A questo punto si decide di creare un nuovo kernel, modificando alcune opzioni nel file di configurazione e di installare il nuovo kernel accanto a quello presente sul sistema. Clonata la configurazione, modificate le opzioni secondo le proprie necessità, compilato e installato il nuovo kernel con i suoi moduli si pone un problema: per questo nuovo kernel i moduli verranno installati probabilmente nella stessa directory /lib/modules. Però, siccome la configurazione è stata modificata (si sono forse introdotte nel nuovo kernel opzioni, che prima erano realizzate sotto forma di modulo o viceversa), ne risulta un conflitto! Può anche capitare che un kernel meno recente richieda impostazioni diverse nel file /etc/modprobe.conf. Esistono poi altre possibili incompatibilità. In questo paragrafo si propongono soluzioni al problema.

Il procedimento da seguire per impostare una release univoca è diverso a seconda che si tratti di un kernel Vanilla o di un kernel SUSE. Procedimento per il kernel Vanilla:

Per l'installazione di kernel diversi evitando conflitti si utilizzerà la variabile EXTRAVERSION nel Makefile. Editare il Makefile presente nella directory principale del codice sorgente. Esso inizia come segue:

VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 17
EXTRAVERSION = .7

L'aggiunta di una EXTRAVERSION identifica in modo univoco la kernel release, evitando collisioni con altri kernel. Il Makefile può essere modificato e salvato come segue:

VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 17
EXTRAVERSION = .7-th1

Il kernel compilato, installato e attivato sulla macchina risponderebbe al comando "uname -r" con un 2.6.17.7-th1 e i moduli corrispondenti verrebbero installati nella directory /lib/modules/2.6.17.7-th1, senza scontrarsi quindi con i moduli di un altro kernel 2.6.17.7 già presente.

Adattatato quindi il makefile alle proprie esigenze, si potrà passare alla configurazione o alla compilazione del codice sorgente.

Nel caso di un kernel SUSE si può inserire una denominazione univoca durante la fase di configurazione (vedasi al riguardo la prossima sezione). Il punto "General Setup" offre l'opzione "Local version - append to kernel release". Questa opzione potrà eventualmente essere già impostata a "-default", come è consuetudine per i kernel SUSE (senza SMP). Questa impostazione può essere integrata come per il kernel Vanilla in modo da ottenere una denominazione di release univoca. La lunghezza dell'opzione non deve superare i 64 caratteri.

Il Makefile del kernel è in grado di mostrare la denominazione di release configurata:

$> cd /percorso/alla/build/directory
$> make kernelrelease

Consentendo cosí un facile controllo.

Ritorna al sommario

Configurare il kernel

Il sistema offre diverse possibilità per eseguire la configurazione del kernel. Come detto in precedenza, si raccomanda di eseguire la configurazione come semplice user. Per una configurazione (quasi) automatica esistono le seguenti possibilità (i comandi vengono immessi dalla directory pricipale del codice sorgente):

Resta da osservare che la configurazione ottenute mediante "defconfig", "allmodconfig", "allyesconfig", "allnoconfig" o randconfig raramente porta a risultati accettabili. Però questa opzione può servire da base di partenza o per tests. La configurazione definitiva andrà adattata sempre alle reali necessità con uno dei seguenti metodi.

Il programma di configurazione offre dei testi d'aiuto per ogni opzione da configurare. Se ne raccomanda un'attenta lettura fino a comprendere il significato dell'opzione corrispondente. Di solito viene anche consigliato cosa impostare in caso di dubbio. Non è possibile discutere tutte le opzioni di configurazione, perchè a) si uscirebbe dai limiti imposti da questo testo e b) funzioni e opzioni vengono aggiunte o modificate in continuazione, per cui questo testo non sarebbe mai aggiornato e c) ogni sistema è diverso a causa delle diverse composizioni a livello di hardware. Si elencano solo alcuni punti di particolare rilievo:

Per la configurazione di kernels SuSE impostare le "Build options" con valori validi e inequivoci. Come accennato, queste indicazioni sostituiscono l'editazione manuale del makefile e l'adattamento delle variabile EXTRAVERSION.

Prima di abbandonare la maschera di configurazione si devono salvare le impostazioni, altrimenti le modifiche vanno perdute. Inoltre si può salvare la propria configurazione in un secondo file, con un nome qualunque. Si raccomanda questa operazione, perchè basta poco per sovrascrivere il file .config. Per questo secondo file (in linea di principio si tratta solo di una copia di .config), conviene scegliere un nome pregnante. Ovviamente si può eseguire anche una semplice copia dalla console. Tenere presente che se si impiega una buil-directory il file .config non viene inserito nella directory del codice sorgente, ma nella build-directory.

Dopo la configurazione e qualora si impieghi una build-directory conviene spostarsi all'interno di quest'ultima. Tutti i comandi make si immetteranno senza l'indicazione "O=/percorso/alla/build/directory". In analogia, di seguito si riportano i comandi di make senza l'indicazione della build-directory.

Ritorna al sommario

Compilare il kernel

Configurato il nuovo kernel, si passerà alla sua compilazione. Per solito si esegue questa operazione con i soli diritti di user (non come root), in analogia con quanto praticato per la configurazione. Per il kernel esiste la possibilità di creare un pacchetto RPM (rudimentale). Questa possibilità verrà esaminata più avanti. Il comando

$> make

ovvero

$> make all

compila kernel e moduli. Se si vuole compilare solamente il file del kernel (senza moduli), si potrà ricorrere al comando "make bzImage" (su sistemi i386). I moduli stessi vengono compilati successivamente immettendo un "make modules" ovvero "make O=/percorso/alla/build/directory modules". Supponendo una corretta compilazione, l'assenza di bugs nel codice del kernel o del compiler impiegato, il processo di compilazione e linking dovrà terminare senza messaggi d'errore.

Se il sistema, su cui si compila il kernel, è dotato di più processori questi potranno essere utilizzati per ridurre i tempi di compilazione. Allo scopo si potrà utilizzare l'opzione "-j jobs" del comando make, con la quale si indica il numero di job (comandi) eseguibili in parallelo. Quindi si potrebbe compilare un kernel con:
$> make -j4 all
L'utilizzo di questa opzione fa senso solo su sistemi multiprocessore.

Quale effetto ha il comando "make bzImage" (in linea di principio dipende dall'architettura; qui si considera quindi solo la famiglia x86)? Il primo passo consiste nel compilare i sorgenti in C e in Assembler nel formato ELF (Executable and Linking Format) Object-Files (.o); durante questo procedimento alcuni files vengono raggruppati secondo uno schema logico e raccolti in archivi (.a). Con il linker ld si collegano staticamente verso vmlinux gli archivi e gli object-files. Con "nm vmlinux" viene generata la System.map. Nella directory ./arch/i386/boot/ il codice assembler del settore di boot bootsect.S viene preprocessato in bbootsect.s, poi viene elaborato e convertito nel raw binary bbootsect. Poi viene preprocessato il codice di setup setup.S in bsetup.s, che viene a sua volta convertito nel raw binary bsetup. Dopo il passaggio alla directory ./arch/i386/boot/compressed/, viene convertito <source-directory/vmlinux nel formato raw binary, le sezioni ELF .note e .comment vengono rimosse e salvate in un file temporaneo. Subito dopo il file viene compresso con un "gzip -9", convertito nel formato ELF "ld -r" e salvato in piggy.o. Vengono compilate le routines di compressione head.S e misc.c in head.o e misc.o, per poi venire "linkate" assieme a piggy.o con bvmlinux. bvmlinux viene portato nel formato raw binary (dopo che sono state rimosse le sezioni ELF .note e .comment) e salvato nel file bvmlinux.out. Per ultimo in ./arch/i386/boot/ vengono riuniti i files bbootsect, bsetup e compressed/bvmlinux.out con l'aiuto di ./arch/i386/boot/tools/build per formare il file bzImage.

l'image del kernel è compressa. Si fa notare che il nome "bzImage" non ha nulla a che vedere con il programma di compressione bzip2. Il kernel viene compresso con gzip. Il nome "bzImage" sta per "big zImage". In passato, quando l'image del kernel era più piccola, il kernel veniva creato con il comando "make zImage". La differenza tra "zImage" e "bzImage" consiste in un layout e in un algoritmo di caricamento per il kernel diversi, per cui un kernel creato con un "make bzImage" può avere dimensioni maggiori, perchè in questo caso il kernel Linux viene caricato in memoria oltre il limite di 1MB del real-mode.

Se la compilazione si interrompe con un "Signal 11" o con un "Segmentation fault", la causa più probabile è un difetto nell'hardware! Per solito si tratta di un banco RAM difettoso. Raramente programmi di test delle memorie sollecitano le RAM in misura maggiore del gcc, il programma di compilazione del kernel. Si può dire che quando si compila un kernel si esegue anche un test dell'hardware. Se, dopo un'interruzione di questo tipo, si riavvia il compilatore con "make" e il processo di compilazione si interrompe nuovamente con un "Signal 11", ma un po' più avanti rispetto al primo tentativo, al 99,9% si tratta di un difetto nell'hardware. Per questo genere di problemi si raccomanda una visita all'indirizzo https://www.bitwizard.nl/sig11/.

La produzione di un pacchetto RPM per il kernel 2.6 con un proprio file di configurazione risulta molto facile; volendo, si crea automaticamente anche un source-RPM. Il pacchetto RPM può essere installato successivamente nel sistema molto semplicemente: occorre solamente creare un'eventuale ramdisk e adattare la configurazione del bootloader. Il pacchetto RPM si crea immettendo

$> make rpm-pkg

ovvero

$> make binrpm-pkg

(a seconda dei casi, cioè se si vuole avere anche un source-RPM). In luogo di "make rpm-pkg" si può anche immettere "make rpm". Dipende dalla configurazione del sistema se per questa operazione è necessario ricorrere a root. In /usr/src/packages/RPMS/i386/ verrà depositato il kernel compilato con i suoi moduli mentre il source-RPM si troverà in /usr/src/packages/SRPMS/ Se si ricorre ad un RPM non occorrerà compilare esplicitamente kernel e moduli (make bzw. make bzImage und make modules) nè ovviamente installare kernel e moduli manualmente (make modules_install). Al posto di queste operazioni verrà installato il pacchetto RPM (non scordarsi della Initial Ramdisk, se necessaria).

Il binary-RPM completato viene sempre inserito in /usr/src/packages/RPMS/i386/, a prescindere dalle opzioni RPM eventualmente selezionate. Questo dipende dal fatto che l'architettura del sistema viene indicata nel kernel com i386, cioè il Makefile del kernel sostituisce tutti i i?86 con i386. Ciò non significa però che il kernel venga tradotto con l'opzione "-march=i386". Questo dipende da arch/i386/Makefile, la porzione del Makefile dipendente dall'architettura, che ricava i flags ottimali di compilazione dall'opzione di configurazione relativa al processore. Se nella configurazione del kernel si è impostata l'opzione Pentium-M CPU, il compilatore utilizzerà "-march=i686". Giustamente, il Makefile del kernel ricava flags di compilazione ottimali sulla base del sistema di destinazione. Opzioni RPM come "--target=i686" vengono ignorate, poichè potrebbero scontrarsi con l'opzione impostata nella configurazione (il nuovo kernel potrebbe eventualmente non funzionare sul sistema di destinazione).

Ritorna al sommario

Installare il kernel

Compilati il kernel e i moduli per esso necessari, si dovrà procedere alla sua installazione. Si parte dal presupposto che le indicazioni circa la kernel-release siano state considerate. Diversamente potranno insorgere dei porblemi, specialmente se il nuovo kernel si scontra con il kernel standard di SUSE, pretendendo l'installazione dei moduli nella stessa directory. Si presuppone che non sia stato generato un RPM.

Per prima cosa si dovranno installare i moduli. Questa operazione (e le seguenti riguardanti l'installazione del kernel e la riconfigurazione del bootloader) deve essere eseguita da root. Ci troviamo sempre ancora nella directory principale ovvero nella build-directory:

$> su
$> make modules_install

Il comando installa i moduli compilati in precedenza in /lib/modules/ e precisamente in una directory il cui nome corrisponde con il contenuto della variabile UTS_RELEASE del file ./include/linux/version.h. La variabile corrisponde con l'output prodotto dal comando "make kernelrelease" oppure da un "uname -r" una volta avviato il nuovo kernel. Il comando dovrebbe eseguire anche automaticamente un "depmod -ae" per attualizzare le dipendenze dei moduli.

A questo punto si copieranno il nuovo kernel e il file System.map (come root) nell'indirizzario /boot. Fare attenzione ad usare suffissi uguali per i files copiati nella directory /boot. I suffissi usati dovranno corrispondere con il nome dato alla directory in cui si sono installati i moduli (=kernel-release). Supponendo si sia creato un nuovo kernel 2.6.18-th1, i moduli corrispondenti sono stati installati in /lib/modules/2.6.18-th1, quindi si procederà con un:

$> cp ./arch/i386/boot/bzImage /boot/vmlinuz-2.6.18-th1
$> cp ./System.map /boot/System.map-2.6.18-th1

Si ossservi il trattino posto tra il nome del file e l'indicazione di versione.

Se si è inserito il nuovo kernel in un pacchetto RPM non si potranno installare manualmente kernel, moduli e il file System.map (come indicato sopra). Invece si dovrà installare da root il pacchetto binary-RPM. Il file spec utilizzato per la costruzione del kernel è molto semplice, per cui una initial ramdisk non viene generata automaticamente da uno script di postinstallazione. Quindi la initial ramdisk deve essere fatta a mano, come indicato più avanti. In alternativa si poterbbe creare un kernel-RPM con un file spec di SUSE adattato. In tal caso le operazioni manuali verrebbero eliminate quasi completamente. Tuttavia si consiglia questo metodo solo agli user progrediti, esperti nell'assemblaggio di pacchetti RPM.

Il file System.map, generato dal programma "nm", contiene i nomi e gli indirizzi simbolici del kernel Linux. Il file non è di vitale importanza, ma se si verifica un cosiddetto kernel oops (che per solito viene registrato in /var/log/messages) il programma "ksymoops" decodifica il messaggio con l'aiuto del file System.map convertendolo in un qualcosa di utile per gli sviluppatori del kernel. Il file System.map viene dunque adoperato essenzialmente per il debug del kernel. Ma anche il tool "ps" richiamato con l'opzione "-l" ricorre alla System.map per il contenuto del campo WCHAN nell'output. Da ps viene utilizzato il primo file System.map utile secondo quest'ordine:
/boot/System.map-`uname -r`
/boot/System.map
/lib/modules/`uname -r`/System.map
/usr/src/linux/System.map
Come si vede, è molto utile dotare tutti i files presenti in /boot di un suffisso, che corrisponde con l'output di "uname -r". Informazioni più dettagliate sugli oops si trovano in ./Documentation/oops-tracing.txt nella directory del codice sorgente.

Cos'è esattamente un oops? Chi sviluppa software riconoscerà immediatamente l'errore: Signal 11 o "Segmentation Fault". Un errore simile può verificarsi anche con il kernel. Se il kernel di linux legge un pointer non valido, questo può avere conseguente molto serie. Un oops dunque indica un bug nel kernel, che andrebbe sempre corretto. In altre parole: un oops è il modo con cui il kernel comunica all'operatore un evento particolarmente sgradevole (solo un'attenta analisi dell'oops è in grado di fornire informazioni più precise). Il kernel di Linux è comunque particolarmente robusto per cui, contrariamente con quanto accade per il segmentation fault di un programma, esso non dovrà necessariamente diventare instabile o addirittura bloccarsi. Un oops non ha nulla in comune con un kernel-panic. In quest'ultimo caso il kernel Linux si trova in una situazione di stallo, il sistema deve essere riavviato. Naturalmente anche un oops può causare un kernel-panic, se l'errore coinvolge parti vitali del codice. Se l'errore si manifesta solo in un driver probabilmente non ne nascerà un kernel-panic. Il kernel probabilmente si ritirerà più o meno elegantemente dal contesto e proverà a riportare la situazione sotto controllo. L'oops verrà registrato attraverso i canali tradizionali in /var/log/messages.

Eventualmente va creata una initial ramdisk per il nuovo kernel (da root):

$> cd /boot
$> mkinitrd -k vmlinuz-2.6.18-th1 -i initrd-2.6.18-th1

Come si vede, anche la initial ramdisk andrebbe "versionata". Le opzioni "-k" e "-i" sono necessarie, perchè in loro assenza la initial ramdisk verrebbe compilata per il kernel attivo e non per il kernel nuovo. I moduli da inserire nella initial ramdisk vengono letti dal file /etc/sysconfig/kernel (variabile: INITRD_MODULES). Se si vogliono inserire altri moduli nella initial ramdisk se ne indicheranno i nomi dopo il parametro -m del comando mk_initrd. Vedasi anche "mk_initrd -h" per ulteriori dettagli. Uno splash-screen dovrebbe generarsi automaticamente. Allo scopo viene esaminato il file /etc/sysconfig/bootsplash. Per dettagli al riguardo (anche per l'impiego di themes propri) si veda https://www.bootsplash.org/.

Per l'installazione del kernel si può ricorrere anche al comando "make install". Ma attenzione: il comando modifica link simbolici nella directory /boot, che possono riguardare le impostazioni standard del bootloader. Può capitare quindi che, riavviata la macchina, sia presente ancora l'opzione del vecchio kernel funzionante, ma alle spalle di essa si trovi il kernel nuovo, per cui il boot con il vecchio kernel non è più possibile. Eseguito un make install o si verificano subito le impostazioni standard per il kernel default di SUSE o si installa il kernel a mano, come indicato in precedenza. Questo ultimo metodo pare più sicuro, poichè i singoli passi possono essere ricontrollati e corretti.

Ora si andrà a riconfigurare il bootloader per avviare il nuovo kernel. Di seguito si descriverà questa operazione.

Ritorna al sommario

Riconfigurazione del bootloader

Chi impiega LILO dovrà adattare il file /etc/lilo.conf (da root). Il file va integrato con un ulteriore sezione per il nuovo kernel. Ci si potrà orientare osservando le sezioni esistenti. Una simile sezione potrebbe avere questo aspetto:

[...]
image = /boot/vmlinuz-2.6.18-th1
       label = linux2618
       append = "selinux=0 splash=0 resume=/dev/hda7 showopts"
       initrd = /boot/initrd-2.6.18-th1
       optional
       root = /dev/hda6
[...]

Una descrizione dettagliata delle opzioni si trova con un "man lilo.conf". Inoltre si raccomanda la lettura dei files presenti in /usr/share/doc/packages/lilo/.

Se nel file /etc/lilo.conf viene trovata l'opzione default il sistema avvierà il kernel specificato in quel punto, diversamente sarà il kernel specificato con "image" oppure "other" (questo potrebbe essere anche un sistema operativo diverso da linux). Si raccomanda di non assegnare immediatamente al kernel nuovo l'opzione default, ma di sceglierlo dal menù o dal boot-prompt di LILO. Una volta verificato il funzionamento del nuovo kernel, si adatterà il file /etc/lilo.conf, assegnando a questo kernel il default.

Dopo aver modificato il file /etc/lilo.conf si dovrà reinstallare il bootloader (da root!) come d'uso:

$> /sbin/lilo

Vengono elencati i labels di /etc/lilo.conf, il label che viene avviato per default viene contrassegnato con un asterisco *.

Per chi usa grub: la differenza maggiore tra LILO e grub sta nel fatto che grub è in grado di leggere la propria configurazione direttamente dalla directory di /boot. In caso di modifiche alla configurazione non sarà più necessario reinstallare il bootloader. Il file di configurazione si trova in /boot/grub e si chiama menu.lst. Un file menu.lst potrebbe contenere queste indicazioni:

default 0
title linux
       root (hd0,5)
       kernel /boot/vmlinuz root=/dev/hda6 vga=0x342 resume=/dev/hda7 showopts
       initrd /boot/initrd
title linux2618
       root (hd0,5)
       kernel /boot/vmlinuz-2.6.18-th1 root=/dev/hda6 vga=0x342 resume=/dev/hda7 showopts
       initrd /boot/initrd-2.6.18-th1

Con il seguente significato

Nella notazione di grub un device viene indicato con (hdX,Y). X sta indicare il disco, si comincia a contare da 0 per il numero dei dischi presenti sul sistema. La sequenza viene definita dal BIOS di sistema. Y è il numero della partizione sul disco individuato precedentemente con X e anche qui si inizia a contare partendo da zero. Nell'esempio (hd0,5) corrisponde alla sesta partizione presente sul primo disco. Se /boot è una partizione separata e non fa parte della partizione root(/), potranno trovarsi per kernel e initrd impostazioni tipo "(hd0,0)/vmlinuz-2.6.18-th1" o "(hd0,0)/initrd-2.6.18-th1", con le partizioni indicate così come spiegato sopra.

Informazioni dettagliate riguardo la configurazione si possono trovare come sempre con "man grub" oppure "info grub" e naturalmente in /usr/share/doc/packages/grub/. Anche qui vale la regola di non assegnare ad un kernel nuovo l'opzione default senza averne prima verificato il corretto funzionamento.

Moduli, che non fanno parte dei sorgenti del kernel, (come per esempio i moduli per i chips grafici) devono venire ricompilati e installati dopo che il nuovo kernel è stato avviato! Per il caso dei driver delle schede grafiche, avviare il kernel nuovo nel runlevel 3 immettendo un "3" al bootprompt (invece di partire con il runlevel standard 5). Compilare e installare dalla console in nuovo driver. Si veda al riguardo la documentazione presente nel pacchetto del driver per la grafica.

Installato il kernel (e dopo aver eventualmente adattato il file /etc/modprobe.conf si dovrà riavviare la macchina. Se non si sono fatti errori, la macchina dovrebbe avviarsi senza problemi.

Ritorna al sommario

Problemi in fase di boot con il nuovo kernel

Durante l'avviamento del nuovo kernel potrà apparire sulla console o in /var/log/boot.msg il seguente messaggio:
Inspecting /boot/System.map-2.6.13-15.11-default
Loaded 24247 symbols from /boot/System.map-2.6.13-15.11-default.
Symbols match kernel version 2.6.13.
No module symbols loaded - kernel modules not enabled.
Il messaggio non è originato direttamente dal kernel, ma da klogd, il demone del kernel e proviene dal file ksym_mod.c del codice sorgente di sysklogd. Qui si utilizza un metodo superato (get_kernel_syms) per ottenere informazioni sui moduli. Un'indicazione al riguardo si trova in "man get_kernel_syms": Because of these limitations, this system call is deprecated in favor of query_module. Il problema produce in ultima analisi un messaggio d'errore con il kernel 2.6 (impostazione CONFIG_MODULES=y nel file di configurazione .config), nonostante i moduli vengano comunque utilizzati. Il module-handling funzionerà correttamente, a dispetto del messaggio, quindi non lasciarsi confondere! Se si vuole eliminare il messaggio è necessario fare ricorso ad una versione (patch) nuova di sysklogd.

Durante il boot del nuovo kernel possono presentarsi problemi. Essi stanno ad indicare che alcuni punti durante la configurazione o durante l'installazione non sono stati rispettati. Per esempio:

Il seguente articolo (in lingua tedesca) può essere d'aiuto se si presentano problemi di boot: Oliver Diedrich (odi), "Wenn der Pinguin nicht abhebt: Erste Hilfe bei Linux-Startproblemen", Praxis PC-Selbsthilfe: Linux, lilo, grub, ext2, ext3, reiserfs, fsck, c't 26/01, Seite 130.

Ritorna al sommario

Varie

Il comando cancella il file .config eventualmente presente! Se si vuole mantenere il file questo dovrà essere salvato separatamente prima di eseguire il comando.

Immettendo un "make help" nella directory del codice sorgente si ottiene un breve messaggio d'aiuto (in inglese), riguardo possibili targets del makefile. Viene riassunta la maggior parte dei comandi citati in questo testo.

Ritorna la sommario

Installazione di più di un kernel con RPM

Capita di frequente che, accanto al kernel di prima installazione, si voglia installare un altro kernel SuSE (per esempio un nuovo kernel sperimentale dall'archivio ftp di SUSE). Sarebbe comodo se si potesse fare con il comando RPM, senza dover compilare i codici sorgente stessi. SuSE mette a disposizione esplicitamente binary-rpms ( o si è assemblato un RPM, vedasi più sopra). Con il comando

$> rpm -ihv nuovo-kernel.rpm

si può tentare l'installazione. In caso di successo si potrà verificare ed adattare la configurazione del bootloader (non dimenticarsi della initial ramdisk). Come accennato in precedenza, si può aggiungere la directory KOTD di SUSE con YAST in funzione di archivio d'installazione. In questo modo si potranno installare kernel KOTD per mezzo del Software Management-System di SUSE (in questo caso però non si tratta d'installazioni di kernel in parallelo).

Il comando "rpm -Uhv nuovo-kernel.rpm" sostituirà il kernel attualmente installato con il nuovo kernel! Con questo metodo non è possibile installare kernel in parallelo.

Per scoprire quale kernel-Rpm risulta installato momentaneamente sul sistema si può utilizzare il comando "rpm -qa | grep '^kernel'".

A secondo del procediemnto seguito nella creazione del file RPM, può capitare che vengano segnalati conflitti se si tenta l'installazione. Per risolvere il problema esistono diverse possibilità. Con il comando

$> rpm -qpl nuovo-kernel.rpm

si possono vedere i files che verrebbero installati sul sistema. Se non emergono gravi conflitti con kernel già installati sul sistema si può tentare l'installazione del nuovo kernel con l'opzione RPM "--force --nodeps". Si fa però presente che questo procedimento porta ad una banca dati RPM inconsistente. In alternativa si può fare in modo che dalla banca dati RPM venga cancellato il record del vecchio kernel (viene cancellata solo l'inserimento, non viene modificato il filesystem cioè non vengono cancellati files):

$> rpm -e --justdb vecchio-kernel

Ora è possibile installare il nuovo kernel:

$> rpm -ihv nuovo-kernel.rpm

Il comando crea la directory /lib/modules (che contiene i moduli del nuovo kernel) e ricopia l'image del kernel nella directory /boot. Una eventuale Initial Ramdisk dovrebbe venire creata correttamente durante l'installazione; diversamente (nel caso di kernel Vanilla) si dovrà intervenire manualmente come spiegato nel capitolo riguardante l'installazione del kernel. Attenzione: la cancellazione del kernel standard di SUSE dalla banca dati RPM comporta che in futuro non verranno più offerti kernel-updates attraverso YOU. Se si utilizza un proprio kernel si dovrà provvedere personalmente per gli aggiornamenti.

Per poter avviare tanto il vecchio quanto il nuovo kernel si dovrà eventualmente ancora riconfigurare il bootloader. Il procedimento è descritto nell'apposito capitoletto. Attenzione: controllare attentamente nomi e sezioni del file di configurazione del bootloader, pena il mancato avviamento del sistema.

Per il nuovo kernel vanno creati/installati i moduli esterni.
 

Ritorna al sommario

Installazione di moduli kernel esterni

Capita talvolta che si debbano installare o aggiornare moduli esterni, che cioè non fanno parte del codice sorgente del kernel, poichè un modulo kernel binario di solito funziona solamente con una versione del kernel ben precisa. Moduli kernel possono eventualmente essere utilizzati con versioni del kernel non proprio identiche se è stato attivato il meccanismo chiamato "modversions" (impostabile tramite il file di configurazione del kernel). In questo caso viene aggiunto ad ogni simbolo (variabile, funzione) un codice di controllo. In questo modo dovrebbero essere disponibili informazioni sufficienti a scoprire un eventuale incompatibilità tra il modulo ed il kernel. In ogni caso è preferibile compilare sempre un modulo su misura per il kernel che si intende usare.

Per compilare un modulo esterno devono essere presenti determinati header-files del kernel. In altre parole, il kernel-source deve essere configurato correttamente. Se si è configurato, compilato ed installato un kernel e non si sono più apportate modifiche alla configurazione, il codice sorgente del kernel risulterà correttamente configurato. In tutti gli altri casi procedere come descritto nel capitoletto sulla clonazione di una configurazione. Particolarmente importante è il comando "make modules_prepare", poichè esso genera tra l'altro gli header-files collegati con il modulo esterno ( come per esempio: linux/version.h).

Il procedimento da seguire per compilare un modulo esterno dipende dal Makefile del modulo stesso. Di regola si parte dal presupposto che il kernel, per il quale si vuole compilare il modulo, sia quello attivo al momento sul sistema.

Per compilare il modulo esterno vero e proprio ci si sposterà nella sua source-directory. Con il comando

$> make -C /percorso/alla/build/directory M=$(pwd) modules

(si spera) verrà compilato il modulo esterno che potrà essere installato da root con un

$> make -C /percorso/alla/build/directory M=$(pwd) modules_install

Se inizialmente non si è fatto uso della build-directory si dovrà indicare all'opzione "-C" la directory del codice sorgente (al posto della build-directory). La lettura della documentazione del modulo può fornire ulteriori informazioni circa le modalità di compilazione. In caso di dubbio non rimane che analizzare il Makefile del modulo.

Ritorna al sommario

Il processo di boot

Il BIOS del PC avvia il processo di boot. Terminato il Power-on-self-test (POST), il BIOS cercherà di caricare il primo settore del primo dischetto, il cosiddetto bootsector. Se questo tentativo fallisce, il BIOS cercherà di leggere il primo settore del primo disco rigido. BIOS abbastanza recenti sono in grado di modificare la sequenza di lettura delle periferiche (floppy, disco rigido, CD-ROM, scheda di rete, ecc.) secondo i desideri dell'utente. Il boot del sistema operativo si svolge poi in più passi. Siccome lo spazio disponibile nel boot-sector è molto limitato, viene caricato per solito un secondo loader, ecc., fino a caricare il sistema operativo vero e proprio. In linea di principio la struttura del boot-sector è abbastanza semplice e la sua grandezza è sempre uguale a 512 bytes. Il codice inizia con l'offset 0 e il boot-sector termina sempre con il Magic Number 0xAA55.

Il boot da dischetto è molto semplice, poichè ogni dischetto dispone di un unico boot-sector. Dischi rigidi possono essere suddivisi in partizioni per il cui il boot si complica. Il BIOS nulla sa di questa ulteriore ripartizione, per cui si limita a leggere il primo settore del disco, chiamato anche Master Boot Record (MBR). La struttura del MBR è identica con quella di un qualsiasi altro boot-sector, cioè il codice inizia all'offset 0 e all'offset 0x1FE si trova il magic number 0xAA55. La parte terminale del MBR accoglie la partition table, che è costituita sempre da 4 registrazioni. Ogni registrazione nella partition table è lunga 16 bytes (boot-flag: 1 byte; numero di head d'inizio della partizione: 1 byte; numero settore e cilindro del boot-sector: 2 bytes; systemcode: 1 byte; numero di head alla fine della partizione: 1 byte; numero settore e cilindro dell'ultimo settore della partizione: 2 bytes; numero relativo del settore di inizio: 4 bytes; numero di settori nella partizione: 4 bytes). Dunque, un disco può essere suddiviso in 4 partizioni, le cosiddette partizioni primarie. Se questo non dovesse essere sufficiente si potrà ricorrere ad una partizione estesa. Quest'ultima accoglie come minimo almeno un device logico. La struttura del primo settore di una partizione estesa corrisponde con un MBR. La prima registrazione della partizione estesa contiene il primo device logico della partizione. La seconda viene utilizzata come pointer nel caso esistano ulteriori devices logici. Il pointer è impostato in modo da puntare dietro al device logico, dove, a sua volta, si trova una partition table con la registrazione del prossimo device logico. I devices logici sono dunque organizzati in una lista concatenata. Il primo settore di ogni partizione primaria o estesa contiene un boot-sector strutturato come descritto. Siccome è possibile avviare la macchina solo da una partizione, il boot-flag indica la partizione attiva. Il codice nel MBR deve dunque a) indicare la partizione attiva, b) caricare il boot-sector della partizione attiva con l'ausilio del BIOS e c) eseguire un salto logico verso l'offset 0 del boot-sector. Un bootloader come LILO o grub è in grado di sostituire il MBR con codice proprio o occupare il boot-sector di una partizione.

Se il bootloader (LILO o grub) viene installato nel MBR esso sostituisce il codice lì presente, cioè il MBR originale viene sovrascritto. Se il MBR originale non viene salvato automaticamente, si può provvedere con

 $> dd if=/dev/hda of=MBR.backup bs=512 count=1
 

al salvataggio. Con

 $> dd if=MBR.backup of=/dev/hda bs=446 count=1
 

si potrà ripristinare. Attenzione: questa è un'operazione estremamente delicata. Prestare la massima attenzione, pena la perdita di tutti i dati del disco. Se si vuole ripristinare la vecchia partition table si può ricorrere al parametro bs=512. Si raccomanda prudenza: è molto facile distruggere la propria partition table. Il boot-sector, che avvia il kernel Linux, può essere il boot-sector di Linux (corrisponde con arch/i386/boot/bootsect.S) o anche il boot-sector di un bootloader.

Il boot continua come segue: il BIOS sceglie il device di boot. Poi viene letto il boot-sector di questo device, nel nostro caso si tratta del boot-sector del bootloader sul disco rigido. Questo carica setup, routines di espansione e image del kernel in memoria. Un kernel originato da un "make bzImage" può essere trasferito con il BIOS-service "int 0x15" direttamente dalla zona bassa della memoria alla zona alta. (il boot-loader utilizza le stesse tecniche contenute nel boot-sector del kernel). Una volta caricato (un bootloader come LILO termina qui) viene elaborato arch/i386/boot/setup.S e si raggiunge il punto di ingresso start: viene inizializzata una parte dell'hardware e il processore viene posto nel "Protected Mode". In ultimo si passa all'inizio del kernel compresso che corrisponde con arch/i386/boot/compressed/{head.S,misc.c}. Il kernel viene espanso e vengono eseguite le inizializzazioni contenute in arch/i386/boot/compressed/head.S (per esempio inizializzazione della page-table, del coprocessore, dello stack, ecc.). Poi viene avviato start_kernel(). Qui avvengono le inizializzazioni "high level", tra l'altro: setup specifico per l'architettura, output del Linux-banner (versione kernel, compiler, ecc.); inizializzazione di IRQs, ACPI, scheduler, timer, ecc.; esame della riga di comando al boot (boot-parameter); inizializzazione della console e caricamento dinamico dei moduli; vengono permessi interrupts; accertamento dei BogoMIPS; inizializzazione del management della memoria e output del valori (riga di memory); inizializzazione del proc-filesystem, di diversi caches e del thread-handler; analisi di bugs specifici per l'architettura e attivazione di workarounds (per esempio il classico CPU "f00f Bug") Finalmente viene creato il kernel-thread init(), che inizializza i driver per l'hardware per poi avviare il programma init= immesso al boot come parametro o tentare uno /sbin/init, /etc/init o /bin/init (in questo ordine). Se nessuno dei programmi viene trovato viene tentato l'avviamento di una console di rescue con la shell /bin/sh. Se anche questo tentativo fallisce il l'elaborazione termina con un kernel-panic. Altrimenti il boot continua come descritto in "man init". Di solito vengono avviati i processi attivi in background e si provvede ad avviare il programma getty su ogni terminale connesso. Uno user può accedere al sistema.

Il boot non è descritto in modo completo ed esauriente, anche per i limiti imposti al documento. In pratica esso è molto più complicato, dato che si dovrebbe considerare l'impiego di una Initial Ramdisk o di un sistema multiprocessore. La descrizione in forma molto semplificata serve comunque a dare un'idea della complessità e dei processi che avvengono dietro le quinte.

Ritorna al sommario

Il kernel Linux: l'inizio di tutto

Linus Torvalds, uno studente finlandese a Helsinki, diede il via nel 1991 ad un piccolo, nuovo sistema operativo:

From:       torvalds@klaava.Helsinki.FI (Linus Benedict Torvalds)
Newsgroups: comp.os.minix
Subject: What would you like to see most in minix?
Date: 25 Aug 91 20:57:08 GMT

Hello everybody out there using minix -
I'm doing a (free) operating system (just a hobby, won't be big and professional like gnu) for 386(486) AT clones. This has been brewing since april, and is starting to get ready. I'd like any feedback on things people like/dislike in minix, as my OS resembles it somewhat (same physical layout of the file-system (due to practical reasons) among other things).

I've currently ported bash(1.08) and gcc(1.40), and things seem to work. This implies that I'll get something practical within a few months, and I'd like to know what features most people would want. Any suggestions are welcome, but I won't promise I'll implement them :-)

Linus (torvalds@kruuna.helsinki.fi)

PS. Yes - it's free of any minix code, and it has a multi-threaded fs. It is NOT protable (uses 386 task switching etc), and it probably never will support anything other than AT-harddisks, as that's all I have :-(.

Il suo piccolo progetto ebbe una grande eco e un gruppo di attivisti formatosi rapidamente iniziò a sviluppare il nuovo sistema operativo. Oggi vi sono numerosi utenti che provano nuove versioni e contribuiscono a ripulire il software da errori. Linux viene sviluppato spesso a condizioni aperte e distribuite. Con "aperte" si intende dire che chiunque sia in grado di farlo può partecipare allo sviluppo. Per questo scopo è necessario un mezzo di comunicazione rapido, efficiente e su scala planetaria: internet. Non meraviglia quindi che buona parte dello sviluppo provenga da studenti, che hanno accesso a internet attraverso le università. Nella fase iniziale questi studenti avevano a disposizione equipaggiamenti modesti, per cui Linux è sempre ancora il sistema operativo a 32 bit, che necessita di meno risorse senza rinunciare a funzionalità. Nel frattempo Linux è presente su quasi tutto ciò che elabora bits e bytes. L'hardware supportato va da sistemi embedded ai mainframes IBM. Questa indipendenza dall'hardware non viene raggiunta da nessun altro sistema operativo.

Il rapido sviluppo di Linux è dimostrato dalle dimensioni del codice sorgente:

Con una crescita esponenziale e si può immaginare cosa significa per i futuri kernel :-). L'obbiettivo di far funzionare la maggior parte del software GNU su Linux era già stato raggiunto con la versione 0.99.

Ritorna al sommario

Links

Alcuni links, che riguardano il kernel o altri aiuti. L'elenco non è completo, ma dovrebbe essere sufficiente per trovare ulteriori informazioni. Si osservi: la maggior parte della documentazione, che riguarda il kernel Linux, è redatta in inglese.

Ritorna al sommario

Ringraziamenti

Un grazie a tutti quelli che hanno fornito idee e aiuto sulla mailinglist suse-linux@suse.com, che mi hanno incoraggiato e sostenuto durante la stesura di questa piccola traccia. Un grazie ad Andreas Winkelmann per il testo su grub e a Philipp Thomas, che mi ha esposto alcuni dettagli del kernel SuSE, a David Haller per il suo multi-kernel-howto. Inolter a Sebastian Huber, Klaus Ondrich, David Hundenborn, Hans-Robert Wagner, Horst Graffy und Guido Nottebrok (olte purtroppo ad alcuni anonimi), che con il loro feedback hanno contribuito a migliore il documento.

A personal note by the author: special thanks go to Luigi di Lazzaro for the translation into Italian! He did a magnificent job!

Ritorna al sommario

Disclaimer

Questo testo non avanza pretese di correttezza e completezza e naturalmente non si possono dare garanzie riguardo contenuti e conseguenze derivanti. Per completezza:

There is no warranty for this text. The author provides it 'as is', without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality of this text is with you. In no event will any author named in this text be liable to you for damages, including any general, special, incidental or consequential damages arising out of any use of this text (including, but not limited to, loss of data, data being rendered inaccurate, or losses sustained by anyone as a result of using this text).

Può darsi che altri vedano le cose in modo diverso, ma mi auguro che il principio ispiratore venga condiviso e che il testo non contenga troppi bugs. Il manuale SuSE recita: Questo testo è frutto di molte ore di lavoro. Inevitabilmente vi saranno passaggi buoni e meno buoni. Anche se sono riuscito ad eliminare parecchi errori, non vorrei dare l'impressione di aver creato un prodotto finito. Gli errori grossi sono solo nascosti meglio ;-) e forse mi offerta data la possibilità di fare meglio la prossima volta. Pertanto correzioni, miglioramenti, consigli, critiche e lodi sono sempre benvenuti.

Dr. Thomas Hertweck