Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

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:


Inhaltsverzeichnis

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; wenn False, wird nicht abgetrennt.

Wenn bei der Initialisierung der Instanz nicht spezifiziert (None), wird dies standardmäßig auf True gesetzt, und nur False, wenn das Abtrennen des Prozesses als überflüssig erachtet wird; zum Beispiel, wenn der Prozess von init, initd oder inetd gestartet 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 None ignoriert das Signal (indem die Signalaktion auf signal.SIG_IGN gesetzt 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: None
  • signal.SIGTTOU: None
  • signal.SIGTSTP: None
  • signal.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 root laufen, zu vermeiden.

stdin
Standard:
Keine
stdout
Standard:
Keine
stderr
Standard:
Keine

Jedes von stdin, stdout und stderr ist ein dateiähnliches Objekt, das als neue Datei für die Standard-I/O-Streams sys.stdin, sys.stdout bzw. sys.stderr verwendet wird. Die Datei sollte daher geöffnet sein, mit einem Minimum von Modus ‘r’ für stdin und Modus ‘w+’ für stdout und stderr.

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 in files_preserve aufgeführt).

Wenn None, wird der entsprechende Systemstream an die Datei gebunden, die durch os.devnull benannt 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, open mehrmals auf einer Instanz aufzurufen.
  • Wenn das Attribut prevent_core wahr ist, werden die Ressourcenlimits für den Prozess gesetzt, um Core-Dumps zu verhindern.
  • Wenn das Attribut chroot_directory nicht None ist, wird das effektive Stammverzeichnis des Prozesses auf dieses Verzeichnis gesetzt (über os.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 uid und gid.
  • Schließt alle offenen Dateideskriptoren. Dies schließt diejenigen aus, die im Attribut files_preserve aufgeführt sind, sowie diejenigen, die den Attributen stdin, stdout oder stderr entsprechen.
  • Ändert das aktuelle Arbeitsverzeichnis in den Pfad, der durch das Attribut working_directory angegeben ist.
  • Setzt die Datei-Zugriffserstellungsmaske auf den Wert des Attributs umask zurück.
  • Wenn die Option detach_process wahr ist, wird der aktuelle Prozess in seine eigene Prozessgruppe abgetrennt und von jeder steuernden Terminaltrennstelle getrennt.
  • Setzt die Signal-Handler wie im Attribut signal_map angegeben.
  • Wenn eines der Attribute stdin, stdout oder stderr nicht None ist, werden die Systemstreams sys.stdin, sys.stdout und/oder sys.stderr an 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 pidfile nicht None ist, tritt in seinen Kontextmanager ein.
  • Markiert diese Instanz als geöffnet (für zukünftige open- und close-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, close mehrmals auf einer Instanz aufzurufen.
  • Wenn das Attribut pidfile nicht None ist, tritt aus seinem Kontextmanager aus.
  • Markiert diese Instanz als geschlossen (für zukünftige open- und close-Aufrufe).
is_open
Rückgabe:
True, wenn die Instanz geöffnet ist, andernfalls False.

Diese Eigenschaft gibt den Zustand an, ob die Instanz derzeit geöffnet ist. Sie ist True, wenn die open-Methode der Instanz aufgerufen wurde und die close-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:
True oder False, wie durch das Kontextmanager-Protokoll definiert.

Ruft die close()-Methode der Instanz auf und gibt dann True zurück, wenn die Ausnahme behandelt wurde, oder False, 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.

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) oder inetd(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.

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 Unix initd(8) zur Steuerung eines Daemon-Prozesses.

Referenzen

[stevens] (1, 2, 3, 4)
Unix Network Programming, W. Richard Stevens, 1994 Prentice Hall.
[slack-daemon] (1, 2)
Die (nicht-Python) „libslack“-Implementierung eines daemon-Tools http://www.libslack.org/daemon/ von „raf“ <raf@raf.org>.
[python-daemon] (1, 2)
Die python-daemon-Bibliothek http://pypi.python.org/pypi/python-daemon/ von Ben Finney et al.
[cookbook-66012] (1, 2)
Python Cookbook Rezept 66012, „Fork a daemon process on Unix“ http://code.activestate.com/recipes/66012/.
[cookbook-278731]
Python Cookbook Rezept 278731, „Creating a daemon the Python way“ http://code.activestate.com/recipes/278731/.
[bda.daemon]
Die bda.daemon-Bibliothek http://pypi.python.org/pypi/bda.daemon/ von Robert Niederreiter et al.
[zdaemon]
Das zdaemon-Tool http://pypi.python.org/pypi/zdaemon/ von Guido van Rossum et al.
[clapper-daemon] (1, 2)
Die daemon-Bibliothek http://pypi.python.org/pypi/daemon/ von Brian Clapper.
[seutter-daemonize]
Die daemonize-Bibliothek http://daemonize.sourceforge.net/ von Jerry Seutter.
[burr-daemon]
Das daemon.py-Modul http://www.nightmare.com/~ryb/code/daemon.py von Ray Burr.
[twisted]
Das Twisted-Anwendungsframework http://pypi.python.org/pypi/Twisted/ von Glyph Lefkowitz et al.
[dagitses-initd]
Die Python-initd-Bibliothek http://pypi.python.org/pypi/initd/ von Michael Andreas Dagitses.

Quelle: https://github.com/python/peps/blob/main/peps/pep-3143.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT