Dienst im Hintergrund

Martin Kompf

Bei einigen meiner Projekte zur Datenerfassung mit dem Raspberry Pi ist es notwendig, Programme im Hintergrund zu starten, so dass sie auch nach dem Abmelden des Benutzers vom Rechner weiterlaufen. Dafür gibt es eine Vielzahl von Möglichkeiten, von denen ich im folgenden einige zeigen möchte. Sie funktionieren nicht nur auf dem Raspberry Pi, sondern auf jedem modernen Linux System.

Das Problem

Ausgangspunkt könnte zum Beispiel das Projekt zum Auslesen des Wasserzählers sein. Hierbei muss das Datenerfassungsprogramm wairc.py ständig laufen, um Daten vom Arduino abzuholen und in die Datenbank zu schreiben. Wenn man per ssh oder einem Desktop-Terminalfenster eine Shell auf dem Raspberry Pi geöffnet hat, dann startet die Eingabe von

./wairc.py

das Kommando im Vordergrund. Nach einer kurzen Ausgabe passiert dann nichts mehr, das Programm tut seine Arbeit, man kann aber auch keine weiteren Eingaben an die Shell absetzen.

Das trifft nicht nur für wairc.py, sondern für viele weitere Programme zu. Sie können zum Nachvollziehen der folgenden Beispiele auch den Befehl

vmstat 10

verwenden, der in einer Endlosschleife aller 10 Sekunden den Systemstatus ausgibt.

Solche den Vordergrund blockierenden Prozesse lassen sich generell per Tastenkombination Strg-C terminieren, so dass man wieder die Kontrolle über die Shell bekommt und weitere Kommandos absetzen kann. Allerdings läuft jetzt auch die Datenerfassung nicht mehr - was also tun?

nohup

Die Lösung besteht darin, das Programm im Hintergrund zu starten. Die einfachste Möglichkeit dafür ist die Verwendung von nohup und dem Ampersand-Operator &

nohup kommando &
nohup ./wairc.py &
nohup vmstat 10 &

nohup: ignoring input and appending output to 'nohup.out'

Danach kann man ganz normal mit der Shell weiterarbeiten, das Kommando verrichtet seine Arbeit im Hintergrund. Seine Ausgabe landet dabei in der Datei nohup.out. Ein per nohup gestarteter Prozess überlebt auch das Beenden der ssh Session oder den Logout vom Desktop. Nach dem erneuten Anmelden gibt das Kommando ps ax Auskunft über die laufenden Prozesse:

ps ax | grep vmstat
  PID TTY          TIME CMD
32282 ?        S      0:00 vmstat 10
...

Die hier ausgegebene PID hilft bei der gezielten Beendigung des Hintergrundprogramms per kill:

kill 32282

screen

Eine andere Möglichkeit ist die Verwendung eines Terminal-Multiplexers, wie zum Beispiel screen. Er ist in der Regel auf dem Raspberry Pi nicht standardmäßig installiert, Abhilfe schafft ein

sudo apt install screen

Durch Eingabe von screen startet man eine neue Screen-Sitzung. Sie unterscheidet sich auf den ersten Blick nicht von einer normalen Shell, die wichtigen Befehle verstecken sich hinter kryptischen Tastenkombinationen, die wichtigsten sind:

Nach dem Starten einer Sitzung per

screen

kann man das Datenerfassungsprogramm im Vordergrund starten und sieht dann auch seine Ausgabe. Per Strg-A gefolgt von C öffnet sich ein zweites Fenster, in dem man ganz normal an der Shell weiterarbeiten kann. Zurück zum ersten Fenster geht es per Strg-A gefolgt von Leertaste.

Strg-A gefolgt von D beendet screen, man kann sich nun beruhigt ausloggen, denn die innerhalb von screen gestarteten Programme laufen weiter. Nach dem erneuten Login nimmt

screen -r

die Verbindung zur letzten Sitzung wieder auf, die Tastenkombination Strg-A gefolgt von Leertaste navigiert durch die einzelnen Fenster der Sitzung. Hier findet sich dann auch das laufende Datenerfassungsprogramm.

Sowohl nohup als auch screen haben den Nachteil, dass das Betriebssystem alle auf diese Art gestarteten Programme beim Reboot des Systems beendet und nicht wieder automatisch neu startet! Will man sich also nicht nach jedem Neustart des Raspberry Pi einloggen und händisch alle Programme starten, dann sollte ein anderer Mechanismus zur Anwendung kommen.

cron @reboot

Neuere Versionen des Zeitplandienstes cron erlauben die Angabe des Zeitplans @reboot. Cron führt dann das angegebene Programm beim Neustart des Systems aus. Das Editieren der crontab erfolgt mit dem Kommando crontab -e, in den sich öffnenden Editor gibt man dann zum Beispiel

@reboot $HOME/water-counter/wairc.py >> $HOME/water-counter/wairc.log 2>&1

ein, um das Programm wairc.py automatisch beim Start auszuführen. Durch die Ausgabeumleitung gelangen Ausgaben und Fehlermeldungen in eine Logdatei. Leider gibt es keine komfortable Möglichkeit, ein vom cron gestartetes Programm zu beenden und neu zu starten, hierzu muss man wieder auf die oben gezeigten Möglichkeiten per kill und nohup zurückgreifen. Auch kümmert sich cron nicht darum, wenn das Programm abstürzt.

systemd

Die zuletzt genannten Nachteile behebt systemd, der auf modernen Linuxen inklusive Raspberry Pi OS aktiv ist. Es handelt sich um einen System-Dienst, der das Starten weiterer Programme beziehungsweise Dienste vornimmt und überwacht.

Die Organisation von systemd erfolgt über Units. Zur Definition einer Unit dient eine spezielle Datei. Für unser Beispiel, das Starten des Datenerfassungsprogramms wairc.py, erfolgt die Definition der Unit in der Datei wairc.service:

[Unit]
Description=Water meter service
After=network-online.target

[Service]
Type=simple
Restart=on-failure
RestartSec=5
WorkingDirectory=/home/pi/water-counter
ExecStart=/home/pi/water-counter/wairc.py
User=pi
Group=dialout

[Install]
WantedBy=multi-user.target

Wichtig sind vor allem die Einträge im Abschnitt [Service]. Hier stehen der Pfad zum ausführbaren Programm, das Arbeitsverzeichnis sowie Benutzer und Gruppe, mit deren Rechten das Programm abläuft. Die mit Restart beginnenden Einträge legen fest, dass systemd den Dienst nach einem Absturz automatisch nach fünf Sekunden neu startet.

Die Unit-Datei gehört auf dem Raspberry Pi in das Verzeichnis /etc/systemd/system/

sudo cp wairc.service /etc/systemd/system/

Die weitere Steuerung erfolgt mit dem Tool systemctl. Zunächst ist der Dienst zu aktiveren und dann zu starten:

sudo systemctl enable wairc.service
sudo systemctl start wairc

Weiterhin erlaubt systemctl Statusabfrage, Neustart und Beenden von Diensten, die man über den Namen der Unit (hier: wairc) identifiziert;

sudo systemctl status Unit
sudo systemctl restart Unit 
sudo systemctl stop Unit 

Auch die Verwaltung einer Logdatei übernimmt systemd, ihren Inhalt liefert das Tool journalctl:

journalctl -u Unit
journalctl -u wairc

Fazit

Während Entwicklung und Test einer neuen Software benutze ich zum Starten von Programmen im Hintergrund meistens den Ampersand-Operator & und nohup. Geht die Software dann in die Produktivphase, bietet systemd alles für einen stabilen Betrieb notwendige: Selbständige Neustarts bei Fehler und Reboot, Logging und Möglichkeiten zum händischen Eingriff per systemctl. Das Werkzeug ist weitaus mächtiger als hier gezeigt, zur weiteren Vertiefung empfiehlt sich der Besuch eines der folgenden Links.