Debian/Raspbian Linux vs. Yocto Linux on embedded systems

Warum soll man Yocto Linux und nicht Debain/Raspbian Linux auf einem embedded System verwenden?

Folgendes Szenario ist in den letzten Jahren immer wieder einmal an uns herangetragen worden: Ein embedded System mit einem Touchscreen soll die Steuerung und Hauptbedienung für ein Gerät umsetzen. Dazu gibt es neben dem Touchscreen auch noch diverse Hardware-Schnittstellen (CAN-Bus, RS485, Bluetooth und Hardware-Taster). Als Basis für die Elektronik ist ein Raspberry-PI Compute Module angedacht. Die Software soll basierend auf Linux laufen, wobei am Touchdisplay entweder eine HTML5-basierte Web-Anwendung oder eine native laufende Qt5 Anwendung laufen soll. Doch welches Betriebssystem soll dafür verwendet werden?

Das Raspberry PI Compute Module hat sich in den letzten Jahren gut entwickelt, viele Hersteller bieten mittlerweile Platformen / Basisboards dafür an. Auch in meinem persönlichen Umfeld hatte ich schon das „Glück“, so ein Gerät basierend auf einem RPI -Compute Module CM3 zu nutzen (siehe hier und ein Bild hier). Das Kalkül der Hersteller ist, dass ein Compute Module mit einer so breiten Nutzerbasis wohl noch über viele Jahre breiten Software-Support erhalten wird. Für Mainstream-Anwendungsfälle (wie z.B. Musik) wird das sicher gut funktionieren.

Doch um bei unserem eingangs eingeführten Szenario mit embedded Touch-Display zu bleiben: Welches Betriebssystem soll nun für den Einsatz im embedded Produkt verwendet werden? Für den Raspberry-PI gibt es aktuell „out-of-the-box“ zwei ganz interessante Möglichkeiten, ein Linux-basiertes Betriebssystem zu nutzen:

  • Eine fertige Linux Distribution wie z.B. Raspberry PI OS (Raspbian) oder auch Ubuntu 20.10 Desktop
  • Ein selbstgebautes Linux basierend auf dem Build-System Yocto

Die Vorteile der fertigen Linux Distributionen liegen auf der Hand: Die Installation ist einfach: Fertiges Image runterladen, SD-Karte flashen und der Raspberry-PI bootet. Bei Yocto sieht der Workflow etwas umfangreicher und komplexer aus. Doch nun etwas genauer: Wie funktionieren die Workflows, um ein Image zu generieren, das für weitere embedded Produkte verwendet werden könnte?

Der Workflow um ein Linux Image zu erzeugen
Im Falle einer fertigen Linux Distribution wird der Workflow ungefähr so aussehen: Fertiges Linux-Image runterladen, auf die SD-Karte flashen, Raspberry-PI booten, weitere Software-Pakete hinzufügen/entfernen, indem man am Raspberry-PI an der Konsole alle Arbeiten erledigt. Diese Linux-Konfiguration kann dann als „Prototyp“ für weitere Installationen genutzt werden, eventuell zieht man sich dann ein „Golden Image“ von diesem Raspberry-Pi für die weitere Verwendung. Dieser Workflow ist auch in den grünen Workflow-Schritten in nachfolgender Grafik ersichtlich (siehe auch [1])

Bei Yocto sieht der Workflow etwas anders aus (obwohl so ganz anders ist er dann doch nicht, wenn man bedenkt, dass ja auch Raspbian einmal gebaut worden sein muss, siehe blaue Schritte im Raspbian Workflow). Nachdem man alle Sourcen lokal heruntergeladen hat, erfolgt der configure-Schritt und die Auswahl der Software-Pakete. Dann kann der Cross-Compilations-Vorgang gestartet werden, welcher dann ein fertiges Linux-binary-image ausgibt. Um das fertige Image zu erzeugen, muss also (im Gegensatz zum Raspbian Workflow) das Endgerät nicht gebootet werden oder sonstige manuelle Schritte dazwischen ausgeführt werden.

Wie man am Raspbian-Workflow sieht, könnte man in diesem Fall auch ohne Cross-Compiler arbeiten. Ein Cross-Compiler ist ein Compiler, der ausführbaren Code für eine Platform erzeugt, die nicht der Platform entspricht auf dem der Compiler läuft. Ein Beispiel: Der Yocto-Cross-Compiler läuft auf einem normalen Ubuntu-PC mit schnellem Intel-Prozessor, erzeugt aber Code der dann nur auf dem Raspberry-PI ARM-Prozessor läuft. Cross Compiler haben den Vorteil, dass sie zum Entwickeln komfortabler sind, weil der Kompiliervorgang nicht auf der langsamen embedded Hardware laufen muss. Entwicklungs-Umgebungen können zudem langfristig in virtuellen Maschinen „konserviert“ werden und unabhängig von verfügbarer embedded Hardware erneut aktiviert werden.

Szenario Embedded Linux Display Steuerung
Embedded Linux Projekte sind meist davon geprägt, dass ein Gerät einen ganz speziellen Zweck für den Endbenutzer erfüllen soll, ohne dass der Endbenutzer etwas an diesem System dafür starten, konfigurieren oder gar einrichten muss. In so einem Szenario ist dem Endbenutzer eigentlich auch gar nicht bewusst, dass sein Gerät „embedded Linux“ nutzt. Das Gerät erfüllt einfach NUR seinen Spezialzweck! Alles andere und sämtliche Komplexität dafür wird vor dem Endbenutzer versteckt. Damit das möglich ist, ist eine umfangreiche Konfiguration und Anpassung an das Linux System erforderlich. Hier ein Auszug aus einer vereinfachten Beispiel-Liste an speziellen Konfigurationen für so ein embedded System:

  • Der Bootloader zeigt sofort nachdem der Raspberry-PI Strom bekommt einen speziellen Splash-Screen an
  • Der Bootloader bootet vom internen Speicher, außer man steckt einen Update-USB-Stick an den Raspberry-PI an
  • Während dem Boot-Vorgang werden keine Kernel-Messages angezeigt, es wird nur ein Splash-Screen angezeigt
  • CAN-Bus, Netzwerkeinstellungen und weitere Hardwareschnittstellen werden mit der Geräte-spezifischen Konfiguration automatisch eingerichtet. Der Touch-Screen wird gleich richtig kalibriert.
  • Nachdem der Kernel gebootet ist, wird gleich die Anwendung gestartet, welche ohne Umweg eines Fenstermanager im Full-Screen-Modus direkt in den Framebuffer des Displays schreibt.

Vor diesem Hintergrund ergeben sich folgende Vorteile für ein Yocto-Basiertes Linux, das die zentrale Konfiguration sämtlicher Linux-Aspekte als oberstes Ziel verfolgt:

Yocto-Linux mit besserer Anpassbarkeit
Raspbian hat per Default viele Pakete installiert, die man in einem typischen embedded Szenario nicht braucht. Ein Fenstermanager z.B. (X-Server bei Raspbian) ist hier ein prominentes Beispiel: Auf einem embedded-Gerät möchte man typischweise nur genau eine Anwendung starten und bedienen können. Darum sind folgende Features nicht notwendig, oder meist sogar absolut unerwünscht (Stichtwort: Security):

  • Zusätzliche Fenster nebenbei aufmachen
  • Fenster-Steuerungen (Minimieren, Maximieren, individuelle Fenstergrößen)
  • Globale Touch Gestensteuerungen (Mit 3 Fingern Anwendungen „weg swipen“)
  • Globale Keyboard-Shortcuts hinsichtlich Fenstersteuerungen und Anwendungsstarts

Diese Features jedoch im Nachhinein wieder zu entfernen führt zu einem technisch wackeligen Konstrukt (z.B. Kiosk-Mode) oder ufert in sehr hohem Mehraufwand aus. Technisch klarer und einwandfrei für so ein Szenario ist es, von vornherein nur ein Betriebssystem zu nutzen, das nur die notwendigen Software-Schichten bootet.

Ein weiteres Beispiel für die bessere Anpassbarkeit von Yocto Linux befindet sich beim Boot-Loader. Raspbian kommt mit einem fertigen Binary Boot-Loader, welcher das Betriebssystem startet. Diesen speziell anzupassen ist nur sehr eingeschränkt möglich. Bei Yocto hingegen ist der Bootloader wie jede andere Linux Komponente konfigurierbar und kann wie das restliche Image bei Bedarf neu gebaut werden.

Cross-Compiler für die embedded Hardware
Das Buildsystem Yocto generiert nicht nur ein bootfähiges Linux Image für die embedded Hardware, sondern bei Bedarf z.B. auch noch eine Cross-Compiler-Toolchain für C++ samt Libraries für Qt, den man am PC zum Kompilieren für das Endgerät nutzen kann. Dieser Mechanismus fehlt bei Raspbian. Unter Raspbian baut man normalerweise am embedded Gerät (Cross-Compiler wäre nur mit extra-Aufwand verfügbar).

Reproduzierbarkeit von Linux-Images
Unter Yocto kann man bit-identische Builds ausgehend von Linux-Sourcen (welche dann alle lokal am Entwicklerrechner liegen) generieren. Unter Raspbian gibt es nur ein fertiges Download-Image, das kann man dann zwar manuell anpassen, Pakete mit apt-get (von der cloud, welche sich laufend ändern) installieren und wieder ein Image von der SD-Karte „ziehen“, was jedoch zum Problem eines „golden image“ führt: Wiederholt man diesen Vorgang bei einem anderen Gerät zu einem anderen Zeitpunkt, bekommt man wahrscheinlich ein anderes Image. Das Raspbian-Image ist nicht „generiert“, sondern braucht den aufwändigen Workflow „Original-Raspbian-Flashen, am Gerät umändern, vom Gerät wieder auslesen und Image ablegen“. Dieser Workflow ist fehleranfälliger als das generieren eines fertigen Images von lokalen Sourcen bei Yocto.
Ein weiteres Problem beim „golden image“ ist, dass das OS nicht im „Initalzustand“ ist und möglicherweise viele Spuren von der „golden-image-Hardware“ beinhaltet. z.B. Möglicherweise SSH-Key-Fingerprint, alte bootup-logs,… bis zu möglichweise vergessenen Einträgen in der bash-history, die nicht mehr gelöscht wurden.

Fazit
Zusammenfassend könnte man auch sagen: Das fertige Debian/Raspbian kommt als fertiges Binary, es gibt einen Package-Manager (und somit viele Software-Pakete die leicht im laufenden Betrieb nachzuinstallieren sind), einen Fenster-Manager, und generell mit sehr üppiger Ausstattung. Es ist aber nicht in erster Linie für die Konfiguration von embedded Systemen konzipiert. Yocto-Linux hingegen ist selbst zu bauen, dafür bekommt man einen Cross-Compiler und ein flashbares sauberes Binary, ohne den Umweg über ein „golden Image“ gehen zu müssen. Hier ist alles für die Verwendung in einem embedded System konzipiert. Konzipiert man ein professionelles langlebiges Produkt, sollten die Vorteile der besseren Konfigurierbarkeit von Yocto überwiegen und den Nachteil der höheren Komplexität während der Entwicklung wettmachen.

Bei Fragen und Anregungen zu diesem Thema stehe ich Ihnen gerne zur Verfügung!
stefan.larndorfer@sequality.at

Weiterführende Links
[1] Yocto or Debian Whitepaper