Ein Daemon ist ein Diensteprozess, der im Hintergrund läuft und das System beaufsichtigt oder Funktionalitäten für andere Prozesse bereitstellt. Traditionell werden Daemons so implementiert, dass sie einem ursprünglich aus SysV-Unix stammenden Schema folgen. Moderne Daemons sollten einem einfacheren, aber dennoch leistungsfähigeren Schema folgen (hier »neuartige« Daemons genannt), wie es von systemd(1) implementiert wird. Diese Handbuchseite deckt beide Schemata ab und enthält insbesondere Empfehlungen für Daemons, die im Systemd-Init-System aufgenommen werden sollen.
Wenn ein traditioneller SysV-Daemon startet, sollte er die folgenden Schritte als Teil seiner Initialisierung ausführen. Beachten Sie, dass diese Schritte von neuartigen Daemons (siehe unten) nicht benötigt werden. Sie sollten daher nur implementiert werden, wenn die Kompatibilität zu SysV essenziell ist.
1.Alle offenen Dateideskriptoren außer der Standardeingabe, -ausgabe und -fehlerausgabe (d.h. den drei ersten Dateideskriptoren 0, 1, 2) werden geschlossen. Dies stellt sicher, dass kein versehentlich geöffneter Dateideskriptor im Daemon-Prozess verbleibt. Unter Linux wird dies am besten dadurch implementiert, dass durch /proc/self/fd iteriert wird, mit der Rückfalloption, von Dateideskriptor 3 bis zu dem von getrlimit() für RLIMIT_NOFILE zurückgelieferten Wert durchzuiterieren.
2.Alle Signal-Handler werden auf ihre Vorgaben zurückgesetzt. Dies erfolgt am besten durch Iteration durch alle verfügbaren Signale bis zur Grenze von _NSIG und dem Zurücksetzen dieser auf SIG_DFL.
3.Zurücksetzen der Signal-Maske mittels sigprocmask().
4.Bereinigen des Umgebungsblocks, Entfernen oder Zurücksetzen der Umgebungsvariablen, die einen negativen Einfluss auf die Laufzeit des Daemons haben könnten.
5.Aufrufen von fork(), um einen Hintergrundprozess zu erzeugen.
6.Aufruf von setsid() im Kind, um von jedem Terminal zu trennen und eine unabhängige Sitzung zu erzeugen.
7.Erneuter Aufruf von fork() im Kind, um sicherzustellen, dass der Daemon niemals wieder ein Terminal erlangen kann. (Dies ist relevant, falls das Programm und alle seine Abhängigkeiten nicht sorgfältig genug sind, um bei jedem »open()«-Aufruf, der möglicherweise einen TTY-Geräteknoten erlangen könnte, stets »O_NOCTTY« festzulegen.)
8.Aufruf von exit() im ersten Kind, so dass nur das zweite Kind (der eigentliche Daemon) verbleibt. Dies stellt sicher, dass der Daemon-Prozess PID 1 als neuen Elternprozess bekommt, wie das bei allen Daemons der Fall sein sollte.
9.Verbinden von /dev/null im Daemon-Prozess mit der Standardeingabe, -ausgabe und -fehlerausgabe.
10.Zurücksetzen der Umask auf 0 im Daemon-Prozess, so dass die an open(), mkdir() und ähnliche weitergegebenen Dateimodi direkt die Zugriffsmodi auf die erstellten Dateien und Verzeichnisse steuern.
11.Ändern des aktuellen Verzeichnisses im Daemon-Prozess auf das Wurzelverzeichnis (/), um zu vermeiden, dass der Daemon unfreiwillig die Einhängepunkte vor dem Aushängen blockiert.
12.Schreiben der Daemon-PID (wie von getpid() zurückgeliefert) im Daemon-Prozess in eine PID-Datei, beispielsweise /run/foobar.pid (für einen hypothetischen Daemon »foobar«), um sicherzustellen, dass der Daemon nicht mehr als einmal gestartet wird. Dies muss so implementiert werden, dass kein Ressourcenwettlauf möglich ist, so dass die PID-Datei nur aktualisiert wird, wenn gleichzeitig sichergestellt ist, dass die vorher in der PID-Datei gespeicherte PID nicht mehr existiert oder zu einem fremden Prozess gehört.
13.Abgeben der Privilegien im Daemon-Prozess, falls möglich und zutreffend.
14.Benachrichtigen des ursprünglich gestarteten Prozesses aus dem Daemon-Prozess, dass die Initialisierung abgeschlossen ist. Dies kann über eine unbenannte Pipe oder einen ähnlichen Kommunikationskanal, der vor dem ersten fork() erstellt wurde und daher in sowohl dem ursprünglichen als auch dem Daemon-Prozess verfügbar ist, erfolgen.
15.Aufrufen von exit() im ursprünglichen Prozess. Der Prozess, der den Daemon gestartet hat, muss in der Lage sein, sich darauf zu verlassen, dass dieser exit() passiert, nachdem die Initialisierung abgeschlossen und die externen Kommunikationskanäle etabliert und zugreifbar sind.
Die BSD-Funktion daemon() sollte nicht verwandt werden, da sie nur eine Teilmenge dieser Schritte implementiert.
Ein Daemon, der Kompatibilität mit SysV-Systemen bereitstellen muss, sollte das oben dargestellte Schema implementieren. Es wird allerdings empfohlen, dieses Verhalten über eine Befehlszeilenoption optional und konfigurierbar zu gestalten, um die Fehlersuche zu erleichtern und um die Integration in Systeme, die Systemd verwenden, zu erleichtern.
Moderne Dienste für Linux sollten als neuartige Daemons implementiert werden. Dies erleichtert die Beaufsichtigung und Steuerung zur Laufzeit und vereinfacht ihre Implementierung.
Zur Entwicklung eines neuartigen Daemons muss keiner der für SysV-Daemons empfohlenen Initialisierungsschritte implementiert werden. Bei neuartigen Init-Systemen wie Systemd sind diese alle redundant. Da einige dieser Schritte sogar die Prozessüberwachung, Dateideskriptorweitergabe und andere Funktionalitäten des Diensteverwalters stören, wird empfohlen, sie nicht auszuführen, wenn sie als neuartiger Dienst ausgeführt laufen.
Beachten Sie, dass neuartige Init-Systeme die Ausführung eines Daemon-Prozesses in einem sauberen Prozesskontext garantieren: Es wird garantiert, dass der Umgebungsblock bereinigt ist, dass Signal-Handler und -Masken zurückgesetzt sind und dass kein übrig gebliebener Dateideskriptor weitergegeben wird. Daemons werden in ihrer eigenen Sitzung ausgeführt, wobei die Standardeingabe mit /dev/null und die Standardausgabe/Standardfehlerausgabe mit dem Protokollierungsdienst systemd-journald.service(8) verbunden ist, außer dies ist anders konfiguriert. Die Umask ist zurückgesetzt.
Es wird empfohlen, dass neuartige Daemons folgendes implementieren:
1.Falls zutreffend sollte der Daemon über den Abschluss des Startens oder Statusaktualisierungen den Diensteverwalter mittels der Schnittstelle sd_notify(3), insbesondere READY=1 und STATUS=…, informieren.
2.If SIGTERM is received, shut down the daemon and exit cleanly. A STOPPING=1 notification should be sent via sd_notify(3).
3.If SIGHUP is received, reload the configuration files, if this applies. This should be combined with notifications via sd_notify(3): RELOADING=1 and READY=1.
4.Es soll ein korrekter Exit-Code vom Haupt-Daemon-Prozess bereitgestellt werden, da dieser vom Diensteverwalter benutzt wird, um Dienstefehler und Probleme zu erkennen. Es wird empfohlen, dem in LSB-Empfehlungen für SysV-Init-Skripte[1] definierten Exit-Code-System zu folgen.
5.Falls möglich und zutreffend sollte die Steuerschnittstelle mittels des D-Bus-IPC-Systems zugänglich gemacht und ein Bus-Name als letzten Schritt in der Initialisierung erlangt werden.
6.Stellen Sie eine .service-Unit-Datei zur Integration mit Systemd bereit, die Informationen zum Starten, Beenden und anderseitigen Verwalten des Daemons enthält. Siehe
systemd.service(5) für Details.
7.Verlassen Sie sich soweit möglich auf die Funktionalitäten des Diensteverwalters, den Zugriff des Daemons auf Dateien, Dienste und andere Ressourcen zu begrenzen, d.h. im Falle von Systemd, verlassen Sie sich auf Systemds Ressourenbegrenzungssteuerung statt Ihre eigene zu implementieren, verlassen Sie sich auf den Privilegienabgabe-Code von Systemd, statt ihn in Ihrem Daemon zu implementieren und so weiter. Siehe
systemd.exec(5) für die verfügbaren Steuerungen.
8.Falls D-Bus verwandt wird, ermöglichen Sie die Bus-Aktivierung Ihres Daemons, indem Sie eine D-Bus-Dienste-Aktivierungsdatei bereitstellen. Dies hat eine Reihe von Vorteilen: Ihr Daemon kann erst bei Bedarf gestartet werden; er kann parallel zu anderen Daemons, die ihn benötigen, gestartet werden — wodurch die Parallelisierung und die Systemstartgeschwindigkeit maximiert wird; Ihr Daemon kann im Fehlerfall neu gestartet werden, ohne eine Bus-Anfrage zu verlieren, da die Bus-Warteschlagen aktivierbare Dienste erbitten. Siehe unten für Details.
9.Falls Ihr Daemon Dienste für andere lokale Prozesse oder Clients in der Ferne über ein Socket anbietet, sollte es gemäß des nachfolgend dargestellten Schemas Socket-aktivierbar sein. Ähnlich der D-Bus-Aktivierung ermöglicht dies bedarfsgesteuertes Starten von Diensten sowie verbesserte Parallelisierung beim Starten der Dienste. Für zustandslose Protokolle (wie Syslog, DNS) kann ein Daemon, der Socket-basierte Aktivierung implementiert, auch ohne Verlust einer einzelnen Anfrage neu gestartet werden. Siehe unten für Details.
10.If the service opens sockets or other files on it own, and those file descriptors shall survive a restart, the daemon should store them in the service manager via sd_notify(3) with FDSTORE=1..
11.Statt den Aufruf
syslog() zum direkten Protokollieren in den Systemprotokolldienst zu verwenden, kann ein neuartiger Daemon einfach auf die Standardfehlerausgabe mittels
fprintf() protokollieren, was dann an Syslog weitergeleitet wird. Falls Protokollierungsstufen notwendig sind, können diese durch Voranstellen von Zeichenketten wie »<4>« (für Protokollierungsstufe 4 »WARNING« im Syslog-Prioritätsschema) vor individuellen Protokollierzeilen kodiert werden. Dies folgt einem ähnlichen Stil wie das
printk()-Stufensystem des Linux-Kernels. Für Details siehe
sd-daemon(3) und
systemd.exec(5).
12.Da neuartige Daemons ohne ein steuerndes TTY (aber sie selbst als ihren eigenen Sitzungsleiter) aufgerufen werden, sollte sichergestellt werden, dass bei
open(2)-Aufrufen, die möglicherweise einen TTY-Geräteknoten referenzieren, stets
O_NOCTTY festgelegt wird, so dass nicht versehentlich ein steuerndes TTY erlangt wird.
Diese Empfehlungen sind ähnlich zu, aber nicht identisch mit den Apple-MacOS-X-Daemon-Anforderungen[2].