Auch wenn es sich bei Linux um ein monolithisches Betriebssystem handelt, sind viele Teile davon in Modulen aufgebaut. Diese Kernelmodule können zur Laufzeit geladen und auch wieder entladen werden. Wie man so ein Modul entwickelt und zum Laufen bringt möchte ich hier kurz zeigen.
C-Code
Linux wird in C entwickelt und darum werden auch Kernel-Module in C geschrieben. Sie bestehen im Grunde aus zwei Funktionen: Eine, die beim Laden ausgeführt wird und eine, die beim Entladen ausgeführt wird.
Beginnen wir mit einem einfachen Hello-World-Beispiel:
Zuerst brauchen wir eine Bibliothek, die wir brauchen um ein Modul laden zu können:
1#include <linux/module.h>
2#include <linux/init.h>
Die Header-Datei module.h
wird von jedem Modul benötigt und die Datei init.h
stellt Makros zur Verfügung, die wir brauchen um die Funktionen zu registrieren, was wir jetzt auch machen:
1#include <linux/module.h>
2#include <linux/init.h>
3
4// Methode, die beim Laden ausgeführt wird
5static int __init hello_init(void)
6{
7 return 0;
8}
9
10// Methode, die beim Entladen ausgeführt wird
11static void __exit hello_exit(void)
12{
13}
14
15// Registrieren der Methoden
16module_init(hello_init);
17module_exit(hello_exit);
An dieser Stellen wurden zwei Funktionen eingebaut, die jeweils entweder beim Laden oder Entladen ausgeführt werden. Mit module_init
und module_exit
wird dem Betriebssystem mitgeteilt, welche es sind.
Das alles sieht zwar nett aus, tut aber noch nichts. Darum soll das Modul das berühmte “Hello world” ins syslog schreiben:
1#include <linux/module.h>
2#include <linux/init.h>
3#include <linux/kernel.h>
4
5// Methode, die beim Laden ausgeführt wird
6static int __init hello_init(void)
7{
8 printk(KERN_DEBUG "Hello World!");
9 return 0;
10}
11
12// Methode, die beim Entladen ausgeführt wird
13static void __exit hello_exit(void)
14{
15 printk(KERN_DEBUG "Bye World!");
16}
17
18// Registrieren der Methoden
19module_init(hello_init);
20module_exit(hello_exit);
Mit printk(<loglevel>,<text>)
lässt sich das bewerkstelligen. Um das Loglevel angeben zu können, benötigen wir aber noch zusätzlich die Datei kernel.h
.
Wenn man das Module so ausführt wird Linux übrigens sofort über die fehlende Lizenzangabe jammern. An dieser Stelle sollte man aber auch gleich noch Informationen über den Autor angeben:
1#include <linux/module.h>
2#include <linux/init.h>
3#include <linux/kernel.h>
4
5#define DRIVER_AUTHOR "Johannes Mittendorfer <...@... .com>"
6#define DRIVER_DESC "Hello world"
7
8// Methode, die beim Laden ausgeführt wird
9static int __init hello_init(void)
10{
11 printk(KERN_DEBUG "Hello World!");
12 return 0;
13}
14
15// Methode, die beim Entladen ausgeführt wird
16static void __exit hello_exit(void)
17{
18 printk(KERN_DEBUG "Bye World!");
19}
20
21// Registrieren der Methoden
22module_init(hello_init);
23module_exit(hello_exit);
24
25MODULE_LICENSE("GPL");
26MODULE_AUTHOR(DRIVER_AUTHOR);
27MODULE_DESCRIPTION(DRIVER_DESC);
In diesem Beispiel habe ich den Autor und den Titel oben definiert und unterhalb samt Lizenz, in diesem Fall GPL, angegeben.
Jetzt kann man das Modul ausführen. Aber wie eigentlich? Dazu braucht man ein…
Makefile
Das Makefile gibt an, wie der Code kompiliert werden soll. Alles was man dazu braucht ist eine Datei namens Makefile
mit folgendem Inhalt:
1obj-m += helloworld.o
Wenn man jetzt im Verzeichnis make
ausführt, dann sollte in etwa so etwas erscheinen:
1make -C /lib/modules/3.11.0-19-generic/build M=/home/johannes/Schreibtisch/kernel modules
2make[1]: Betrete Verzeichnis '/usr/src/linux-headers-3.11.0-19-generic'
3 CC [M] /home/johannes/Schreibtisch/kernel/helloworld.o
4 Building modules, stage 2.
5 MODPOST 1 modules
6 CC /home/johannes/Schreibtisch/kernel/helloworld.mod.o
7 LD [M] /home/johannes/Schreibtisch/kernel/helloworld.ko
8make[1]: Verlasse Verzeichnis '/usr/src/linux-headers-3.11.0-19-generic'
Außerdem sollte im Verzeichnis eine Datei namens helloworld.ko
entstanden sein. Das ist das fertige Modul. Das muss aber jetzt noch an einen vorgesehenen Platz.
Installation
Die ko-Datei muss unterhalb des Ordners /lib/modules/<kernel-version>/kernel/
kopiert werden. Die Kernelversion lässt sich durch den Befehl uname -r
herausfinden. Um die Module neu einzulesen braucht es dann den Befehl sudo depmod -a
und schließlich zum Laden des Modules modprobe helloworld
.
Wenn alles geklappt hat, sollte man jetzt im Syslog ein “Hello world” sehen!
(Das sieht man zum Beispiel durch tail -f /var/log/syslog
)
Teil 2 kommt dann irgendwann, wenn ich Zeit habe. ;)