Mein erstes Linux Kernel Modul
Bevor wir beginnen
Jeder zukünftige Kernel Hacker fängt irgendwann mal an, so wie ich jetzt.
Ich habe natürlich auch den Traum, mal mein eigenes Modul zu schreiben und zu kompilieren. So habe ich mal aufgeschrieben, wie ich ein absolut sinnloses Modul schreibe und kompiliere, in den laufenden Kernel einbinde und wieder raus werfe. Diese Modul macht erst mal nichts, es lässt sich jedoch , wie ein richtiges Modul laden und gibt eine Meldung nach /var/log/messages.
Wir brauchen unbedingt root-Privilegien um das Modul zu laden und zu entladen! Aber wenn Ihr an dieser Stelle seit, ein Kernel-Modul schreiben zu wollen, solltet ihr über die Grundkenntnisse im Umgang mit Linux Bescheid wissen.
Grundlagen
Es gibt einige Dinge, die Ihr unbedingt auf Eurem System braucht, sonst funktioniert die ganze Sache nicht! Ich liste hier einfach mal alles auf:
1.) Die Standard Entwicklungsumgebung bestehend aus bintuils, gcc, make und so weiter. Diese solltet Ihr aber ohne Problem mit Eurem Paketverwaltungswerkzeug installiert bekommen.
2.) Die module-init-tools, also die Tools die die Kommandos lsmod, insmod, rmmod usw. liefern.
3.) Die Kernel Sourcen für Euren laufenden Kernel. Hier gibt es unterschiedliche Möglichkeiten; ihr habt einen Vanilla-Kernel, so wie ich, dann habt Ihr die Sourcen sowieso
, oder Ihr habt einen Distributions-Kernel, dann solltet ihr die jeweilgen Devel- oder Headers-Pakete installieren. Beispielsweise für Ubuntu/Debian
1 | sudo apt-get install linux-headers-`uname -r` |
und unter Fedora/Redhat lautet das Paket kernel-devel.
Kernel Sourcen
Die Kernel Sourcen sind so wichtig, das wir hier noch ein wenig darüber reden wollen. Auf JEDEN Fall müsst Ihr die passenden Sourcen zu Eurem laufenden Kernel installieren. Diese liegen dann gewöhnlich irgendwo unterhalb /usr/src, je nach Distribution heissen die Ordner dort dann anders. Gebt Ihr im Terminal den Befehl
1 | guenti@guenti-laptop:~/Projekte/kernel-module$ ls -l /lib/modules/`uname -r` | grep build |
ein, dann solltet Ihr eine ähnliche Ausgabe bekommen, wie die folgende:
lrwxrwxrwx 1 root root 31 2009-07-09 13:20 build -> /home/guenti/build/linux-2.6.30
Bei mir liegen die Sourcen deshalb woanders, weil ich einen Vanilla Kernel, also einen Original-Kernel, verwende. Normalerweise verweist der Symlink /lib/module/`uname -r`/build nach /usr/src/kernel-headers-xxx, oder ähnlichem.
Die passenden Kernel Sourcen sind deshalb so enorm wichtig, da die Präprozessor #include Anweisungen NICHT auf die normalen System Header-Dateien verweisen, sondern auf die in den Kernel-Source Pfaden. Später, im Makefile, werden wir dem Build-Prozess dann den genauen Pfad zu den Kernel-Sourcen mitteilen.
Das Modul
Hier erst mal der Quelltext von firstmodule.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <asm/current.h> #include <linux/sched.h> static int fmload(void) { printk(KERN_INFO "firstmodule module being loaded.\n"); return 0; } static void fmunload(void) { printk(KERN_INFO "firstmodule module being unloaded.\n"); } module_init(fmload); module_exit(fmunload); MODULE_AUTHOR("Mario Guenterberg"); MODULE_LICENSE("Dual new BSD/GPL"); MODULE_DESCRIPTION("My first kernel module."); |
Einige Erläuterungen zum Quelltext:
1.) Als erstes includen wir die Kernel-Header Dateien.
2.) Eigentlich ist es nicht nötig, Meldungen ausgeben zu lassen, doch zu Testzwecken machen wir es, um in den Logs was zu lesen zu haben.
3.) Der return-Wert der init Funktion sollte immer 0 sein, damit angezeigt wird, das alles in Ordnung ist.
4.) Fügt eine “valide” Lizenz ein, damit Ihr den Kernel nicht “tainted”. Also BSD/GPL/LGPL oder dergleichen. Ein Beispiel für ein Modul, welches den Kernel in den tainted Zustand überführt, ist das Closed Source Nvidia Treiber Modul.
5.) Auf keinen Fall ein Komma hinter den Log Level Parameter in der printk Anweisung einfügen. Dort gehört keines hin!
So nun kommen wir zum Build-Prozess.
Das Makefile
1 2 3 4 5 6 7 8 9 10 11 12 | ifeq ($(KERNELRELEASE),) KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) .PHONY: build clean build: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.markers *.symvers *.order else $(info Building with KERNELRELEASE = ${KERNELRELEASE}) obj-m := firstmodule.o endif |
Eine kurze Erklärung zu diesem Makefile:
Das Makefile weiß, das es sich im Source-Verzeichnis (PWD) des Moduls befindet, von dem aus es sich in das Kernel-Source Verzeichnis (KERNELDIR), so wie angegeben, begibt, in dem es alles benötigte vorfindet und dieses auch benutzt, um anschliessend wieder in das Modul Verzeichnis (PWD) zurückzukommen, um das Modul zu kompilieren. Hört sich wirr an, is aber so.
Nach einigem Überlegen erschließt es sich dann auch.
Wenn alles durchläuft, sollte nach einem Aufruf von make build, die Ausgabe wie folgt aussehen:
1 2 3 4 5 6 7 8 9 10 11 | guenti@guenti-laptop:~/Projekte/kernel-module$ make build make -C /lib/modules/2.6.30.1-selfmade/build M=/home/guenti/Projekte/kernel-module modules make[1]: Betrete Verzeichnis '/home/guenti/build/linux-2.6.30' Building with KERNELRELEASE = 2.6.30.1-selfmade CC [M] /home/guenti/Projekte/kernel-module/firstmodule.o Building modules, stage 2. Building with KERNELRELEASE = 2.6.30.1-selfmade MODPOST 1 modules CC /home/guenti/Projekte/kernel-module/firstmodule.mod.o LD [M] /home/guenti/Projekte/kernel-module/firstmodule.ko make[1]: Verlasse Verzeichnis '/home/guenti/build/linux-2.6.30' |
So, nun haben wir im Verzeichnis die Datei firstmodule.ko – unser eigenes Kernel Modul.
Wenn wir jetzt mal modinfo firstmodule.ko aufrufen, dann sehen wir folgende Ausgabe:
1 2 3 4 5 6 7 8 | guenti@guenti-laptop:~/Projekte/kernel-module$ modinfo firstmodule.ko filename: firstmodule.ko description: My first kernel module. license: Dual new BSD/GPL author: Mario Guenterberg srcversion: 0CB416C6EAC5346958A6EAE depends: vermagic: 2.6.30.1-selfmade SMP mod_unload modversions CORE2 |
Die letzte Zeile und srcversion kann abweichen; der Rest sollte so aussehen, wie hier abgebildet.
Laden des Moduls
Zum Schluss laden wir das Modul und spielen damit ein wenig herum. Hier müsst Ihr auf jeden Fall root-Rechte haben!
guenti@guenti-laptop:~/Projekte/kernel-module$ sudo insmod firstmodule.ko [sudo] password for guenti: guenti@guenti-laptop:~/Projekte/kernel-module$ lsmod | grep firstmodule firstmodule 1292 0 guenti@guenti-laptop:~/Projekte/kernel-module$ sudo rmmod firstmodule
Und wo sind nun die printk Meldungen geblieben? Ganz einfach, sie befinden sich in /var/log/messages:
guenti@guenti-laptop:~/Projekte/kernel-module$ tail /var/log/messages [snip] Jul 12 11:24:21 guenti-laptop kernel: [ 1022.497508] firstmodule module being loaded. Jul 12 11:24:42 guenti-laptop kernel: [ 1044.069566] firstmodule module being unloaded. [/snip]
Regel Nummer 1 bei der Kernel Programmierung lautet nämlich: Normalerweise findet keine Interaktion mit dem User-Space statt. Also damit auch keine Ausgaben auf dem Terminal, statt dessen wird, wie in unserem Fall, das Standard System Logfile benutzt und alle Ausgaben hierhin umgeleitet.
Aufräumen
Zum Schluss räumen wir nach das Quellverzeichnis unseres Moduls auf mit dem einfachen Befehl:
guenti@guenti-laptop:~/Projekte/kernel-module$ make clean
Abschluss
Wie wir sehen konnten, ist es relativ einfach ein Kernel-Modul zu bauen (hat mich auch überrascht
), zumindest in dieser vereinfachten Form. Jetzt ist es Zeit, dem ganzen auch noch eine vernünftige Funktionalität zu geben.
Erstellt am Samstag 11. Juli 2009
Unter: Kernel, Linux und Unix | Keine Kommentare »
