The Linux Kernel Module Programming Guide

Sommario

  1. Prefazione
  2. Introduzione
  3. Ciao Mondo
  4. Fasi preliminari
  5. Comportamento dei driver dei device
  6. Il filesystem /proc
  7. Usare /proc per l'input
  8. Comunicare con i file dei device
  9. Chiamate di sistema
  10. Bloccare i processi
  11. Rimpiazzare printk
  12. Pianificare compiti
  13. I gestori degli interrupt
  14. Multi-processing simmetrico
  15. Insidie comuni
  16. Appendice A
  17. Appendice B


Hello, World (parte 1): Il modulo più semplice

Quando il primo programmatore delle caverne scolpì il primo programma sul muro del primo computer delle caverne, fu un programma che disegnava la stringa "Hello, world" con i graffiti. I libri di testo romani sulla programmazione iniziano con il programma "Salut, Mundi". Non so cosa possa succedere alle persone che rompono questa tradizione, ma penso sia più sicuro non scoprirlo. Inizieremo con una serie di programmi "hello world" che mostrano vari aspetti delle conoscenze di base nella scrittura di moduli del kernel.

Qui vediamo il modulo più semplice possibile. Non compilarlo ancora; analizzeremo la compilazione di un modulo nella prossima sezione.

Esempio 2-1. hello-1.c

/*  
 *  hello-1.c - Il modulo più semplice.
 */
#include <linux/module.h>	/* Necessario a tutti i moduli */
#include <linux/kernel.h>	/* Necessario per la macro KERN_INFO */

int init_module(void)
{
	printk(KERN_INFO "Hello world 1.\n");

	/* 
	 * Un valore di ritorno diverso da 0 indica che init_module è fallita; il modulo non può essere caricato
	 */
	return 0;
}

void cleanup_module(void)
{
	printk(KERN_INFO "Goodbye world 1.\n");
}

I moduli del kernel devono avere almeno due funzioni. Una funzione "iniziale" (inizializzazione) detta init_module() che è chiamata quando il modulo è inserito nel kernel tramite insmod, e una funzione "finale" (di pulizia) detta cleanup_module() che è chiamata prima che il modulo venga rimosso con rmmoded. In realtà, le cose sono cambiate a partire dal kernel 2.3.13. Puoi utilizzare qualunque nome ti piaccia per la funzione di inizializzazione e di cleanup, e imparerai a farlo nella Sezione XXXX2.3XXXX. Infatti, il nuovo metodo è quello preferito. Comunque, molti continuano ad utilizzare init_module() e cleanup_module() per le proprie funzioni iniziali e finali.

Tipicamente, init_module() o registra un handler con il kernel per fare una serie di azioni, oppure rimpiazza una della funzioni del kernel stesso con un codice proprio (usualmente questo codice fa una serie di azioni e poi chiama la funzione originale). Si suppone che la funzione cleanup_module() disfaccia tutto ciò che ha fatto init_module(), in modo tale che il modulo possa essere rimosso tranquillamente.

Infine, ogni modulo del kernel ha bisogno di includere linux/module.h. Nell'esempio abbiamo dovuto includere linux/kernel.h solo per la macro KERN_ALERT utilizzata per definire il livello di log della printk(), che imparerai nella Sezione XXXX.

Introduzione a printk()

Nonostante quello che si potrebbe pensare, printk() non aveva lo scopo di comunicare informazioni all'utente, anche se l'abbiamo utilizzato esattamente per questo in hello-1! E' stato pensato, invece, per essere un meccanismo di logging per il kernel, ed è utilizzato per loggare informazioni e mostrare dei warning. Perciò, ogni dichiarazione di printk() viene fornita di un livello di priorità, che, nell'esempio, è la KERN_ALERT (<1>). Ci sono 8 priorità e il kernel possiede delle macro per queste, quindi non c'è il bisogno di utilizzare numeri criptici. Si possono vedere le macro (e il loro significato) nel file linux/kernel.h. Se non si specifica un livello di priorità, verrà utilizzata quella di default: DEFAULT_MESSAGE_LOGLEVEL.

E' importante prestare una certa attenzione nel leggere le macro di priorità. L'header file descrive anche cosa significa ogni priorità. In pratica, è bene non utilizzare i numeri, come <4>. Si utilizzino sempre le macro, come KERN_WARNING.

Se la priorità è minore di int console_loglevel, il messaggio è stampato sul proprio terminale corrente. Se sia syslogd che klogd stanno girando, allora il messaggio verrà inserito in coda a /var/log/messages, indipendentemente dal fatto che venga stampato sulla console. Nell'esempio abbiamo utilizzato una priorità alta, come KERN_ALERT, per essere sicuri che il messaggio di printk() venga stampato sulla console piuttosto che inserito nel file di log. Quando si scrivono moduli reali, si utilizzano le priorità che meglio si adattano alla situazione in esame.

Compilare i moduli del kernel

I moduli del kernel devono essere compilati un po' differentemente dalle applicazioni userspace regolari. Le vecchie versioni del kernel richiedevano molta attenzione riguardo queste impostazioni, che sono, di solito, memorizzare nei Makefiles.

Hello, World (parte 2)