====== Raspberry Pi Kernelprogrammierung: HelloWorld ====== {{:inf:msr:nut.jpg?200 |}}//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: * Gerätetreiber * Netzwerktreiber * Treiber für Dateisysteme * Systemcalls 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 ==== * Gute Linuxkenntnisse und C-Programmierung. * Getestet wurde mit Rasbian Debian-Wheezy, d.h. ein Raspberry Pi mit installiertem Linux-System muss zur Verfügung stehen. 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. - Kernel-Quellen (auch Kernel-Header genannt) herunterladen - Hallo Welt Modul erstellen und übersetzen (compilieren) - 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. |{{ :inf:msr:raspi_kernel_1.png? |}}| |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. |{{ :inf:msr:raspi_kernel_3.png? |}}| |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 #include /* Metainformation */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Torsten Röhl "); 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.: * Die Datei muss exakt Makefile geschrieben werden. * Die Zeilen 2, 5, 6 und 9 müssen mit //"Tab"// (Tabulatortaste) beginnen. 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: * ''modinfo'': liefert Informationen über ein Modul * ''insmod'': fügt zur Laufzeit Module in den Linux-Kernel ein * ''lsmod'': zeigt alle geladenen Module an * ''rmmod'': entfernt Laufzeitmodule aus dem Kernel === 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 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: * Die Kernelquellen und modutils sind installiert und lauffähig. * Der Kompiler und make sind lauffähig. * Das fertige Kernel-Modul ''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. ===== Literatur ===== * Jürgen Quade, Eva-Katharina Kunst: Linux-Treiber entwickeln (dpunkt.verlag) * Jürgen Quade: Embedded Linux lernen mit dem Raspberry (dpunkt.verlag)