Aus Linux-Magazin 08/2022

Linux ins Internet der Dinge integrieren

© Rohmaterial Grafik: Siarhei Yurchanka / 123RF.com

Egal ob nur 1 MByte Flash-Speicher, Docker-Images, komplexe Systeme mit einem grafischen Stack oder eine Datenbank – mithilfe von Buildroot richten Sie schnell ein funktionierendes Betriebssystem ein.

Um die Software für ein Linux-basiertes IoT-System zusammenzustellen, müssen Sie ein Basissystem schnell und einfach zum Laufen bringen. Dabei gilt es, den Flash-Footprint im Auge zu behalten – einige IoT-Plattformen verfügen nur über 64 MByte Flash. Daneben wollen Sie die Kontrolle über die enthaltene Software behalten und Ihre eigenen Anwendungen einfach hinzufügen. Nicht zuletzt müssen Sie außerdem auf Sicherheit achten und Open-Source- und proprietäre Lizenzen einhalten.

Buildroot [1] vereint als Distribution alle diese Aspekte. Es ging in den frühen 2000er-Jahren aus µClinux und Busybox hervor – Projekte, die Linux auf kleine Prozessoren mit wenig Arbeitsspeicher und Speicherplatz transportierten. Deswegen legt Buildroot bis heute großen Wert auf einen minimalen Footprint. Wie bei anderen Build-Systemen steckt dahinter eine Paketsammlung. In einer auf dem Menüsystem des Linux-Kernels basierenden Auswahl legen Sie die benötigten Pakete und die zugehörigen Konfigurationsmöglichkeiten fest. In der Regel geht es dabei darum, optionale Komponenten wegzulassen, was den Flash-Footprint minimiert.

Dann gilt es, jedes Paket herunterzuladen, zu patchen, zu konfigurieren, zu kompilieren und schließlich zu installieren (Abbildung 1). Zusätzlich lassen sich auf Wunsch einige Metadaten erzeugen: ein Manifest der installierten Pakete, Lizenzinformationen (»legal-info«), der Platzbedarf jedes Pakets (»graph-size«) und die Abhängigkeiten zwischen den Komponenten (»graph-depends«). Sie generieren alle Pakete aus dem Quellcode, was zu maximaler Kontrolle über die Konfiguration führt.

Abbildung 1: Nachdem Sie die Pakete heruntergeladen und gepatcht haben, braucht es vor dem Installieren noch die richtige Konfiguration und etwas Kompilierung.

Abbildung 1: Nachdem Sie die Pakete heruntergeladen und gepatcht haben, braucht es vor dem Installieren noch die richtige Konfiguration und etwas Kompilierung.

Anders als zum Beispiel Ubuntu Core und Linux from Scratch basiert Buildroot auf Cross-Compilation. Das Kompilieren erfolgt also auf einer anderen Prozessorarchitektur als der des Systems, auf dem das Resultat letztendlich läuft. Beispielsweise erfolgt der Build auf einer x86-Architektur, aber das Zielsystem ist ARM. Für viele Prozessorarchitekturen stellt das den einzig realistischen Weg der Kompilierung dar, da das Zielsystem oft viel zu langsam ist und zu wenig Speicher mitbringt. Zusätzlich können Sie mit einer anderen Standard-C-Bibliothek (etwa Musl oder uClibc) und einem anderen Linux-Kernel arbeiten.

Das Cross-Compiling birgt jedoch Herausforderungen. Ein Großteil der von Buildroot geleisteten Arbeit besteht darin, diese durch spezielle Kompilierungsoptionen oder Quellcode-Patches zu überwinden. Das Cross-Compiling erfordert eine spezielle Toolchain, die aus dem Compiler, Linker und Assembler für das Zielsystem, den Linux-Kernel-Headern, der C- und C++-Standardbibliothek und optional dem Cross-Debugger besteht. Buildroot erstellt eine solche für die Zielplattform optimierte Toolchain als Teil des Build-Prozesses. Alternativ laden und nutzen Sie eine bestehende Toolchain, wie die ARM GNU Toolchain oder die Bootlin-Toolchains.

Flexibilität

Zu den wichtigsten Grundsätzen von Buildroot zählt Flexibilität. Sie sollen alle möglichen Anpassungen am System vornehmen können, um genau das Ergebnis zu erhalten, das Sie benötigen. Das erreicht die Distribution in erster Linie durch die Auswahl der Pakete. So bietet Buildroot beispielsweise diverse grundlegende Auswahlmöglichkeiten für das Init-System (Systemd, klassisches SysVinit, dessen abgespeckte Busybox-Version oder OpenRC) und mehr als zehn verschiedene Webserver an. Buildroot unterstützt die C-Bibliotheken Glibc, Musl und uClibc. Darüber hinaus stehen Ihnen 15 verschiedene Root-Dateisysteme zur Verfügung: Neben Klassikern wie Ext4 und Embedded-spezifischen Varianten wie UBIFS für NAND-Flash zählen dazu so spezielle wie ein RAM-Dateisystem, das in den Linux-Kernel gelinkt wird.

Die Auswahl der Pakete speichern Sie in einer Konfigurationsdatei. Die kann ihrerseits auf eine Reihe von anderen Dateien verweisen, die die Anforderungen weiter spezifizieren. Einige Pakete wie der Linux-Kernel weisen ihre eigene Konfigurationsdatei auf. Darüber hinaus gibt es eine Reihe von Informationen, die zu kompliziert sind, um sie in der Konfigurationsdatei zu sichern, darunter Dateibesitz und -berechtigungen sowie Benutzernamen und Passwörter. Hierfür definieren Sie separate Dateien. Mithilfe des Rootfs kopieren Sie eine Verzeichnisstruktur (das Rootfs-Overlay) und führen nach dem Erstellen aller Pakete ein Skript aus. Die zusätzlichen Konfigurationsdateien liegen normalerweise im Verzeichnis »boards/«.

Abbildung 2 zeigt die Konfigurationsdateien, die in einem Beispielprodukt zum Einsatz kommen. Davon existieren zwei verschiedene Varianten mit jeweils einer »defconfig«: »bmax_b1« und »raspberrypi4_64«. Beide Varianten nutzen gemeinsam den größten Teil des Rootfs-Overlays im Verzeichnis »common/«, die vom Benutzer definierte Tabelle, sowie ein Post-Build-Skript, das Versions- und Plattforminformationen generiert. Die beiden Boards benötigen etwas unterschiedliche Konfigurationen für den Grafik-Stack, deswegen weisen sie jeweils ein eigenes Rootfs-Overlay auf.

Abbildung 2: Die Konfigurationsdateien gibt es hier in zwei Versionen mit jeweils einer »defconfig«: »bmax_b1« und »raspberrypi4_64«.

Abbildung 2: Die Konfigurationsdateien gibt es hier in zwei Versionen mit jeweils einer »defconfig«: »bmax_b1« und »raspberrypi4_64«.

Um Ihnen den Einstieg zu erleichtern, gibt es Beispielkonfigurationen für rund 230 gängige SBCs (Single Board Computer) und SoMs (System-on-Module) sowie etwa 40 Konfigurationen für die Simulation in Qemu. Dabei handelt es sich um Minimalkonfigurationen: Sie enthalten lediglich eine Toolchain, einen Kernel, einen Bootloader und eine Busybox. Falls erforderlich, bringen sie Firmware mit, etwa für einen Wi-Fi-Chip oder eine GPU. Das Ergebnis – üblicherweise ein SD-Karten-Image – packen Sie anschließend direkt auf das Board. Mit der SD-Karte booten Sie das Gerät und bekommen eine Shell, die Sie gegebenenfalls an Ihre Bedürfnisse anpassen. Solange Sie wissen, um welche CPU-Variante es sich handelt und welche Bootloader- und Kernel-Optionen (Device Tree) Sie verwenden müssen, genügt es, eine Basiskonfiguration aufzusetzen.

Buildroots Flexibilität erlaubt es sogar, nicht standardisierte, aber oft benötigte Funktionen zu integrieren. Dazu gehören ein nur lesbares Root-Dateisystem mit einer separaten beschreibbaren Partition, Aktualisierungen von Kernel und Rootfs zusammen mit A-B-Swapping zwischen zwei Partitionen und verifiziertes Booten unter Einsatz einer Hardware Root of Trust. All das ist zwar möglich, erfordert im Einzelfall aber harte Arbeit, um herauszufinden, wie man es auf die plattformspezifischen Anforderungen zuschneidet.

Einige Projekte nutzen Buildroot, um eine besser verwaltete und damit weniger flexible Distribution zu bauen. Dazu zählen unter anderem SkiffOS, DahliaOS, Recalbox, Batocera Linux und Home Assistant Operating System. Sie unterliegen einem bestimmten Anwendungsfall und bedienen sich der Flexibilität von Buildroot, um ihn bestmöglich zu erfüllen.

Eigene Anwendungen

Damit am Ende ein funktionierendes Produkt steht, muss die Applikation die Funktionalität des IoT-Geräts bereitstellen. Manchmal beschränkt sich das auf einige wenige Skripte wie PHP-Dateien, die Sie über den Webserver nutzen und die einfach Teil des Rootfs-Overlays sind. In der Regel müssen Sie jedoch eine oder mehrere eigene Komponenten kompilieren und installieren.

Buildroot bietet zwei Möglichkeiten, Ihre eigenen Komponenten zu kompilieren. Häufig setzen Sie für jede Ihrer Komponenten eine eigene Paketdefinition auf, die nur ein paar Zeilen umfasst, besonders wenn eines der unterstützten Build-Systeme zum Einsatz kommt (Abbildung 1). Für C/C++ sind dies Meson, Cmake, die Autotools, Qmake und Waf. Für andere Sprachen gibt es sprachspezifische Build-Systeme: Go für Golang, Cargo für Rust, Rebar für Erlang, Luarocks für Lua, MB oder EUMM für Perl. Python nimmt dabei eine Sonderrolle ein, da es mit Distutils, Setuptools, Pep517, Flit und Maturin gleich mehrere Build-Systeme gibt. Das umfangreiche Handbuch von Buildroot [2] klärt anschaulich darüber auf, wie Sie eine Paketdefinition erstellen und mithilfe welcher Variablen Sie den Build-Prozess feintunen.

Häufig wollen Entwickler vermeiden, eigene Paketdefinitionen mit den Open-Source-Paketen von Buildroot zu vermischen. Der »BR2_EXTERNAL«-Mechanismus unterstützt das, indem er erlaubt, Paketdefinitionen zu denen von Buildroot hinzuzufügen. Zusätzlich können Sie Ihre Konfiguration und weitere Dateien wie das Rootfs-Overlay im selben »BR2_EXTERNAL«-Repository speichern. So bleiben alle Ihre individuellen Anpassungen ordentlich zusammen.

Manchmal ist es bequemer, Anwendungscode außerhalb von Buildroot zu kompilieren. In größeren Projekten finden sich zum Beispiel Softwareentwickler, die nicht täglich mit Buildroot arbeiten. Zudem fällt es leichter, von einer IDE aus zu kompilieren, wenn Sie direkt mit einem der Entwicklungsumgebung bekannten Build-System arbeiten. Aus diesem Grund bietet Buildroot ein SDK an, das die Cross-Compilation-Toolchain mit allen konfigurierten Bibliotheken und Host-Tools enthält. Schließlich benötigen Sie einige dieser Host-Tools zum Kompilieren. »pkg-config« etwa definiert die Kompilieroptionen einer Bibliothek, »protoc« generiert den Code für eine Google-Protobuf-Definition. Das SDK steht als Tarball bereit, den Sie an beliebiger Stelle entpacken. Richten Sie die IDE so ein, dass sie den Cross-Compiler des SDK verwendet, können Sie direkt daraus kompilieren und debuggen.

Verwalten

Ist ein IoT-System grundlegend entwickelt, folgen in der Regel Aktualisierungen und neue Generationen. Entsprechend ist es wichtig, die zugrunde liegende Distribution durch Korrekturen für Sicherheitsprobleme sowie durch neue Funktionen zu pflegen.

Buildroot existiert bereits seit 2001 und ist damit die älteste der in dieser Ausgabe besprochenen IoT-Distributionen. Die Kontinuität des Projekts hat sich also durchaus bewährt. Seit 2009 halten die Entwickler einen festen Veröffentlichungsrhythmus mit vier Veröffentlichungen pro Jahr ein: 20XX.02, 20XX.05, 20XX.08 und 20XX.11. Jedes Release durchläuft eine einmonatige Stabilisierungsphase, in der nur Korrekturen stattfinden, und wird anschließend drei Monate lang gewartet (Fehlerbehebungen und Sicherheitsaktualisierungen). Eine kleinere Version erscheint etwa monatlich. Bei der Version 20XX.02 handelt es sich um eine LTS-Version (Long Term Support), die etwas mehr als ein Jahr lang gewartet wird und von der erneut monatlich kleinere Versionen hinzukommen.

Buildroot funktioniert Community-getrieben, zu jedem Release tragen etwa hundert Personen mit insgesamt 1500 Änderungen pro Version bei. Die Community ist hauptsächlich über die Mailingliste (»lists.buildroot.org«) und im IRC »#buildroot« auf Open and Free Technology Community (OFTC) aktiv. Zwei Mal im Jahr findet ein Entwicklertreffen statt, auf dessen Agenda Probleme und neue Funktionen stehen. Zudem diskutiert und beschließt die Community in diesem Rahmen kontroverse Änderungen. Hinter der Distribution steht kein Unternehmen, das die Kontrolle hat. Einige Unternehmen und freiberufliche Berater bieten jedoch Dienstleistungen im Zusammenhang damit an. Sie helfen Kunden dabei, das Betriebssystem mit allen notwendigen Funktionen auf der IoT-Plattform zum Laufen zu bringen. Weitere Informationen finden sich auf der Buildroot-Website.

Einige Tools unterstützen Sie dabei, Buildroot zu warten. Mithilfe von »make pkg-stats« halten Sie die Pakete in Buildroot aktuell. Der Aufruf sammelt Versionsinformationen über die von Ihnen ausgewählten Pakete, sucht nach neueren Versionen im Netz, prüft, ob über die NIST National Vulnerability Database Common Vulnerabilities and Exposures (CVE) gemeldet wurden, und gibt die Ergebnisse in HTML und JSON aus. Dieser Bericht erscheint wöchentlich für alle Pakete [3]. Führen Sie ihn für Ihre eigene Konfiguration aus, erhalten Sie einen personalisierten Report.

Daneben laufen kontinuierlich weitere Tests ab – 440 davon pro Woche. Die 270 Konfigurationen für SBCs, SoMs und Qemu, die mit Buildroot gebündelt sind, werden ebenfalls wöchentlich erstellt. Schließlich gibt es noch zehn Build-Server, die stetig beliebige Konfigurationen erzeugen. Die Berichte dieser Server sind online verfügbar [4].

Fazit

Buildroot hat sich über die Jahre zu einem Build-System entwickelt, das sich in der Embedded-Welt etabliert und bewährt hat. Es unterstützt zahlreiche Anwendungsfälle von Systemen mit nur 1 MByte Flash-Speicher über Docker-Images für die Cloud-Bereitstellung bis hin zu komplexen Systemen mit einem grafischen Stack oder einer Datenbank. Die Prinzipien der Einfachheit, Flexibilität, Reproduzierbarkeit und Wartbarkeit machen es zu einer für IoT-Systeme hervorragend geeigneten Distribution. Nachdem Sie Buildroot über sein Git-Repository [5] geklont haben, beansprucht das Konfigurieren kaum mehr als eine Stunde. Am Ende steht ein perfekt funktionierendes Betriebssystem. (csi)

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben