Inhaltsverzeichnis
Linux-Kernel-Hacking
Noch in Rekonstruktion
Im Dezember 2012 hat Yu vorgeschlagen, einen tieferen Einblick in die Programmierung von Modulen für den Linux-Kernel zu geben.
Am 9. Februar 2013 trafen wir uns und haben gemeinsam ein einfaches Modul für den Linux-Kernel erstellt. Dies legt ein Char-Device an. Darüber kann man die Ausgabe eines Zählers abfragen.
Shownotes
Einstieg in das Kernelbauen
Yu hat für den Vortrag ein paar Folien zusammengestellt, die es hier gibt und einige der Grundlagen zusammen fassen.
Das Minimal-Kernel-Modul
Als Anfang das simpelste Kernelmodul.
Benötigte Utensilien:
- Ein Linux (Header nicht vergessen)
- Editor/Terminal
- C-Code und passenden Compiler (aka GCC)
- Makefile
(Nicht unbedingt in dieser Reihenfolge)
Ist alles zur Hand, kann es losgehen. Das einfachste Kernelmodul sieht ungefähr so aus:
- geruest.c
// The simplest kernel module. #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/stat.h> int init_module(void) { printk(KERN_INFO "Hello world!\n"); return 0; } void cleanup_module(void) { printk(KERN_INFO "Goodbye world!\n"); }
Es macht im Grunde nichts anderes, als beim Laden via insmod geruest.ko
die Nachricht Hello world! in das Log zu schreiben. Wenn man es mit rmmod geruest
wieder entlädt, wird die Nachricht Goodbye world! in das Log geschrieben.
Um es aber zu übersetzen, benötigt man ein Makefile (benötigt man nicht, macht die Sache aber einfacher). Dieses könnte ungefähr so aussehen:
- Makefile
obj-m += geruest.o all: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
Damit kann durch die Aufruf von make
das Modul übersetzen.
Damit kann durch die Aufruf von make
das Modul übersetzen und über insmod
bzw rmmod
laden und entladen.
Im nächsten Schritt soll das Modul noch ein paar Informationen erhalten, die z.B. mit modinfo
ausglesen werden können. Dazu kann man diese Zeilen, natürlich mit sinvollem Inhalt gefüllt, in die Quellen einbauen:
MODULE_AUTHOR("Dein Name") MODULE_LICENSE("Name der Lizenz, z.B. GPL") MODULE_DESCRIPTION("Beschreibung, was dein Modul so macht") MODULE_SUPPORTED_DEVICE("Gerad unwichtig. Zur Dokumentationszwecken nutzbar")
Variablen im Kernelland
Der Kernel definiert zum Teil seine eigenen Variablentypen. Diese könnten z.B. sein:
- byte, short, ushort, int, uint, long, ulong
- charp: a character pointer
- bool: a bool, values 0/1, y/n, Y/N.
- invbool: the above, only sense-reversed (N = true).
Diese kann man nutzen, um Parameter vom Terminal einzulesen, die z.B. beim Laden des Moduls mit übergeben werden.
Um Parameter vom Terminal einzulesen nutzt man:
/* Prototyp */ module_param(variablenname, type, permission); /* Hauptsächlich für modinfo */ MODULE_PARM_DESC(variablenname, "Beschreibung");
Um tatsächlich auf das neu geschaffene Device zugreifen zu können, muss man ein device in /dev
erstellen. Dies geht ungefähr so:
dev_t devnr = MKDEV(int majornr, int minornr);
In dem konrketen Beispiel sollten wobei majornr
120 und minornr
1 sein.
Als nächstes braucht man ein struct, welches die character-device-Struktur des Kernels beinhaltet.
struct cdev *char_dev;
Sowie noch einen int
fuer den Rückgabewert, sowie den Names
des Devices als char*.
Dies könnte dann ungefähr so aussehen:
// .. dev_t devnr = MKDEV(120, 1); struct cdev *char_dev; int rv; char* name = "device_name"; //..