LKN: Personalizzare un Kernel
Torna all'indice: Linux Kernel in a Nutshell
Uno dei punti più delicati e difficili nella creazione della propria versione del kernel Linux è quello di determinare esattamente quali driver e quali opzioni di configurazione sono richiesti per il corretto funzionamento dalla macchina su cui viene installato. Questo capitolo guiderà il lettore attraverso questo processo di selezione e scelta dei driver corretti.
Usare un kernel di una distribuzione
Uno dei metodi più semplici, per determinare quali moduli siano necessari, è quello di partire dalla configurazione che viene installata dal pacchetto del kernel della distribuzione che si sta usando. È infatti molto più semplice determinare di quali driver si ha bisogno basandosi su quelli installati in un sistema in funzione, in cui i driver corretti sono già associati all'hardware in utilizzo.
Se invece si sta personalizzando un kernel per una macchina sulla quale non è installata una distribuzione Linux, allora conviene partire dalla versione LiveCD di una distribuzione. Questo consente all'utente di far partire Linux sulla macchina in oggetto e di determinare in maniera semplice le opzioni di configurazione del kernel e che consentono il funzionamento ottimale della macchina stessa.
Dove si trova la configurazione del kernel?
Quasi tutte le distribuzioni forniscono il file di configurazione nello stesso pacchetto del kernel. Si consiglia di leggere la documentazione relativa alla distribuzione stessa per sapere dove viene installato il file di configurazione. Solitamente si trova da qualche parte in una sotto-directory di /usr/src/linux/.
Nota per Debian (NdT) In debian il file di configurazione di ogni kernel installato si trova in /boot/ ed ha come nome config-versione. |
Se avete difficoltà a trovare la configurazione del kernel, allora guardate nel kernel stesso. I kernel di molte distribuzioni sono compilati in modo da includere il file di configurazione dentro il filesystem /proc. Per verificare se questo è il vostro caso, digitate:
$ ls /proc/config.gz /proc/config.gz
Se il file /proc/config.gz è presente, allora copiatelo nella directory del sorgente kernel ed estraetelo:
$ cp /proc/config.gz -/linux/ $ cd -/linux $ gzip -dv config.gz config.gz: 74.9% - - replaced with config
Copiate questo file di configurazione nella vostra directory del kernel e rinominatelo in .config. Ora potrete utilizzare questo file come base di partenza nella personalizzazione della configurazione del kernel così come descritto nel Capitolo 4.
Usando questo file di configurazione si dovrebbe ottenere sempre un file immagine del kernel (un kernel ricompilato, NdT) funzionante sulla propria macchina. Lo svantaggio di questa immagine è che verranno compilati quasi tutti i moduli e driver presenti nei sorgenti del kernel. Ciò non è quasi mai necessario per una singola macchina, quindi sarebbe meglio disabilitare tutti i driver e le opzioni non necessarie. Si raccomanda di disabilitare solo quelle opzioni che si è sicuri non serviranno, poiché ci sono parti del sistema che richiedono l'abilitazione di certe opzioni.
Determinare quali moduli siano necessari
Usando il file di configurazione fornito dalla vostra distribuzione il tempo richiesto per la compilazione del kernel è molto lungo poiché tutti i possibili driver vengono abilitati. Si dovrebbe cercare di abilitare solo i driver per l'hardware presente nel sistema, così da ridurre i tempi di compilazione del kernel. Inoltre, compilando staticamente (invece che come moduli) alcuni o tutti i driver necessari, si riduce la memoria utilizzata ed in alcune architetture si velocizza il funzionamento del sistema. Per escludere i driver dal kernel è necessario però determinare quali moduli sono indispensabili per il funzionamento dell'hardware installato. Attraverso l'utilizzo di due esempi, cercheremo di spiegare come determinare quali driver siano indispensabili al controllo dell'hardware.
Le informazioni che mettono in relazione i dispositivi ai driver presenti nel kernel sono conservate in varie parti del sistema. Uno dei posti più importanti dove sono salvate queste informazioni è il filesystem virtuale sysfs. All'avvio di Linux, sysfs, dovrebbe essere montato dagli script di inizializzazione della vostra distribuzione nella directory /sys. sysfs consente di dare un'occhiata a come le varie parti del kernel sono legate l'una a l'altra, questo lo si deduce grazie ai vari collegamenti simbolici (symlink NdT) che puntano all'interno dell'intero filesystem.
In tutti gli esempi di seguito, saranno riportati i veri percorsi (path NdT) di sysfs corrispondenti ad hardware specifico. La vostra macchina sarà certamente diversa, ma la posizione relativa delle informazioni sarà la stessa. Non ci si deve allarmare se i nomi di file nel sysfs non sono i medesimi, ciò è normale e prevedibile.
Inoltre, la struttura interna del file di sistema sysfs subisce modifiche, a causa sia della riorganizzazione dei driver sia del fatto che gli sviluppatori del kernel trovano nuovi modi per meglio presentare in user space le strutture interne del kernel. A causa di questo, col tempo, alcuni dei symlink, precedentemente menzionati in questo capitolo, possono non essere presenti. Tuttavia, le informazioni sono ancora tutte presenti, al massimo sono state un po' spostate.
Esempio: Come determinare il driver di rete
Uno degli elementi più comuni ed importanti in un sistema è la scheda di rete. È essenziale capire quale driver la controlla ed attivarlo nella configurazione in maniera da consentire un corretto funzionamento delle connessioni di rete.
Primo: partendo dalle connessioni di rete si risale al device PCI
$ ls /sys/class/net/ eth0 eth1 eth2 lo
La directory lo rappresenta il dispositivo di rete loopback, ed non è dipendente da nessun dispositivo di rete realmente installato. Invece si dovrebbe riservare particolare attenzione alle directory eth0, eth1 e eth2, dato che si riferiscono a dispositivi realmente esistenti
Per determinare di quali dispositivi ci si deve occupare, si utilizza il comando ifconfig:
$ /sbin/ifconfig -a eth0 Link encap:Ethernet HWaddr 00:12:3F:65:7D:C2 inet addr:192.168.0.13 Bcast:192.168.0.255 Mask:255.255.255.0 UP BROADCAST NOTRAILERS RUNNING MULTICAST MTU:1500 Metric:1 RX packets:2720792 errors:0 dropped:0 overruns:0 frame:0 TX packets:1815488 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:100 RX bytes:3103826486 (2960.0 Mb) TX bytes:371424066 (354.2 Mb) Base address:0xdcc0 Memory:dfee0000-dff00000 eth1 Link encap:UNSPEC HWaddr 80-65-00-12-7D-C2-3F-00-00-00-00-00-00-00-00 BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) eth2 Link encap:UNSPEC HWaddr 00-02-3C-04-11-09-D2-BA-00-00-00-00-00-00-00 BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) lo Link encap:Local Lookback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:60 errors:0 dropped:0 overruns:0 frame:0 TX packets:60 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:13409 (13.0 Kb) TX bytes:13409 (13.0 Kb)
Da questo listato si può riconoscere nel dispositivo di rete eth0, quello attivo e funzionante, infatti nelle righe;
eth0 Link encap:Ethernet HWaddr 00:12:3F:65:7D:C2 inet addr:192.168.0.13 Bcast:192.168.0.255 Mask:255.255.255.0
Questo risultato dimostra che il dispositivo Ethernet si vede assegnato un indirizzo IP valido (inet).
Ora, dopo che abbiamo individuato il dispositivo eth0 e ci siamo accertati di volerlo abilitare nel nostro nuovo kernel, dobbiamo individuare quale driver lo controlla. Ciò si realizza con una semplice procedura, che è quella si seguire i link nel filesystem sysfs, basta digitare un comando di una sola riga:
$ basename `readlink /sys/class/net/eth0/device/driver/module` e1000
Il risultato mostra che il modulo e1000 controlla il dispositivo di rete eth0. Il comando basename racchiude in un'unica linea di comando i seguenti passaggi:
- 1. Individua il symlink /sys/class/net/eth0/device contenuto all'interno della directory /sys/device/, la quale contiene le informazioni relative al dispositivo che controlla eth0. Fate attenzione al fatto che nelle nuove versioni del kernel la directory /sys/class/net/eth0 potrebbe essere un symlink.
- 2. All'interno della directory che descrive il dispositivo in sysfs, c'è un symlink che punta al driver relativo a questo dispositivo. Questo symlink è nominato driver, pertanto si segue questo collegamento.
- 3. All'interno della directory che descrive il driver in sysfs, c'è un symlink che punta al modulo che si trova all'interno del driver in oggetto. Questo symlink è chiamato module. Noi cerchiamo l'oggetto a cui punta questo symlink, per ottenerlo ci serviamo del comando readlink, il quale produce un risultato simile a questo:
$ readlink /sys/class/net/eth0/device/driver/module
../../../../module/e1000
- 4. Dato che a noi interessa solo il nome del modulo e ci disinteressiamo del resto del risultato ottenuto con il comando readlink, tenendo solo la parte più a destra del risultato. Questo è appunto ciò che il comando basename realizza. Applicandolo direttamente all'intero percorso, questo comando ci ritorna quanto segue:
$ basename ../../../../module/e1000
e1000
Così abbiamo inserito il lungo risultato del symlink, ottenuto da readlink, quale parametro nel programma basemane, permettendo così l'intero processo di essere realizzato in una sola riga.
Ora che abbiamo identificato il nome del modulo, si dovrebbe trovare l'opzione della configurazione del kernel che lo controlla. Si può cercare nei vari menu di configurazione dei dispositivi di rete oppure cercare nel codice sorgente del kernel stesso per essere sicuri di avere l'opzione giusta.
$ cd ~/linux/linux-2.6.17.8 $ find -type f -name Makefile | xargs grep e1000 ./drivers/net/Makefile:obj-$(CONFIG_E1000) += e1000/ ./drivers/net/e1000/Makefile:obj-$(CONFIG_E1000) += e1000.o ./drivers/net/e1000/Makefile:e1000-objs := e1000_main.o e1000_hw.o e1000_ethtool.o e1000_param.o
Si precisa che "e1000, usato in questo esempio, deve essere sostituito con il nome del modulo che state analizzando.
La cosa che ci interessa nel risultato del precedente comando find sono le righe dove compaia il termine CONFIG_. Questa è l'opzione di configurazione che il kernel deve aver attivato per poter compilare il modulo. Nell'esempio precedente l'opzione di configurazione che c'interessa è pertanto CONFIG_E1000.
Adesso si dispone dell'informazione necessaria per poter configurare il kernel. Si esegue lo strumento menu di configurazione:
$ make menuconfig
Dopodiché si prema il tasto / (slash) (che ha il compito di far partire una ricerca), e si digita l'opzione di configurazione, senza la parte di testo CONFIG_. Questo processo è mostrato nella figura 7-1.
Il sistema di configurazione del kernel vi dirà ora esattamente dove selezionare l'opzione per abilitare questo modulo. Vedi figura 7-2.
Il primo elemento nella schermata mostra l'opzione che stavate cercando. Le informazioni mostrate dalla schermata vi dicono che, per attivare il modulo E1000 nel kernel, la seguente opzione di configurazione deve essere abilitata:
Device Drivers Network device support [*] Network device support Ethernet (1000 Mbit) [*] Intel(R) PRO/1000 Gigabit Ethernet support
Questo modo di procedere funziona per ogni tipo di dispositivo attivo nel kernel.
Esempio: Un dispositivo USB
Come secondo esempio, esaminiamo ora un convertitore USB-seriale che è presente nel nostro sistema preso ad esempio. Attualmente il convertitore è collegato alla porta /dev/ttyUSB0, pertanto si deve prendere in esame la sezione tty del sysfs.
$ ls /sys/class/tty/ | grep USB ttyUSB0
Potete ora eseguire una ricerca di questo dispositivo nel sysfs allo scopo di trovare il modulo che lo gestisce, utilizzando la stessa procedura mostrata nella sezione precedente:
$ basename `readlink /sys/class/tty/ttyUSB0/device/driver/module` pl2303
Dopodiché, per poter individuare l'opzione di configurazione che si deve abilitare, si cerca nell'albero del codice sorgente del kernel:
$ cd ~/linux/linux-2.6.17.8 $ find -type f -name Makefile | xargs grep pl2303 ./drivers/usb/serial/Makefile:obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o
Si utilizzi lo strumento di configurazione del kernel, come indicato in figura 7-3, per trovare l'opzione adeguata da abilitare relativa al settaggio dell'opzione CONFIG_USB_SERIAL_PL2303.
Nel nostro caso il risultato è mostrato nella figura 7-4.
Ciò mostra esattamente dove trovare l'opzione USB Profilic 2303 Single Port Serial Driver che è necessaria alla corretta gestione di questo dispositivo.
Riassunto: Alla scoperta del dispositivo
Riassumendo, ecco i vari passaggi che servono per identificare il driver funzionante di un dispositivo ad esso collegato:
- 1. Trovate la corretta classe di dispositivi in sysfs relativa al dispositivo che ci interessa. I dispositivi di rete sono elencati in /sys/class/net, mentre i dispositivi tty sono elencati in /sys/class/tty. Gli altri vari dispositivi si trovano in altre sotto-directory di /sys/class, a seconda del tipo.
- 2. Ricercate nell'albero di sysfs il nome del modulo che controlla il dispositivo in oggetto. Lo si trova in /sys/class/class_name/device_name/device/driver/module, la ricerca è agevolata se si utilizzano i comandi readlink e basename.
$ basename `readlink /sys/class/class_name/device_name/device/driver/module`
- 3. Ricercate nei file Makefile con find e grep le opzioni CONFIG_ che abilitano il modulo
$ find -type f -name Makefile | xargs grep ''module_name''
- 4. Ricercate l'opzione trovata nel sistema di configurazione del kernel, dopodiché andate dove indicato dal menu per attivare il driver in oggetto.
Lasciamo che il kernel ci dica ciò di cui abbiamo bisogno
Dopo esserci infilati nel sysfs e aver seguito i sui symlinks per ricercare passo passo i nomi dei moduli, presentiamo un semplice script che farà per noi tutto il lavoro in un modo leggermente diverso:
#!/bin/bash # # find_all_modules.sh # for i in `find /sys/ -name modalias -exec cat {} \;`; do /sbin/modprobe --config /dev/null --show-depends $1 ; done | rev | cut -f i -d '/' | rev | sort -u
Si può scaricare un file d'esempio, contenente questo script, dal sito web del libro, riportato nella sezione Come contattarci che si trova nella prefazione.
Questo script cerca nel sysfs tutti file chiamati modalias. Il file modalias contiene gli alias dei moduli e comunica al comando modprobe quali moduli debbano essere caricati per ogni dispositivo. L'alias del modulo è composto da una combinazione di: produttore del dispositivo, ID, tipo di classe ed altri identificativi univoci per il tipo di dispositivo in questione. Tutti i moduli del driver del kernel hanno una lista interna dei dispositivi che supportano, che è generata automaticamente dalla lista dei dispositivi che il driver comunica al kernel di poter supportare. Il comando modprobe ricerca tutti i dispositivi nella lista di tutti i driver e cerca di trovare una corrispondenza confrontando l'alias. Se trova una corrispondenza, allora provvede al caricamento del modulo (questa procedura è la stessa seguita dal caricamento automatico dei driver in Linux).
Lo script prevede l'arresto del programma modprobe prima di caricare il modulo, e visualizza a schermo solo le azioni che eseguirebbe. Questo ci dà una lista di tutti i moduli che sono necessari al controllo di tutti i dispositivi del sistema. Eseguendo una piccola pulizia della lista, ordinandola e selezionando i campi adeguati, otteniamo il seguente risultato:
$ find_all_modules.sh 8139cp.ko 8139too.koo ehci-hcd.ko fimware_vlass.ko i2c-i801.ko ieee80211.ko ieee80211_crypt.ko ipw2200.ko mii.ko mmc_core.ko pcmcia_core.ko rsrc_nonstatic.ko sdhci.ko snd-hda-codec.ko snd-hda-intel.ko snd-page-alloc.ko snd-pmc.ko snd-timer.ko snd.ko soundcore.ko uhci-hcd.ko usbcore.ko yenta_socket.ko
Questa è la lista di tutti i moduli che sono necessari alla gestione dell'hardware della macchina.
Lo script mostrerà probabilmente alcuni messaggi di errore che possono essere del tipo:
FATAL: Module pci:v00008086d00002592sv000010CFsd000012E0bc03sc00i00 not found. FATAL: Module serio:ty01pr00id00ex00 not found.
Questo ci dice che non si trova un modulo che gestisce quel dispositivo. Questo non deve comunque interessare più di tanto, poiché alcuni dispositivi non hanno driver nel kernel che lavorino per loro.
Determinare il modulo corretto partendo da zero
Talvolta non c'è la possibilità di avere un kernel funzionante su una macchina in modo da determinare quali moduli del kernel siano necessari per gestire l'hardware. Oppure si è aggiunto del nuovo hardware al sistema e bisogna trovare le opzioni della configurazione necessarie a farlo funzionare correttamente. Questa sezione illustrerà come determinare le opzioni di configurazione necessarie ad far funzionare l'hardware.
Il modo più semplice per capire quale driver controlla un nuovo dispositivo è quello di compilare come moduli tutti i driver di quel tipo disponibili nei sorgenti del kernel, e lasciare che il processo di avvio tramite udev associ il driver al dispositivo. Una volta fatto ciò, si dovrebbe essere in grado di risalire al driver necessario seguendo i passi descritti precedentemente, ed infine ricompilare il kernel abilitando il solo driver necessario.
Se invece non si vogliono compilare tutti i driver, o questo meccanismo non funziona per qualche motivo, sarà necessario un pò più di lavoro per individuare il driver necessario. I passi successivi sono complessi e richiedono talvolta di dover cercare nei sorgenti del kernel. Non abbiate timore di ciò, sarà solo di aiuto a comprendere meglio l'hardware ed i sorgenti del kernel.
I passi necessari per trovare il driver corrispondente di un dispositivo cambiano a seconda del tipo di dispositivo in questione. In questo capitolo discuteremo le due tipologie di dispositivi più comuni: PCI e USB. I metodi descritti qui saranno validi anche per altri tipi di dispositivi.
È inoltre molto importante per il kernel di essere in grado di trovare tutti i filesystem presenti nel sistema, ed in particolare il filesystem di root. Approfondiremop questo aspetto successivamente in "Root filesystem".
Dispositivi PCI
I dispositivi PCI si distinguono per vendor ID e device ID; ogni combinazione di vendor ID e di device ID può richiedere un driver unico. Questa è la base per la ricerca mostrata in questa sezione.
Per questo esempio useremo un scheda di rete PCI che supporremo non funzionante con l'attuare versione del kernele in esecuzione. Questo esempio sarà diverso dalla vostra situazione, con differenti device PCI e valori ID del bus, ma i passi salienti dovrebbero essere rilevanti per ogni tipo di dispositivo PCI per il quale vogliate trovare un driver funzionante.
In primo luogo troviamo nel sistema il dispositivo PCI che non stà funzionando. Per ottenere una lista di tutti i dispositivi PCI usiamo il programma lspci
. Poiché a noi interessano solo dispositivi PCI ethernet restringeremo la nostra ricerca filtrado tra i risultati solo quelli che conterranno la parola ethernet (case-insensitive):
$ /usr/sbin/lspci | grep -i ethernet 06:04.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/ 8139C/8139C+ (rev 10)
Questo è il dispositivo che vorremmo fare funzionare.
- Quasi tutte le distribuzioni mettono il programma lspci in /usr/sbin/, ma alcune lo mettono in altri percorsi. Per trovare in quale posizione è stato messo digitare:
- $ which lspci
- /usr/sbin/lspci
Se state usando una distribuzione che mette lspci in una altra posizione usate il percorso corretto per il vostro caso negli esempi seguenti.
Le primi bit d'informazione che l'output di lspci ci mostra sono l'ID del bus PCI per questo dispositivo, 06:04.0. Questo è il valore che useremo quando guarderemo nel sysfs per trovare più informazioni riguardo questo dispositivo.
Andiamo in sysfs dove tutti i dispositivi PCI sono elencati, e guardiamo i loro nomi:
$ cd /sys/bus/pci/devices/ $ ls 0000:00:00.0 0000:00:1d.0 0000:00:1e.0 0000:00:1f.3 0000:06:03.3 0000:00:02.0 0000:00:1d.1 0000:00:1f.0 0000:06:03.0 0000:06:03.4 0000:00:02.1 0000:00:1d.2 0000:00:1f.1 0000:06:03.1 0000:06:04.0 0000:00:1b.0 0000:00:1d.7 0000:00:1f.2 0000:06:03.2 0000:06:05.0
Il kernel numera i dispositivi PCI con un 0000: iniziale che non viene mostrato nell'output di lspci. Dunque, aggiungiamo un 0000: al numero datoci da lspci e entriamo in quella directory:
$ cd 0000:06:04.0
In questa directory vogliamo conoscere il valori dei file vendor e device.
$ cat vendor 0x10ec $ cat device 0x8139
Questi sono il vednor ID ed il device ID per questo dispositivo PCI. Il kernel usa questi valori per associare correttamente un driver ad un dispositivo.
Dispositivi USB
Root filesystem
Controller del disco
Un aiuto dallo script
This is an indipendent translation of the book Linux Kernel in a Nutshell by Greg Kroah-Hartman. This translation (like the original work) is available under the terms of Creative Commons Attribution-ShareAlike 2.5.