Linux Kernel Module – A [little] programming guide

penguinVolete scrivere un modulo per il kernel di linux ? Conoscete il linguaggio C ? Benissimo, allora armatevi e partite. Considerate questa piccola guida, un punto di partenza per la creazione di un piccolo modulo per il kernel di linux.
Prima di partire cerchiamo di capire cos’è un modulo del kernel. I moduli in generale sono dei piccoli programmi che possono essere montati e smontati run-time dal kernel ogni qual volta si presenta la necessità. Ad esempio un modulo potrebbe essere quello per un dispositivo hardware, stiamo parlando quindi di un driver di dispositivo, il quale dopo essere stato caricato permetterà l’utilizzo del dispositivo stesso. I moduli quindi estendono la funzionalità del kernel senza necessità che si effettui un reboot del sistema.
Senza moduli si avrebbero i cosiddetti kernel monolitici, ovvero blocchi di kernel che inglobano tutte le funzionalità le quali verrebberò caricate tutte assieme al momento del boot del sistema.

In linux per elencare la lista dei moduli caricati dal kernel è sufficiente eseguire il seguente comando: 
 

mulp@devme-station:~$ lsmod
Module                  Size  Used by
binfmt_misc             7960  1 
snd_hda_codec_analog    78702  1 
snd_hda_intel          25677  3 
snd_hda_codec          85759  2 snd_hda_codec_analog,snd_hda_intel
snd_hwdep               6924  1 snd_hda_codec
snd_pcm_oss            41394  0 
........
........

Sopra vediamo un esempio di lista dei moduli caricati attualmente dal kernel sulla mia macchina.

Ma come vengono caricati i moduli all’interno del kernel ? Quando il kernel ha bisogno di una funzionalità che non è stata finora caricata, si rifà al demone kmod il quale esegue il comando modprobe che si occupa del caricamento. modprobe seleziona il modulo da caricare indicandone il nome oppure un identificatore generico:

  • nome del modulo: devme-driver
  • identificativo generico: char-major-10-20

Nel caso dell’identificatore generico viene consultato il file /etc/modules.conf all’interno del quale sono definiti gli alias per ogni modulo, in questo caso dato l’identificativo precedente, il kernel farà riferimento al modulo chiamato devme-driver.o. Successivamente viene consultato il file /lib/modules/version/modules.dep per verificare se il modulo ha delle dipendenze rispetto ad altri moduli, e nel caso in cui li abbia, vengono caricate prima del modulo stesso. Questo file è creato attraverso il comando depmod -a.  Un esempio chiarificatore è dato dalla dipendenza del modulo msdos.o dal modulo fat.o, in questo caso la sequenza del caricamento sarà:

mulp@devme-station:~$ insmod /lib/modules/2.5.1/kernel/fs/fat/fat.o
mulp@devme-station:~$ insmod /lib/modules/2.5.1/kernel/fs/msdos/msdos.o

oppure

mulp@devme-station:~$ modprobe -a msdos

Passiamo quindi ad un piccolo esempio pratico di scrittura del nostro modulo. Ovviamente si tratta di un modulo esemplificativo che illustra come fare a scriverne degli altri molto più complessi, il nostro si limiterà ad illustrare i passi da svolgere per arrivare alla creazione. Quello che segue quindi è il più semplice modulo del kernel possibile: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 *  devme-module-t1.c - Il più semplice modulo
 **/
#include <linux/module.h>  /* Needed by all modules */
#include <linux/kernel.h>  /* Needed for KERN_ALERT */
 
 
int init_module(void) {
   printk("<1>Hello, I'm DevMe Kernel Module.\n");
 
   // A non 0 return means init_module failed; module can't be loaded.
   return 0;
}
 
 
void cleanup_module(void) {
  printk(KERN_ALERT "It was a pleasure work with U.\n");
}

Il codice di sopra è molto semplice e di facile comprensione. Come potete vedere ci sono alcuni metodi che devono essere implementati affinché il modulo possa essere creato correttamente. L’unica cosa degna di nota è l’utilizzo della funzione printk che non stampa nulla in output come potrebbe suggerire, ma è un meccanismo di loggin per il kernel. Il numero indicato tra parentesi acute rappresenta la priorità del log. Nel file linux/kernel.h sono elencate le varie priorità.

Affinché il modulo possa essere caricato è necessario compilarlo (ho scoperto l’acqua calda eh! :P ) , procediamo quindi con la creazione del nostro Makefile il quale renderà la procedura di installazione più pratica. Da precisare che con i kernel della serie 2.6 la procedura di compilazione dei kernel è leggermente cambiata, i moduli hanno estensione .ko e non .o come per la precedente serie 2.4. Anche la sintassi del Makefile è cambiata, ci sono alcune MACRO diverse da indicare durante la compilazione. Vediamo l’esempio che segue: 

1
2
3
4
5
6
obj-m += devme-module-t1.o
KVERSION = $(shell uname -r)
all:
	make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
	make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean

otteniamo quindi:

mulp@devme-station:~$ make
make -C /lib/modules/2.6.32-24-generic/build M=/home/mulp/blog/kernel modules
make[1]: ingresso nella directory &laquo;/usr/src/linux-headers-2.6.32-24-generic&raquo;
  CC [M]  /home/mulp/blog/kernel/devme-module-t1.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/mulp/blog/kernel/devme-module-t1.mod.o
  LD [M]  /home/mulp/blog/kernel/devme-module-t1.ko
make[1]: uscita dalla directory &laquo;/usr/src/linux-headers-2.6.32-24-generic&raquo;
 
mulp@devme-station:~$ modinfo devme-module-t1.ko 
filename:       devme-module-t1.ko
srcversion:     C8EB2BC02FE416B3877C877
depends:        
vermagic:       2.6.32.15+drm33.5 SMP mod_unload modversions

Non rimane quindi che inserire il modulo all’intero del kernel e vedere cosa succede. Eseguiamo il comando bash:

mulp@devme-station:~$ insmod devme-module-t1.ko

Tutti i moduli caricati all’interno del kernel sono presenti all’interno di /proc/modules, se non credete andate a fare cat di quel file e troverete il modulo appena inserito al suo interno. Dopo aver provato l’emozione della creazione di un modulo del kernel, procedete alla sua rimozione con il comando:

mulp@devme-station:~$ rmmod devme-module-t1

e andate a vedere nei file kern.log e syslog….troverete i log indicati nel file sorgente del modulo. L’esempio di scrittura di questo modulo è puramente didattico, la logica è praticamente inesistente ed esistono altri modi di ottimizzare le chiamate di init e cleanup di un modulo. Nei prossimi vedremo qualche altro esempio più complesso.

Stay tuned !

WordPress Themes