Dieses Projekt führt in die Linux-Kernelprogrammierung für den Raspberry Pi ein. Das klassische „HelloWorld“-Programm ist nun ein minimales Kernel-Modul. Kernelprogrammierung gehört zu den anspruchsvolleren Themen der Linuxprogrammierung. Dennoch - der Einstieg in die große Welt des Linux-Kernels ist gar nicht so schwierig.
Ein Kernel-Modul ist eine Software, die zur Laufzeit in den Kernel eingebunden und auch wieder entfernt werden kann. Dies ist eine sehr elegante Methode, denn sie hält den eigentlichen Laufzeitkernel schlank. Nur diejenigen Module, die tatsächlich benötigt werden, brauchen so geladen zu werden. Außerdem ermöglicht diese Methode auch eine bequeme Softwareentwicklung, da der Rechner nicht jedesmal neu gestartet werden muss, um Änderungen am Modul zu registrieren. Kernel-Module werden z.B. für folgende Anwendungen benötigt:
Wann immer man also „echte“ Hardware ansteuern will, wird ein Kernel-Modul (Kerneltreiber) benötigt.
Ein Kernel-Modul zur Ansteuerung des GPIO-Ports des Raspberry Pi kann 250 mal schneller sein als ein herkömmlicher Zugriff auf die Ports!
Grundsätzlich ist ein Raspberry Pi nicht notwendig um ein Kernel-Modul zu programmieren, dann muss aber auf dem Entwicklungsrechner (Hostrechner) eine sogenannte Cross-Entwicklungsumgebung eingerichtet werden, worauf dieser Abschnitt aber nicht eingeht (siehe http://elinux.org/RPi_Kernel_Compilation ). Die in diesem Abschnitt erklärte Methode unterscheidet sich auch von der auf „offiziellen“ Methode, wie sie auf elinux.org beschrieben wurde. Die dort beschriebene Methode erfordert mehr Fachwissen, insbesondere wenn nicht alles wie vorgegeben abläuft. Die hier vorgestellte Vorgehensweise erstellt auch keinen neuen Kernel, was ausgesprochen bequem ist, denn das erstellen eines Kernel auf dem Raspberry Pi kann 10-12 Stunden dauern.
Die Quellen des Linux-Kernels müssen installiert werden, um einen Treiber (Treiber werden in der Regel als Kernel-Modul zur Verfügung gestellt) übersetzen zu können. Außerdem muss das System für die Modul Programmierung konfiguriert werden.
Es können nicht irgendwelche Kernel-Quellen heruntergeladen werden, sondern nur die, die zu dem installierten Betriebssystem passen. Auch muss das erstellte Kernel-Modul genau (Versionsmatching) zum laufenden Kernel passen, da der Kernel sonst seine Zusammenarbeit verweigert. In diesem Fall können wir das Modul gar nicht erst laden. Außerdem müssen die verwendeten Programme wie z.B. insmod, modprobe, lsmod, rmmod
oder gcc
zum kompilierten Kernel passen.
Mit uname -r kann die Version des Installierten Kernel in Erfahrung gebracht werden. Es handelt sich hier um einen Kernel 3.10 (Patchlevel 10, Sublevel 25). |
Wir werden deshalb erst einmal den Raspberry Pi auf den aktuellen Stand setzen und anschließend die Kernel-Version ermitteln, die heruntergeladen werden muss.
Geben Sie die nachfolgende Zeile auf der Konsole ein:
sudo rpi-update
Mehr zum rpi-update Programm unter https://github.com/Hexxeh/rpi-update.
Mit sudo apt-get install rpi-update
kann das Programm gegebenenfalls nachinstalliert werden.
Nach dem Update steht jetzt der Kernel 3.12 (Patchlevel 12, Sublevel 19) zur Verfügung. Für diesen Kernel müssen nun die Kernel-Quellen heruntergeladen werden. |
Als nächstes müssen die Kernelheader (Kernel-Quellen) installiert werden, damit man in der Lage ist, ein Kernel-Modul auch übersetzen zu können.
Falls Sie die nachfolgende Fehlermeldung bekommen wenn Sie rpi-source
aufrufen, sollten Sie die Anweisung in der RPI-SOURCE Box berücksichtigen und Anschließend noch einmal rpi-source
aufrufen.
ERROR: gcc version check: mismatch between gcc (4.6.3) and /proc/version (4.7.2) Skip this check with --skip-gcc
sudo wget https://raw.githubusercontent.com/notro/rpi-source/master/rpi-source -O /usr/bin/rpi-source && sudo chmod +x /usr/bin/rpi-source && /usr/bin/rpi-source -q --tag-update rpi-source
Die erste Zeile installiert das Programm rpi-source, und in der zweiten Zeile wird es dann aufgerufen. Im Homeverzeichnis wird ein Verzeichnis namens „linux“ angelegt. Dieses Verzeichnis ist ein Link auf das Verzeichnis „linux-c3db7205bcd8988cf7c185e50c8849542554b1f5“, das sich ebenfalls im Homeverzeichnis befindet. Die heruntergeladene Datei (linux-c3db7205bcd8988cf7c185e50c8849542554b1f5.tar.gz) kann, um Speicherplatz zu sparen, gelöscht werden.
Box: RPI-SOURCE
Mehr zum rpi-source
Programm: https://github.com/notro/rpi-source/wiki
Insbesondes wenn Sie die nachfolgende Fehlermeldung bekommen, sollten Sie so vorgehen wie im Link beschrieben, um auch den richtigen Compiler (GCC Version) zu bekommen.
Der Link beschreibt warum die folgenden Befehle angewandt werden müssen. Vergessen Sie nicht anschließend, noch einmal rpi-source aufzurufen.
sudo apt-get install gcc-4.7 g++-4.7 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.6 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.7 40 --slave /usr/bin/g++ g++ /usr/bin/g++-4.7 sudo update-alternatives --config gcc sudo apt-get install libncurses5-dev
Ein minimales Kernel-Modul wird nun übersetzt. Dazu legen wir im Verzeichnis /home/pi
das Verzeichnis kernel
an. Dieses Verzeichnis enthält die Datei hello_world.c
und Makefile.
#include <linux/module.h> #include <linux/init.h> /* Metainformation */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Torsten Röhl <devel@physics4school.de>"); MODULE_DESCRIPTION("Sample hello_world kernel module" ); /* Loads the module in the kernel */ static int __init init_helloworld (void) { printk("Hello Welt: init_helloworld \n"); return 0; } /* Removes the module from kernel */ static void __exit exit_helloworld(void) { printk("GoodBye World: exit_helloworld \n"); } module_init( init_helloworld ); module_exit( exit_helloworld );
Das Einbinden der Header-Dateien (*.h). So sind z.B. init und exit in init.h definiert.
Mit dem MODULE_XXX-Makros können Metainformationen gesetzt werden. Sie können mit dem modinfo tool ausgelesen werden, lediglich das Makro MODULE_LICENCE ist obligatorisch.
Die Funktion init_helloworld wird beim Laden des Moduls aufgerufen. Die Funktion exit_helloworld wird beim Entladen des Moduls aufgerufen.
Die beiden Makros module_init und module_exit sorgen dafür, dass man die Funktionsnamen (hier init_helloworld und exit_helloworld) frei wählen kann.
Das Einbinden der Header-Dateien (*.h
). So sind z.B. init
in und
exitinit.h
definiert.
Mit dem MODULE_XXX-Makros
können Metainformationen gesetzt werden. Sie können mit dem modinfo tool ausgelesen werden, lediglich das Makro MODULE_LICENCE
ist obligatorisch.
Die Funktion init_helloworld
wird beim Laden des Moduls aufgerufen.
Die Funktion exit_helloworld
wird beim Entladen des Moduls aufgerufen.
Die beiden Makros module_init
und module_exit
sorgen dafür, dass man die Funktionsnamen (hier init_helloworld
und exit_helloworld
frei wählen kann.
Die Datei kann mit einem Editor (z.B. Kate oder vi) erstellt werden.
Um ein vollständiges Kernel-Modul zu erstellen, muss der Quellcode noch übersetzt werden. Das Übersetzen (Kompilieren) des Quellcodes (Sourcecode) in ein ausführbares Programm erledigt man am besten mit einer Steuerdatei (Makefile) und dem Programm make
.
ifneq ($(KERNELRELEASE),) obj-m := hello_world.o else KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules endif
Beim Makefile ist auf einiges zu achten, z.B.:
Um nicht an dieser Stelle zu scheitern, empfehle ich, dieses Makefile downzuloaden: kernel.zip
.
Angenommen, im Verzeichnes kernel
befinden sich nun die Dateien helloworld.c
und Makefile
, dann genügt ein einfacher Aufruf von make
, um das Modul zu erstellen.
pi@raspberrypi ~/kernel $ make
Damit make
weiß, was es tun soll, sollte man sich im Verzeichnis kernel befinden.
Um das Modul zu testen werden die Modul-Utilities benötigt. Darunter versteht man einen Satz von Hilfsprogrammen, um Kernelmodule zu laden, entladen u.s.w.
Um das Modul zu laden/entladen, werden Rootrechte benötigt.
Folgende Programme (modutils
) werden verwendet:
modinfo
: liefert Informationen über ein Modul insmod
: fügt zur Laufzeit Module in den Linux-Kernel einlsmod
: zeigt alle geladenen Module anrmmod
: entfernt Laufzeitmodule aus dem Kernel
Hier die Ausgabe des modinfo
-Programms:
pi@raspberrypi ~/kernel $ modinfo hello_world.ko filename: /home/pi/kernel/hello_world.ko description: Sample hello_world kernel module author: Torsten Röhl <devel@physics4school.de> license: GPL srcversion: 793A736C473976825C9475C depends: vermagic: 3.12.19+ preempt mod_unload modversions ARMv6
Laden des Moduls:
pi@raspberrypi ~/kernel $ sudo insmod hello_world.ko
Überprüfen, ob das Modul korrekt geladen wurde:
spi@raspberrypi ~/kernel $ lsmod | grep hello_world hello_world 735 0
Um die Ausgabe von lsmod nicht zu lang zu machen, können wir mit grep hello_world nach hello_world suchen. Die Ausgabe zeigt an, dass die Suche erfolgreich war, d.h. das Modul korrekt geladen wurde.
Entladen des Moduls:
pi@raspberrypi ~/kernel $ sudo rmmod hello_world.ko
Lohnend ist auch ein Blick in die Datei /var/log/messages
:
# cat /var/log/messages May 16 16:24:38 kernel: Hello World: init_helloworld May 16 16:26:16 kernel: GoodBye World: exit_helloworld
Auch wenn der Quellcode keine zwanzig Zeilen lang ist, gibt es bei der Erstellung jedoch diverse Fehlermöglichkeiten. Wenn alles bis hier geklappt hat, wurde jedoch eine Menge erreicht:
hello_world.ko
wurde erfolgreich zur Laufzeit in den Kernel geladen und entladen.Ein komplettes System für die Kernelentwicklung auf dem Raspberry Pi steht nun zur Verfügung.