Die Entwicklung für System-on-a-Chip-Geräte wie den Raspberry Pi stellt eine größere Herausforderung dar, als man zunächst annehmen könnte. Diese Geräte sind in erster Linie darauf ausgerichtet, fertige Software auszuführen. Daher hat es sich bewährt, Betriebssystem-Images anzubieten, die der Anwender lediglich auf den Speicherchip oder das Speichermedium des Geräts kopiert. Im Falle des Raspberry Pi genügt es, ein 1-zu-1-Abbild auf eine SD-Speicherkarte zu schreiben (etwa mit dd), von der das Gerät bootet.

Doch wie erstellt man ein solches Image, das zusätzliche Dienste oder Frameworks anbieten soll, möglichst effizient? Es liegt nahe, auf dem Gerät selbst die notwendige Software zu installieren und zu kompilieren. Dabei könnte man alle Einstellungen vornehmen, bevor man das Image auf umgekehrtem Wege wieder von der SD-Karte in eine Datei schreibt, um diese weiterzuverteilen. Das Problem: Die beschriebene Prozedur dauert sehr lange, da die kleinen Geräte keine ausreichenden Ressourcen bieten, um darauf ernsthaft zu entwickeln. Auch geduldige Menschen spüren daher schon bald das Verlangen nach einer Lösung, die möglichst reproduzierbar und automatisiert solche Images generiert.

Draufsicht

Der logische erste Schritt besteht also darin, die eigene Perspektive auf den Prozess zu verändern und einen Schritt zurück zu tun. Um beim Raspberry Pi zu bleiben: Mithilfe von Skripten lässt sich etwa das von der Raspberry-Pi-Foundation bereitgestellte Raspbian-Image, das auf Debian basiert, direkt an die eigenen Bedürfnisse anpassen und weiterverteilen. Das spart nicht nur eine Menge Zeit, sondern lässt sich später auch automatisieren und mit einer Umgebung zur Continuous Integration kombinieren, etwa Jenkins CI [1].

Doch auf dem Weg dorthin liegen noch ein paar Stolpersteine. Der gewitzte Entwickler denkt natürlich sofort daran, das Image in eine Chroot-Umgebung einzubinden. So lässt sich darin agieren, als hätte man das System regulär gebootet. Allerdings macht die abweichende Prozessorarchitektur im konkreten Fall dabei einen Strich durch die Rechnung, denn die Intel-CPU kann die binären ARM-Programme nicht ausführen. Besteht also die einzige Lösung darin, einen Raspberry Pi als virtuelle Maschine zu betreiben, mit all den Nachteilen in Sachen Automatisierung, die das mit sich bringt? Nicht zwangsläufig!

Schnell mal emulieren

Ein Übersetzer muss her, der direkt zur Laufzeit einspringt. Qemu [2], der Quick Emulator, dürfte in Verbindung mit Linux' Virtualisierungslösung KVM ein Begriff sein. Doch das Projekt weist noch deutlich mehr Facetten auf. Zusammen mit dem vom Kernel bereitgestellten binftm_misc-Mechanismus bietet die Qemu-User-Emulation die Möglichkeit, auch plattformfremden Binärcode auszuführen. Einzige Voraussetzung: Die Maschine selbst darf nicht als VM laufen, sonst wird es kompliziert – Stichwort: Nested Virtualization.

Unter Debian genügt für die Installation der für die Emulation per Qemu notwendigen Werkzeuge bereits eine einzige Zeile:

$ sudo apt-get install qemu binfmt-support qemu-user-static

Im nächsten Schritt starten Sie den Daemon binfmt-support und lassen sich anschließend mithilfe des Befehls

$ sudo update-binfmts --display

eine Liste aller unterstützten binären Formate anzeigen. Rufen Sie dabei ein Programm auf, das für eine andere Plattform kompiliert wurde, dann erkennt der Daemon das passende Binärformat automatisch und der zugehörige Qemu-CPU-Emulator führt die Datei aus. Er übersetzt dabei simultan die fremden Prozessorbefehle in Kommandos, die sich auf der Intel-Plattform ausführen lassen.

Das Ganze ist allerdings sehr rechenintensiv und lässt sich zudem auf mehrere CPU-Kerne verteilen. Deshalb spielt hier die Taktung der einzelnen CPU-Kerne eine entscheidende Rolle für die Performance. Ein 2-GHz-Kern liegt nur leicht über der Performance des ARM-Prozessors im Raspberry Pi selbst, die Vorteile dieser Methode liegen also eher in der Flexibilität.

Beeren pflücken

Um das Raspberry-Pi-Image an die eigenen Bedürfnisse anzupassen, mounten Sie es am besten mit einem kleinen Skript [3], das in diesem Beispiel aus der Portierung der quelloffenen Telefonanlage Gemeinschaft 5 [4] stammt:

# mnt-pi-img.sh 2013-02-09-wheezy-raspbian.img /mnt

Es berücksichtigt die zwei Partitionen, die das Image enthält: Eine FAT-Partition für den Bootbereich und eine Ext4-Systempartition. Zudem setzt es Loop-Mounts für /dev/pts, /sys sowie /proc und kopiert die Qemu-Emulatordatei an die richtige Stelle im Dateisystem, damit sie innerhalb der Chroot-Umgebung zur Verfügung steht. Sie sollten das Skript auch beim finalen Aushängen der Gerätedateien verwenden.

Mit dem Befehl sudo chroot /mnt wechseln Sie direkt in die Chroot-Umgebung. Hier nehmen Sie alle notwendigen Änderungen vor, zu denen unter anderem das Installieren und Deinstallieren von Paketen über apt-get, das Kompilieren von Software und das Anpassen der Konfiguration gehören (Abbildung 1). Es ist in der Tat fast so, als würde man tatsächlich auf einem Raspberry Pi arbeiten. Nur Dienste, die man starten muss, um sie zu konfigurieren (etwa MySQL), lassen sich in dieser Umgebung schwieriger handhaben. Läuft ein ähnlicher Netzwerkdienst mit gleichem Port bereits auf dem Hostsystem, lässt er sich nicht ein zweites Mal starten.

Abbildung 1: In mehreren Schritten erstellen Sie aus einem Debian-Image für ARM ein angepasstes Image für den Raspberry Pi.

Das tatsächliche Laufzeitverhalten unterscheidet sich also, abhängig vom Anwendungsfall, von dem auf einem echten Gerät. Im Zweifel behilft sich der clevere Image-Bastler damit, die gewünschten Kommandos in einem Init-Skript zusammenzufassen, welches das System beim ersten Booten des Image abarbeitet und das sich dann selbst löscht.

Theorie und Praxis

Bereitstellung und Pflege eines Images sind relativ aufwendig, und gewöhnlich will man ja in erster Linie, dass die eigene Software läuft. Wer ein von der Community betreutes Image für das Gerät verwendet, muss dieses nicht ständig an neue Entwicklungen auf dem Raspberry Pi selbst anpassen. Gemeinschaft 5 verwendet aus diesem Grund das originale Debian-Image.

Das Erstellen der ISO-Dateien auf dem Gerät übernehmen eigens entwickelte Hook-Skripte, die in Debian Live laufen, aber unabhängig von der CPU-Architektur funktionieren. Damit diese Hook-Skripte auch auf dem Raspberry Pi laufen, erzeugen wieder andere Skripte eine Systemumgebung, die der von Debian Live entspricht. Später aktualisiert eine dritte Variante von Skripten die Software über Apt-get- oder Git-Repositories.

Das Bauen eines Image mit neu angepassten Skripten dauert nun noch fünf bis sechs Stunden, was über Nacht passieren kann. Steht das Grundgerüst aber erst einmal, lassen sich Anpassungen und Erweiterungen schnell erledigen. In Kombination mit einem Git-Repository und Jenkins CI entsteht sogar eine kontinuierliche Integrations- und Build-Umgebung.

Fazit

Die gängigen Emulations- und Virtualisierungswerkzeuge für Linux lassen sich auch bei der plattformübergreifenden Entwicklung von Software gewinnbringend einsetzen. Speziell bei der Anpassung von Programmen und Images für den Raspberry Pi kommt es aber noch mehr als bei der Virtualisierung von PC-Betriebssystemen auf die Leistungsfähigkeit und insbesondere Taktrate der Wirts-CPU an. 

Glossar

Continuous Integration

Software-Entwicklung unter fortlaufendem Zusammenfügen von Komponenten zu einer Anwendung. Umfasst typischerweise neben dem Neubau des Gesamtsystems auch automatisierte Tests.

Der Autor

Julian Pawlowski lebt in München und arbeitet als freiberuflicher IT-Projektmanager im internationalen Umfeld. Er ist seit 2012 im Core-Team der Telefonanlagensoftware Gemeinschaft und dort für den Release-Prozess verantwortlich.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 3 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

LinuxCommunity kaufen

Einzelne Ausgabe
 
Abonnements
 

Related content

Kommentare