Inhaltsverzeichnis

Raspberry Pi Kernelprogrammierung: HelloWorld

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!

Voraussetzung

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.

Überblick

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.

  1. Kernel-Quellen (auch Kernel-Header genannt) herunterladen
  2. Hallo Welt Modul erstellen und übersetzen (compilieren)
  3. Testen des „Hallo Welt“ Moduls

Details

1. Kernel-Quellen (auch Kernel-Header genannt) herunterladen

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

2. Hallo Welt Modul erstellen und übersetzen (compilieren)

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 ); 	  	 
Erläuterung

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 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.
Die Datei kann mit einem Editor (z.B. Kate oder vi) erstellt werden.

Vom Quellcode zum Modul:

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.

3. Testen des "Hallo Welt" Moduls

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:

Beispiel

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

Zusammenfassung

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:

Ein komplettes System für die Kernelentwicklung auf dem Raspberry Pi steht nun zur Verfügung.

Literatur