LKMPG: Ciao Mondo: differenze tra le versioni

nuova categoria
(nuova categoria)
 
(12 versioni intermedie di 3 utenti non mostrate)
Riga 1: Riga 1:
{{Template:LKMPG}}
{{Template:LKMPG}}
==Ciao, Mondo (parte 1): Il modulo più semplice==
==Ciao, Mondo (parte 1): Il modulo più semplice==


Riga 119: Riga 118:
==Ciao, Mondo (parte 2)==
==Ciao, Mondo (parte 2)==


Dalla versione 2.4 del kernel Linux, si possono rinominare le funzioni di init e cleanup dei propri moduli; infatti non devono essere chiamate per forza rispettivamente <code>init_module()</code> e <code>cleanup_module</code>. Questo è reso possibile della macro <code>module_init()</code> e module_exit()</code>. Queste macro sono definite in <code>linux/init.h</code>. L'unica attenzione da avere è quella di definire le funzioni di init e cleanup prima di chiamare le macro, pena errori di compilazione. Vediamo un esempio di questa tecnica:
Dalla versione 2.4 del kernel Linux, si possono rinominare le funzioni di init e cleanup dei propri moduli; infatti non devono essere chiamate per forza rispettivamente <code>init_module()</code> e <code>cleanup_module</code>. Questo è reso possibile della macro <code>module_init()</code> e <code>module_exit()</code>. Queste macro sono definite in <code>linux/init.h</code>. L'unica attenzione da avere è quella di definire le funzioni di init e cleanup prima di chiamare le macro, pena errori di compilazione. Vediamo un esempio di questa tecnica:


'''Esempio 3-3. hello-2.c'''
'''Esempio 3-3. hello-2.c'''
Riga 300: Riga 299:


==Passare argomenti ad un modulo da linea di comando==
==Passare argomenti ad un modulo da linea di comando==
I moduli possono prendere argomenti da linea di comando, ma non tramite argc/argv come potresti essere abituato a fare.
Per passare argomenti al tuo modulo, bisogna dichiarare una variabile globale che assumerà il valore passato da linea di comando e usare la macro <code>module_param()</code> (definita in <code>linux/moduleparam.h</code>) per impostare il meccanismo d'associazione. A runtime, insmod copierà l'argomento da linea di comando passato al modulo nella variabile globale dichiarata come per esempio '''./insmod mymodule.ko myvariable=5'''. La dichiarazione della variabile e le macro dovrebbero essere poste all'inizio del modulo, per chiarezza. L'esempio dovrebbe chiarire la mia pessima spiegazione.
La macro <code>module_param()</code> prende 3 argomenti: il nome della variabile, il suo tipo e i permessi per il corrispondente file in sysfs. Le variabili di tipo integer possono essere signed (come al solito) o unsigned. Se preferisci usare array di interi o stringhe, dai uno sguardo a <code>module_param_array()</code> e <code>module_param_string()</code>.
<pre>
int myint = 3;
module_param(myint, int, 0);
</pre>
Anche gli array sono supportati, ma le cose sono leggermente differenti ora rispetto a come lo erano nei giorni del kernel 2.4. Per tenere traccia dei numeri di parametri è necessario passare un puntatore ad una variabile di conteggio come terzo parametro. A tua scelta, puoi ignorare il conteggio e passare NULL invece. Noi mostriamo entrambe le possibilità qui:
<pre>
int myintarray[2];
module_param_array(myintarray, int, NULL, 0); /* not interested in count */
int myshortarray[4];
int count;
module_parm_array(myshortarray, short, , 0); /* put count into "count" variable */
</pre>
Un buon utilizzo è di avere un valore di default per la variabile settato, come una porta o un indirizzo IO. Se le variabili contengono il valore di default, allora eseguire l'autoriconoscimento (spiegato da qualche parte). Altrimenti, mantenere il valore corrente. Tutto questo sarà chiarito in seguito.
Infine, c'e' una maco, <code>MODULE_PARM_DESC()</code> che è usata per documentare gli argomenti che il modulo accetta. La macro prende due parametri: un nome di variabile e una descrizione della variabile.


'''Esempio 2-7. hello-5.c'''
<pre>
/*
*  hello-5.c - Demonstrates command line argument passing to a module.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/stat.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peter Jay Salzman");
static short int myshort = 1;
static int myint = 420;
static long int mylong = 9999;
static char *mystring = "blah";
static int myintArray[2] = { -1, -1 };
static int arr_argc = 0;
/*
* module_param(foo, int, 0000)
* The first param is the parameters name
* The second param is it's data type
* The final argument is the permissions bits,
* for exposing parameters in sysfs (if non-zero) at a later stage.
*/
module_param(myshort, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(myshort, "A short integer");
module_param(myint, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(myint, "An integer");
module_param(mylong, long, S_IRUSR);
MODULE_PARM_DESC(mylong, "A long integer");
module_param(mystring, charp, 0000);
MODULE_PARM_DESC(mystring, "A character string");
/*
* module_param_array(name, type, num, perm);
* The first param is the parameter's (in this case the array's) name
* The second param is the data type of the elements of the array
* The third argument is a pointer to the variable that will store the number
* of elements of the array initialized by the user at module loading time
* The fourth argument is the permission bits
*/
module_param_array(myintArray, int, &arr_argc, 0000);
MODULE_PARM_DESC(myintArray, "An array of integers");
static int __init hello_5_init(void)
{
int i;
printk(KERN_INFO "Hello, world 5\n=============\n");
printk(KERN_INFO "myshort is a short integer: %hd\n", myshort);
printk(KERN_INFO "myint is an integer: %d\n", myint);
printk(KERN_INFO "mylong is a long integer: %ld\n", mylong);
printk(KERN_INFO "mystring is a string: %s\n", mystring);
for (i = 0; i < (sizeof myintArray / sizeof (int)); i++)
{
printk(KERN_INFO "myintArray[%d] = %d\n", i, myintArray[i]);
}
printk(KERN_INFO "got %d arguments for myintArray.\n", arr_argc);
return 0;
}
static void __exit hello_5_exit(void)
{
printk(KERN_INFO "Goodbye, world 5\n");
}
module_init(hello_5_init);
module_exit(hello_5_exit);
</pre>
Raccomando di giocare con questo codice:
<pre>
satan# insmod hello-5.ko mystring="bebop" mybyte=255 myintArray=-1
mybyte is an 8 bit integer: 255
myshort is a short integer: 1
myint is an integer: 20
mylong is a long integer: 9999
mystring is a string: bebop
myintArray is -1 and 420
satan# rmmod hello-5
Goodbye, world 5
satan# insmod hello-5.ko mystring="supercalifragilisticexpialidocious" \
> mybyte=256 myintArray=-1,-1
mybyte is an 8 bit integer: 0
myshort is a short integer: 1
myint is an integer: 20
mylong is a long integer: 9999
mystring is a string: supercalifragilisticexpialidocious
myintArray is -1 and -1
satan# rmmod hello-5
Goodbye, world 5
satan# insmod hello-5.ko mylong=hello
hello-5.o: invalid argument syntax for mylong: 'h'
</pre>


==Moduli divisi in piu file==
==Moduli divisi in piu file==
A volte ha senso dividere un modulo del kernel in vari codici sorgenti.
Ecco un esempio di un modulo del genere:
'''Esempio 2-8. start.c'''
<pre>
/*
*  start.c - Illustration of multi filed modules
*/
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
int init_module(void)
{
printk(KERN_INFO "Hello, world - this is the kernel speaking\n");
return 0;
}
</pre>
Il prossimo file:
'''Esempio 2-9. stop.c'''
<pre>
/*
*  stop.c - Illustration of multi filed modules
*/
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module  */
void cleanup_module()
{
printk(KERN_INFO "Short is the life of a kernel module\n");
}
</pre>
E, infine, il makefile:
'''Esempio 2-10. Makefile'''
<pre>
obj-m += hello-1.o
obj-m += hello-2.o
obj-m += hello-3.o
obj-m += hello-4.o
obj-m += hello-5.o
obj-m += startstop.o
startstop-objs := start.o stop.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
</pre>
Questo è il makefile completo per tutti gli esempio che abbiamo appena visto. Le prime cinque linee non sono niente di speciale, ma nell'ultimo esempio abbiamo bisogno di due linee aggiuntive. Per primo, inventiamo un nome per il nostro modulo combinato, per secondo diciamo a make quali file oggetto sono parti del modulo.
==Creare moduli per kernel precompilati==
Ovviamente, raccomandiamo fortemente di ricompilare il tuo kernel in modo da abilitare un certo numero di caratteristiche di debugging utili, come MODULE_FORCE_UNLOAD (forzare l'unload di moduli): quando questa opzione è abilitata, puoi forzare il kernel a rimuovere un modulo, anche quando crede (il kernel, n.r.d.) che non è sicuro, tramite il comando '''rmmod -f module'''. Questa opzione può farti risparmiare tanto tempo ed un certo numero di riavvii durante lo sviluppo di un modulo.
Tuttavia, ci sono alcuni casi in cui si voglia caricare un modulo in un kernel precompilato in esecuzione, come per esempio quelli forniti nelle comuni distribuzioni Linux, o in un kernel compilato tempo fa. In alcune circostanze potresti avere la necessità di compilare ed inserire un modulo in un kernel in esecuzione che non hai il permesso di ricompilare, o su una macchina che preferiresti non riavviare. Se non riesci a pensare ad un caso che ti costringerà ad usare moduli per un kernel precompilato, potresti voler saltare questa sezione e trattare il resto di questo capitolo come una grande nota a pie' di pagina.
Ora, se hai appena installato un kernel da sorgente, usalo per compilare i tuoi moduli e prova ad inserirli nel kernel; in molti casi potresti ottenere un errore come il seguente:
<pre>
insmod: error inserting 'poet_atkm.ko': -1 Invalid module format
</pre>
Informazioni meno criptiche sono presenti in /var/log/messages:
<pre>
Jun  4 22:07:54 localhost kernel: poet_atkm: version magic '2.6.5-1.358custom 686
REGPARM 4KSTACKS gcc-3.3' should be '2.6.5-1.358 686 REGPARM 4KSTACKS gcc-3.3'
</pre>
In altre parole, il kernel si rifiuta di accettare il modulo per via delle versioni (più precisamente, magics version) che non corrispondono. Per inciso, le version magics sono conservate nell'oggetto del modulo come stringa statica che inizia per vermagic:. Le informazioni di versione sono inserite nel tuo modulo quando è linkato al file <code>init/vermagic.o</code>. Per dare uno sguardo alle version magics ed altre stringhe conservate in un modulo, puoi dare il comando '''modinfo module.ko''':
<pre>
[root@pcsenonsrv 02-HelloWorld]# modinfo hello-4.ko
license:        GPL
author:        Peter Jay Salzman <p@dirac.org>
description:    A sample driver
vermagic:      2.6.5-1.358 686 REGPARM 4KSTACKS gcc-3.3
depends:       
</pre>
Per superare questo problema possiamo ricorrere all'opzione --force-vermagic, ma questa soluzione è potenzialmente insicura, e indiscutibilmente inaccettabile nella produzione di moduli. Di conseguenza, vogliamo compilare il nostro modulo in un ambiente che sia identico a quello in cui il nostro kernel precompilato è stato costruito. Come fare questo è il tema del resto del capitolo.
Prima di tutto, assicurarsi che il codice sorgente del kernel è disponibile e che abbia esattamente la stessa versione del kernel in esecuzione. Dunque, trova il file di configurazione che è stato usato per compilare il kernel precompilato. Di solito, è disponibile nella directory <code>/boot</code>, sotto un nome tipo config-2.6.x. Potresti volerlo copiare nella directory del kernel: '''cp /boot/config-`uname -r` /usr/src/linux-`uname -r`/.config'''.
Concentriamoci di nuovo sul precedente errore: uno sguardo da vicino alle stringhe version magic suggeriscono che, anche con due file di configurazione che sono esattamente gli stessi, una piccola differenza nel version magic è possibile, ed è sufficiente a prevenire l'inserimento del modulo nel kernel. Questa piccola differenza, cioè la stringa personalizzata che appare nel version magic del modulo e non in quella del kernel, è dovuta ad una modifica nel makefile, che alcune distribuzioni includono, rispetto all'originale. Quindi, esamina il tuo <code>/usr/src/linux/Makefile</code> ed assicurati che l'informazione della versione specificata è esattamente la stessa di quella usata per il tuo kernel corrente. Per esempio, il tuo makefile potrebbe iniziare cosi:
<pre>
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 5
EXTRAVERSION = -1.358custom
...
</pre>
In questo caso, hai bisogno di ripristinare il valore del simbolo EXTRAVERSION a -1.358. Suggeriamo di tenere una copia di backup del makefile usato per compiare il tuo kernel disponibile in <code>/lib/modules/2.6.5-1.358/build</code>. Un semplice '''cp /lib/modules/`uname -r`/build/Makefile /usr/src/linux-`uname -r`''' dovrebbe essere sufficiente. Inoltre, se hai già inziato a compilare il kernel con il precedente (sbagliato) Makefile, dovresti anche rieseguire '''make''', o modificare direttamente il simbolo UTS_RELEASE nel file <code>/usr/src/linux-2.6.x/include/linux/version.h</code> con lo stesso valore presente in <code>/lib/modules/2.6.x/build/include/linux/version.h</code>, o sovrascrivere quest'ultimo sul primo.
Ora, eseguire '''make''' per aggiornare la configurazione, la versione degli headers e gli oggetti:
<pre>
[root@pcsenonsrv linux-2.6.x]# make
CHK    include/linux/version.h
UPD    include/linux/version.h
SYMLINK include/asm -> include/asm-i386
SPLIT  include/linux/autoconf.h -> include/config/*
HOSTCC  scripts/basic/fixdep
HOSTCC  scripts/basic/split-include
HOSTCC  scripts/basic/docproc
HOSTCC  scripts/conmakehash
HOSTCC  scripts/kallsyms
CC      scripts/empty.o
...
</pre>
Se non desideri compiare il kernel ora, puoi interrompere il processo ('''CTRL-C''') subito dopo la linea che inizia per <code>SPLIT</code> poichè in quel momento i file che ti servono saranno già pronti. Ora puoi tornare indietro alla directory del tuo modulo e compilarlo: sarà compilato correttamente in accordo con le impostazioni del tuo kernel corrente e verrà caricato senza nessun errore.
[[Categoria:Linux Kernel Module Programming Guide]]
6 999

contributi