Skip to content
Zurück zum Blog
· 4 Min. Lesezeit

systemd auf Embedded-Systemen: was sich lohnt und was schadet

systemd im Embedded-Einsatz: Units und Abhängigkeiten richtig schneiden, Boot-Zeit statt Reihenfolge optimieren, Dienste per Watchdog überwachen, das Journal vom Flash fernhalten und mit read-only Root-Dateisystem arbeiten.

Diagramm zu systemd auf Embedded-Systemen: links der Startvorgang mit parallelen Units, Socket-Aktivierung und Targets statt starrer Reihenfolge, rechts die Betriebsaspekte Watchdog-Überwachung, Restart-Strategie, flüchtiges Journal im RAM und read-only Root-Dateisystem mit beschreibbaren Overlays.

Kurz gesagt: systemd ist auf Embedded-Systemen kein Selbstzweck, aber auch nicht der Ballast, für den es oft gehalten wird. Der Gewinn liegt weniger im schnelleren Start als in den Dingen, die man sonst mühsam in Shell-Skripten nachbaut: Abhängigkeiten zwischen Diensten, definiertes Verhalten im Fehlerfall, Watchdog-Überwachung, Ressourcengrenzen. Der Schaden entsteht dort, wo Voreinstellungen aus der Serverwelt unverändert auf ein Gerät mit Flash-Speicher treffen.

Was systemd auf einem Gerät tatsächlich beiträgt

Die verbreitete Begründung „schnellerer Boot” greift zu kurz. Interessanter sind vier andere Punkte:

Abhängigkeiten werden explizit. In einer Unit steht, was vorher laufen muss und was danach kommt. In einer gewachsenen Sammlung von Init-Skripten steckt dieselbe Information in Nummernpräfixen und sleep-Aufrufen – und niemand traut sich, daran etwas zu ändern.

Fehlerverhalten ist konfiguriert, nicht programmiert. Restart=on-failure mit RestartSec und StartLimitBurst beschreibt in drei Zeilen, was sonst eine Watchdog-Schleife im Skript wäre, die man beim nächsten Problem erst wieder verstehen muss.

Ressourcen sind begrenzbar. Über die cgroup-Anbindung lassen sich Speicher und CPU-Anteil pro Dienst deckeln. Auf einem Gerät, das nicht swappen kann, ist das der Unterschied zwischen einem Dienst, der abstürzt, und einem System, das stehen bleibt.

Der Hardware-Watchdog wird bedient. systemd kann sowohl den Watchdog der Plattform füttern als auch von einzelnen Diensten regelmäßige Lebenszeichen verlangen – ohne dass dafür eigener Code entsteht.

Units schneiden, ohne sich zu verrennen

Eine typische Anwendungs-Unit ist kürzer, als viele erwarten:

[Unit]
Description=Device control application
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
ExecStart=/usr/bin/devicectl
Restart=on-failure
RestartSec=2s
WatchdogSec=30s
MemoryMax=128M

[Install]
WantedBy=multi-user.target

Zwei Details entscheiden über das Verhalten im Feld. Type=notify bedeutet, dass der Dienst per sd_notify(READY=1) selbst meldet, wann er arbeitsfähig ist – erst dann starten Units, die von ihm abhängen. Ohne das gilt ein Dienst als bereit, sobald sein Prozess existiert, was bei allem mit Initialisierungsphase zu Startproblemen führt, die sich sporadisch anfühlen.

WatchdogSec verlangt vom Dienst ein regelmäßiges sd_notify(WATCHDOG=1). Bleibt es aus, greift die konfigurierte Reaktion – vom Neustart des Dienstes bis zum Reboot des Geräts. Das ersetzt den handgeschriebenen Überwachungsprozess, den es in fast jedem gewachsenen System gibt.

Boot-Zeit: messen statt vermuten

Wer die Startzeit verkürzen will, sollte nicht raten. systemd-analyze blame listet die Units nach Dauer, systemd-analyze critical-chain zeigt die Kette, die den Start tatsächlich verlängert. Der Unterschied ist wesentlich: Eine Unit kann acht Sekunden brauchen und trotzdem irrelevant sein, weil sie parallel zu etwas Längerem läuft.

Die üblichen Funde sind unspektakulär und wiederholen sich: ein Dienst, der auf network-online.target wartet, obwohl er das Netz erst später braucht; ein sleep aus der Init-Skript-Ära, das jemand vorsichtshalber übernommen hat; ein Dienst in der kritischen Kette, der eigentlich per Socket-Aktivierung starten könnte, sobald ihn zum ersten Mal jemand anspricht. Mehr zum Gesamtbild steht im Artikel zur Boot-Zeit-Optimierung – systemd ist dort nur einer von mehreren Abschnitten, und selten der größte.

Die zwei Voreinstellungen, die Geräten schaden

Das Journal auf dem Flash. Standardmäßig schreibt journald nach /var/log/journal, sofern das Verzeichnis existiert. Auf einem Server ist das richtig. Auf einem Gerät mit eMMC oder SD-Karte bedeutet es kontinuierliche Schreibzugriffe über Jahre. Storage=volatile hält das Journal im RAM; wo persistente Logs nötig sind, gehören SystemMaxUse und eine bewusst gewählte Partition dazu. Verschlissene Flash-Speicher nach drei Jahren Feldeinsatz lassen sich oft bis auf diese eine Voreinstellung zurückführen.

Ein beschreibbares Root-Dateisystem. Ein Gerät, das jederzeit die Spannung verlieren kann, fährt mit read-only Root deutlich sicherer. systemd kommt damit gut zurecht: /etc lässt sich per Overlay beschreibbar halten, /var als tmpfs führen, und was wirklich persistent sein muss, liegt auf einer eigenen Datenpartition. Der Aufwand entsteht einmal beim Aufsetzen des Yocto-Images und spart die Klasse von Fehlern, bei denen ein Gerät nach einem Stromausfall nicht mehr hochkommt.

Wann BusyBox init die bessere Wahl bleibt

Nicht jedes Gerät braucht das. Ein Sensorknoten mit 32 MB RAM, drei Prozessen und einer festen Startreihenfolge fährt mit BusyBox init einfacher, kleiner und leichter zu überblicken. Die Grenze verläuft weniger an der Speichergröße als an der Komplexität: Sobald Dienste voneinander abhängen, im Fehlerfall definiert reagieren sollen und überwacht werden müssen, baut man sonst Stück für Stück ein schlechteres systemd nach.

Wenn Sie vor dieser Entscheidung stehen oder ein bestehendes System aufräumen wollen, sehen wir uns das gern gemeinsam an – im Rahmen unserer Embedded-Linux-Entwicklung oder als Schulung, in der Units, Abhängigkeiten und Boot-Analyse an Ihrer eigenen Plattform durchgegangen werden.

Häufige Fragen

Ist systemd für kleine Embedded-Systeme nicht zu schwergewichtig?
Das kommt auf die Klasse an. Auf einem Gerät mit wenigen Megabyte RAM und einer Handvoll Dienste ist BusyBox init schlanker und völlig ausreichend. Sobald aber Abhängigkeiten zwischen Diensten, Neustarts im Fehlerfall, Watchdog-Überwachung oder Ressourcenbegrenzung gefragt sind, schreibt man diese Dinge sonst in Shell-Skripten nach – meist schlechter als systemd es kann. Ab etwa 64 MB RAM und einem Dutzend Diensten spricht wenig dagegen.
Wie verhindere ich, dass das Journal den Flash-Speicher verschleißt?
Mit `Storage=volatile` in der `journald.conf` bleibt das Journal im RAM und überlebt keinen Neustart – für viele Geräte genau richtig, weil Logs ohnehin an einen Server gehen. Wird persistentes Logging gebraucht, begrenzt man es über `SystemMaxUse` und `RuntimeMaxUse` und legt es bewusst auf eine Partition, deren Verschleiß eingeplant ist. Unbegrenztes Schreiben auf eine eMMC ist einer der häufigsten Gründe für Ausfälle nach Jahren im Feld.
Warum startet mein Dienst zu früh, obwohl After= gesetzt ist?
`After=` regelt nur die Reihenfolge, nicht die Bereitschaft. Der vorherige Dienst gilt als gestartet, sobald sein Prozess läuft – nicht, sobald er arbeitsfähig ist. Wer echte Bereitschaft braucht, nutzt `Type=notify` und lässt den Dienst per `sd_notify` melden, wann er so weit ist. Für Netzwerkabhängigkeiten ist `network-online.target` das richtige Ziel, nicht `network.target`.
Bringt systemd wirklich eine kürzere Boot-Zeit?
Nicht automatisch, aber es macht die Boot-Zeit analysierbar und parallelisierbar. `systemd-analyze blame` und `critical-chain` zeigen, welche Unit tatsächlich die Kette verlängert – oft eine ganz andere als vermutet. Der eigentliche Gewinn entsteht durch Socket-Aktivierung und das Entfernen unnötiger Abhängigkeiten, nicht durch den Wechsel des Init-Systems an sich.

Alexander Nassian

Geschäftsführer, bitshift dynamics

Entwickelt mit seinem Team hardwarenahe Software für Embedded-Produkte – C++, Qt/QML, Embedded Linux und das Yocto Project. bitshift dynamics arbeitet seit 2005 in diesem Feld.

Verwandte Beiträge

· 6 Min. Lesezeit

Boot-Zeit von Embedded Linux optimieren: messen, bevor Sie schrauben

Von Power-on bis zum ersten Bild: wie Sie die Boot-Zeit eines Embedded-Linux-Geräts belastbar messen und in Bootloader, Kernel und User-Space die Hebel finden, die tatsächlich Sekunden bringen.

  • Embedded Linux
  • Boot-Zeit
  • Performance
Weiterlesen
· 5 Min. Lesezeit

Linux-Treiber entwickeln: wann der Kernel nötig ist – und wann nicht

Bevor Sie einen Kernel-Treiber schreiben: Welche Geräte sich sauber aus dem Userspace bedienen lassen, wann ein Kernel-Treiber unvermeidlich wird, wie ein Platform-Treiber aufgebaut ist und welche Fehler beim Bring-up am meisten Zeit kosten.

  • Embedded Linux
  • Kernel
  • Treiber
Weiterlesen

Steht bei Ihnen eine ähnliche Aufgabe an?

Wir begleiten Embedded-Teams genau bei solchen Fragestellungen – von der Architekturentscheidung bis zur Serienreife. Schildern Sie uns kurz Ihr Vorhaben.

Projekt anfragen