Hello world in kernel space
Ora che avete capito come compilare un modulo per inserirlo nel kernel, vediamo come è fatto lo scheletro di un driver. Partiamo dal comune hello world:
Codice:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
static int __init hello_init(void)
{
printk(KERN_INFO "Hello world!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ilsensine");
MODULE_DESCRIPTION("hello-world module");
Esaminiamo in dettaglio le varie righe:
Codice:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
Gli include sono ricercati nella directory "include/" dei sorgenti. Dentro questa esistono, tra tutte, due sottodirectory principali:
linux/ contenente gli header piattaforma-indipendenti del kernel
asm/ contenente gli header piattaforma-dipendenti
Per nessun motivo un driver può includere gli header userspace, ovviamente.
I tre #include inseriti sono il minimo necessario per un modulo.
Quote:
static int __init hello_init(void)
static void __exit hello_exit(void)
|
Queste due funzioni sono invocate al caricamento e allo scaricamento del driver. La funzione di caricamento può fallire, ritornando con un valore negativo l'errore avvenuto (ad es. -ENOMEM, -ENODEV...). La seconda funzione non può mai fallire: notate però che lo scaricamento di un driver è consentito solo se non esistono reference count (quindi "utenti") del driver. Il controllo lo fa automaticamente il kernel, quindi non dovete preoccuparvene.
Le macro __init ed __exit servono ad indicare che le funzioni hanno validità solo in fase di inizializzazione o di finalizzazione: quindi ad esempio la funzione hello_init può essere scartata dopo l'inizializzazione del modulo (liberando così un pò di memoria); la hello_exit invece può non essere proprio compilata se inserite il vostro driver staticamente nel kernel.
Codice:
printk(KERN_INFO "Hello world!\n");
Questa funzione è equivalente alla funzione printf in user space. Tuttro ciò che stampa finisce nel kernel log, e può essere visualizzato tramite dmesg.
La macro (opzionale) KERN_INFO indica il livello di priorità del messaggio; esistono 8 livelli di priorità, definiti in kernel.h.
Codice:
module_init(hello_init);
module_exit(hello_exit);
Tramite queste macro informiamo il kernel sui nomi delle nostre funzioni di inizializzazione e finalizzazione.
Codice:
MODULE_AUTHOR("ilsensine");
MODULE_DESCRIPTION("hello-world module");
Informazioni opzionali descrittive. Vengono visualizzate se eseguite un modinfo sul vostro driver.
Codice:
MODULE_LICENSE("GPL");
Non avete intenzione di "tainteggiare" il vostro kernel, vero?
Notate inoltre che alcune funzionalità del kernel sono disponibili solo ai driver con licenza GPL.