PEP 3143 – Standard-Bibliothek für Daemon-Prozesse
- Autor:
- Ben Finney <ben+python at benfinney.id.au>
- Status:
- Verschoben
- Typ:
- Standards Track
- Erstellt:
- 26-Jan-2009
- Python-Version:
- 3.x
- Post-History:
Zusammenfassung
Das Schreiben eines Programms, das sich wie ein wohlverhaltensfähiger Unix-Daemon verhält, ist etwas komplex und knifflig, aber die Schritte sind für jeden Daemon weitgehend gleich, unabhängig davon, was das Programm sonst noch tun muss.
Dieses PEP führt ein Paket in die Standardbibliothek von Python ein, das eine einfache Schnittstelle zur Aufgabe eines Daemon-Prozesses bietet.
PEP Verschiebung
Die weitere Erforschung der in diesem PEP behandelten Konzepte wurde mangels eines aktuellen Vertreters, der an der Förderung der Ziele des PEP interessiert ist und Feedback sammelt und einarbeitet, sowie über ausreichend verfügbare Zeit zur effektiven Umsetzung, zurückgestellt.
Spezifikation
Beispielhafte Nutzung
Einfaches Beispiel für die direkte Verwendung von DaemonContext
import daemon
from spam import do_main_program
with daemon.DaemonContext():
do_main_program()
Komplexeres Anwendungsbeispiel
import os
import grp
import signal
import daemon
import lockfile
from spam import (
initial_program_setup,
do_main_program,
program_cleanup,
reload_program_config,
)
context = daemon.DaemonContext(
working_directory='/var/lib/foo',
umask=0o002,
pidfile=lockfile.FileLock('/var/run/spam.pid'),
)
context.signal_map = {
signal.SIGTERM: program_cleanup,
signal.SIGHUP: 'terminate',
signal.SIGUSR1: reload_program_config,
}
mail_gid = grp.getgrnam('mail').gr_gid
context.gid = mail_gid
important_file = open('spam.data', 'w')
interesting_file = open('eggs.data', 'w')
context.files_preserve = [important_file, interesting_file]
initial_program_setup()
with context:
do_main_program()
Interface
Ein neues Paket, daemon, wird zur Standardbibliothek hinzugefügt.
Eine Klasse, DaemonContext, wird definiert, um die Einstellungen und den Prozesskontext für das als Daemon-Prozess laufende Programm darzustellen.
DaemonContext Objekte
Eine DaemonContext-Instanz repräsentiert die Verhaltensweisen und den Prozesskontext des Programms, wenn es zu einem Daemon-Prozess wird. Das Verhalten und die Umgebung werden durch Setzen von Optionen auf der Instanz angepasst, bevor die open-Methode aufgerufen wird.
Jede Option kann als Schlüsselwortargument an den DaemonContext-Konstruktor übergeben oder anschließend zugewiesen werden, indem ein Attribut der Instanz jederzeit vor dem Aufruf von open gesetzt wird. Das heißt, für Optionen namens wibble und wubble gilt die folgende Einleitung:
foo = daemon.DaemonContext(wibble=bar, wubble=baz)
foo.open()
ist äquivalent zu
foo = daemon.DaemonContext()
foo.wibble = bar
foo.wubble = baz
foo.open()
Die folgenden Optionen sind definiert.
files_preserve- Standard:
Keine
Liste von Dateien, die beim Start des Daemons nicht geschlossen werden sollen. Wenn
None, werden alle offenen Dateideskriptoren geschlossen.Elemente der Liste sind Dateideskriptoren (wie sie von der
fileno()-Methode eines Dateiobjekts zurückgegeben werden) oder Python-file-Objekte. Jedes gibt eine Datei an, die während des Daemon-Starts nicht geschlossen werden soll.chroot_directory- Standard:
Keine
Vollständiger Pfad zu einem Verzeichnis, das als effektives Stammverzeichnis des Prozesses gesetzt werden soll. Wenn
None, wird angegeben, dass das Stammverzeichnis nicht geändert werden soll.working_directory- Standard:
'/'
Vollständiger Pfad zum Arbeitsverzeichnis, in das der Prozess beim Daemon-Start wechseln soll.
Da ein Dateisystem nicht ausgehängt werden kann, wenn ein Prozess sein aktuelles Arbeitsverzeichnis auf diesem Dateisystem hat, sollte dies entweder auf dem Standardwert belassen oder auf ein Verzeichnis gesetzt werden, das ein sinnvolles „Home-Verzeichnis“ für den laufenden Daemon darstellt.
umask- Standard:
0
Datei-Zugriffserstellungsmaske („umask“), die für den Prozess beim Daemon-Start gesetzt werden soll.
Da ein Prozess seine umask von seinem Elternprozess erbt, setzt der Daemon-Start die umask auf diesen Wert, damit von Daemons erstellte Dateien mit den von ihm erwarteten Zugriffsmodi erstellt werden.
pidfile- Standard:
Keine
Kontextmanager für eine PID-Lockdatei. Wenn der Daemon-Kontext geöffnet und geschlossen wird, tritt er in den
pidfile-Kontextmanager ein und verlässt ihn.detach_process- Standard:
Keine
Wenn
True, wird der Prozesskontext beim Öffnen des Daemon-Kontextes abgetrennt; wennFalse, wird nicht abgetrennt.Wenn bei der Initialisierung der Instanz nicht spezifiziert (
None), wird dies standardmäßig aufTruegesetzt, und nurFalse, wenn das Abtrennen des Prozesses als überflüssig erachtet wird; zum Beispiel, wenn der Prozess voninit,initdoderinetdgestartet wurde.signal_map- Standard:
- Systemabhängig
Zuordnung von Betriebssystemsignalen zu Callback-Aktionen.
Die Zuordnung wird beim Öffnen des Daemon-Kontextes verwendet und bestimmt die Aktion für den Signal-Handler jedes Signals.
- Ein Wert von
Noneignoriert das Signal (indem die Signalaktion aufsignal.SIG_IGNgesetzt wird). - Ein Zeichenkettenwert wird als Name eines Attributs auf der
DaemonContext-Instanz verwendet. Der Wert des Attributs wird als Aktion für den Signal-Handler verwendet. - Jeder andere Wert wird als Aktion für den Signal-Handler verwendet.
Der Standardwert hängt davon ab, welche Signale auf dem laufenden System definiert sind. Jedes Element aus der folgenden Liste, dessen Signal im
signal-Modul tatsächlich definiert ist, erscheint in der Standardzuordnung.signal.SIGTTIN:Nonesignal.SIGTTOU:Nonesignal.SIGTSTP:Nonesignal.SIGTERM:'terminate'
Abhängig davon, wie das Programm mit seinen Kindprozessen interagiert, muss möglicherweise eine Signalzuordnung angegeben werden, die das Signal
signal.SIGCHLD(empfangen, wenn ein Kindprozess beendet wird) enthält. Lesen Sie die Dokumentation Ihres spezifischen Betriebssystems für weitere Details, unter welchen Umständen Signal-Handler benötigt werden.uid- Standard:
os.getuid()
gid- Standard:
os.getgid()
Die Benutzer-ID („UID“) und Gruppen-ID („GID“), zu der der Prozess beim Daemon-Start wechseln soll.
Die Standardwerte, die reale UID und GID des Prozesses, geben jede effektive Berechtigungserhöhung auf, die der Prozess geerbt hat.
prevent_core- Standard:
True
Wenn wahr, verhindert die Erstellung von Core-Dateien, um das Auslaufen sensibler Informationen von Daemons, die als
rootlaufen, zu vermeiden.stdin- Standard:
Keine
stdout- Standard:
Keine
stderr- Standard:
Keine
Jedes von
stdin,stdoutundstderrist ein dateiähnliches Objekt, das als neue Datei für die Standard-I/O-Streamssys.stdin,sys.stdoutbzw.sys.stderrverwendet wird. Die Datei sollte daher geöffnet sein, mit einem Minimum von Modus ‘r’ fürstdinund Modus ‘w+’ fürstdoutundstderr.Wenn das Objekt eine
fileno()-Methode hat, die einen Dateideskriptor zurückgibt, wird die entsprechende Datei vom Schließen während des Daemon-Starts ausgeschlossen (d.h., sie wird so behandelt, als wäre sie infiles_preserveaufgeführt).Wenn
None, wird der entsprechende Systemstream an die Datei gebunden, die durchos.devnullbenannt ist.
Die folgenden Methoden sind definiert.
open()- Rückgabe:
Keine
Öffnet den Daemon-Kontext und verwandelt das aktuelle Programm in einen Daemon-Prozess. Dies führt die folgenden Schritte aus:
- Wenn die
is_open-Eigenschaft dieser Instanz wahr ist, kehrt sofort zurück. Dies macht es sicher,openmehrmals auf einer Instanz aufzurufen. - Wenn das Attribut
prevent_corewahr ist, werden die Ressourcenlimits für den Prozess gesetzt, um Core-Dumps zu verhindern. - Wenn das Attribut
chroot_directorynichtNoneist, wird das effektive Stammverzeichnis des Prozesses auf dieses Verzeichnis gesetzt (überos.chroot).Dies ermöglicht das Ausführen des Daemon-Prozesses innerhalb eines „chroot-Gefängnisses“ als Mittel zur Begrenzung der Systemexposition gegenüber Fehlverhalten des Prozesses. Beachten Sie, dass das angegebene Verzeichnis zu diesem Zweck bereits eingerichtet sein muss.
- Setzt die Prozess-UID und -GID auf die Werte der Attribute
uidundgid. - Schließt alle offenen Dateideskriptoren. Dies schließt diejenigen aus, die im Attribut
files_preserveaufgeführt sind, sowie diejenigen, die den Attributenstdin,stdoutoderstderrentsprechen. - Ändert das aktuelle Arbeitsverzeichnis in den Pfad, der durch das Attribut
working_directoryangegeben ist. - Setzt die Datei-Zugriffserstellungsmaske auf den Wert des Attributs
umaskzurück. - Wenn die Option
detach_processwahr ist, wird der aktuelle Prozess in seine eigene Prozessgruppe abgetrennt und von jeder steuernden Terminaltrennstelle getrennt. - Setzt die Signal-Handler wie im Attribut
signal_mapangegeben. - Wenn eines der Attribute
stdin,stdoutoderstderrnichtNoneist, werden die Systemstreamssys.stdin,sys.stdoutund/odersys.stderran die von den entsprechenden Attributen dargestellten Dateien gebunden. Wenn das Attribut einen Dateideskriptor hat, wird der Deskriptor dupliziert (anstatt den Namen neu zu binden). - Wenn das Attribut
pidfilenichtNoneist, tritt in seinen Kontextmanager ein. - Markiert diese Instanz als geöffnet (für zukünftige
open- undclose-Aufrufe). - Registriert die
close-Methode für den Aufruf während der Python-Exit-Verarbeitung.
Wenn die Funktion zurückkehrt, ist das laufende Programm ein Daemon-Prozess.
close()- Rückgabe:
Keine
Schließt den Daemon-Kontext. Dies führt die folgenden Schritte aus:
- Wenn die
is_open-Eigenschaft dieser Instanz falsch ist, kehrt sofort zurück. Dies macht es sicher,closemehrmals auf einer Instanz aufzurufen. - Wenn das Attribut
pidfilenichtNoneist, tritt aus seinem Kontextmanager aus. - Markiert diese Instanz als geschlossen (für zukünftige
open- undclose-Aufrufe).
is_open- Rückgabe:
True, wenn die Instanz geöffnet ist, andernfallsFalse.
Diese Eigenschaft gibt den Zustand an, ob die Instanz derzeit geöffnet ist. Sie ist
True, wenn dieopen-Methode der Instanz aufgerufen wurde und dieclose-Methode anschließend nicht aufgerufen wurde.terminate(signal_number, stack_frame)- Rückgabe:
Keine
Signal-Handler für das Signal
signal.SIGTERM. Führt den folgenden Schritt aus:- Löst eine
SystemExit-Ausnahme aus, die das Signal erklärt.
Die Klasse implementiert außerdem das Kontextmanager-Protokoll über die Methoden __enter__ und __exit__.
__enter__()- Rückgabe:
- Die
DaemonContext-Instanz
Ruft die
open()-Methode der Instanz auf und gibt dann die Instanz zurück.__exit__(exc_type, exc_value, exc_traceback)- Rückgabe:
TrueoderFalse, wie durch das Kontextmanager-Protokoll definiert.
Ruft die
close()-Methode der Instanz auf und gibt dannTruezurück, wenn die Ausnahme behandelt wurde, oderFalse, wenn nicht.
Motivation
Die Mehrheit der Programme, die als Unix-Daemons geschrieben sind, implementiert entweder ein Verhalten, das dem in der Spezifikation sehr ähnlich ist, oder sind schlecht erzogene Daemons gemäß dem korrekten Daemon-Verhalten.
Da diese Schritte bei den meisten Implementierungen gleich sein sollten, aber sehr spezifisch und leicht zu übersehen oder falsch zu implementieren sind, sind sie ein Paradebeispiel für eine standardmäßige, gut getestete Implementierung in der Standardbibliothek.
Begründung
Korrekte Daemon-Verhaltensweisen
Laut Stevens in [stevens] §2.6 sollte ein Programm die folgenden Schritte ausführen, um ein Unix-Daemon-Prozess zu werden:
- Schließt alle offenen Dateideskriptoren.
- Ändert das aktuelle Arbeitsverzeichnis.
- Setzt die Datei-Zugriffserstellungsmaske zurück.
- Läuft im Hintergrund.
- Trennung von der Prozessgruppe.
- Ignoriert Terminal-I/O-Signale.
- Trennung von der Steuertrommel.
- Erwirbt keine Steuertrommel erneut.
- Behandelt die folgenden Umstände korrekt:
- Gestartet durch den System V
init-Prozess. - Daemon-Beendigung durch
SIGTERM-Signal. - Kinder erzeugen
SIGCLD-Signal.
- Gestartet durch den System V
Das daemon-Tool [slack-daemon] listet (in seiner Zusammenfassung der Funktionen) Verhaltensweisen auf, die beim Umwandeln eines Programms in einen wohlverhaltensfähigen Unix-Daemon-Prozess durchgeführt werden sollten. Es unterscheidet sich von der Absicht dieses PEPs darin, dass es ein separates Programm als Daemon-Prozess aufruft. Die folgenden Funktionen sind für einen Daemon geeignet, der sich selbst startet, sobald das Programm bereits läuft:
- Richtet den korrekten Prozesskontext für einen Daemon ein.
- Verhält sich sinnvoll, wenn es von
initd(8)oderinetd(8)gestartet wird. - Entzieht alle suid- oder sgid-Berechtigungen, um Sicherheitsrisiken zu verringern, falls der Daemon falsch mit besonderen Berechtigungen installiert wird.
- Verhindert die Erstellung von Core-Dateien, um das Auslaufen sensibler Informationen von Daemons, die als root laufen, zu vermeiden (optional).
- Benennt den Daemon durch Erstellung und Sperrung einer PID-Datei, um zu garantieren, dass nur ein Daemon mit dem gegebenen Namen gleichzeitig ausgeführt werden kann (optional).
- Legt den Benutzer und die Gruppe fest, unter denen der Daemon ausgeführt werden soll (optional, nur root).
- Erstellt ein chroot-Gefängnis (optional, nur root).
- Erfasst stdout und stderr des Daemons und leitet sie an syslog (optional).
Ein Daemon ist kein Dienst
Dieses PEP befasst sich ausschließlich mit Unix-Style-Daemons, für die das oben genannte korrekte Verhalten relevant ist, im Gegensatz zu vergleichbaren Verhaltensweisen auf anderen Betriebssystemen.
Es gibt ein verwandtes Konzept in vielen Systemen, das als „Dienst“ bezeichnet wird. Ein Dienst unterscheidet sich vom Modell in diesem PEP, da anstatt dass das aktuelle Programm weiterhin als Daemon-Prozess läuft, ein Dienst einen zusätzlichen Prozess startet, der im Hintergrund läuft, und der aktuelle Prozess über definierte Kanäle mit diesem zusätzlichen Prozess kommuniziert.
Das Unix-Style-Daemon-Modell in diesem PEP kann unter anderem zur Implementierung des Hintergrundprozess-Teils eines Dienstes verwendet werden; dieses PEP befasst sich jedoch nicht mit den anderen Aspekten der Einrichtung und Verwaltung eines Dienstes.
Referenzimplementierung
Das Paket python-daemon [python-daemon].
Andere Daemon-Implementierungen
Vor diesem PEP implementierten mehrere bestehende Drittanbieter-Python-Bibliotheken oder -Tools einige der korrekten Daemon-Verhaltensweisen dieses PEP.
Die Referenzimplementierung ist ein ziemlich direkter Nachfolger der folgenden Implementierungen.
- Viele gute Ideen wurden von der Community zu Python Cookbook-Rezepten #66012 [cookbook-66012] und #278731 [cookbook-278731] beigetragen.
- Die Bibliothek
bda.daemon[bda.daemon] ist eine Implementierung von [cookbook-66012]. Sie ist der Vorgänger von [python-daemon].
Andere Python-Daemon-Implementierungen, die von diesem PEP abweichen.
- Das
zdaemon-Tool [zdaemon] wurde für das Zope-Projekt geschrieben. Wie [slack-daemon] unterscheidet es sich von dieser Spezifikation, da es verwendet wird, um ein anderes Programm als Daemon-Prozess auszuführen. - Die Python-Bibliothek
daemon[clapper-daemon] wird (laut ihrer Homepage) nicht mehr gepflegt. Ab Version 1.0.1 implementiert sie die grundlegenden Schritte aus [stevens]. - Die Bibliothek
daemonize[seutter-daemonize] implementiert ebenfalls die grundlegenden Schritte aus [stevens]. - Ray Burrs
daemon.py-Modul [burr-daemon] bietet das [stevens]-Verfahren sowie PID-Datei-Handling und Umleitung der Ausgabe nach syslog. - Twisted [twisted] enthält, vielleicht nicht überraschend, eine Implementierung einer Prozess-Daemonisierungs-API, die in den Rest des Twisted-Frameworks integriert ist; sie unterscheidet sich erheblich von der API in diesem PEP.
- Die Python-Bibliothek
initd[dagitses-initd], die [clapper-daemon] verwendet, implementiert ein Äquivalent zu Unixinitd(8)zur Steuerung eines Daemon-Prozesses.
Referenzen
daemon-Tools http://www.libslack.org/daemon/ von „raf“ <raf@raf.org>.python-daemon-Bibliothek http://pypi.python.org/pypi/python-daemon/ von Ben Finney et al.bda.daemon-Bibliothek http://pypi.python.org/pypi/bda.daemon/ von Robert Niederreiter et al.zdaemon-Tool http://pypi.python.org/pypi/zdaemon/ von Guido van Rossum et al.daemon-Bibliothek http://pypi.python.org/pypi/daemon/ von Brian Clapper.daemonize-Bibliothek http://daemonize.sourceforge.net/ von Jerry Seutter.daemon.py-Modul http://www.nightmare.com/~ryb/code/daemon.py von Ray Burr.Twisted-Anwendungsframework http://pypi.python.org/pypi/Twisted/ von Glyph Lefkowitz et al.initd-Bibliothek http://pypi.python.org/pypi/initd/ von Michael Andreas Dagitses.Urheberrecht
Diese Arbeit wird hiermit in die Public Domain gestellt. Soweit die Platzierung eines Werkes in die Public Domain rechtlich nicht möglich ist, gewährt der Urheberrechtsinhaber hiermit allen Empfängern dieses Werkes alle Rechte und Freiheiten, die sonst durch das Urheberrecht eingeschränkt wären.
Quelle: https://github.com/python/peps/blob/main/peps/pep-3143.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT