it-swarm.com.de

Wie erstelle ich einen Daemon in Python?

Suche bei Google enthüllt x2-Code-Schnipsel. Das erste Ergebnis ist dieses Coderezept , das eine Menge Dokumentation und Erklärung sowie einige nützliche Erläuterungen enthält.

ein weiteres Codebeispiel enthält zwar nicht so viel Dokumentation, aber Beispielcode zum Übergeben von Befehlen wie Starten, Stoppen und Neustarten. Außerdem wird eine PID-Datei erstellt, anhand derer überprüft werden kann, ob der Dämon bereits ausgeführt wird usw.

In diesen Beispielen wird das Erstellen des Dämons erläutert. Gibt es zusätzliche Dinge, die berücksichtigt werden müssen? Ist eine Probe besser als die andere und warum?

234
DavidM

Aktuelle Lösung

Eine Referenzimplementierung von PEP 3143 (Standard-Daemon-Prozessbibliothek ist jetzt als Python-Daemon verfügbar.

Historische Antwort

Sander Marechals Codebeispiel ist dem Original, das ursprünglich im Jahr 2004 veröffentlicht wurde, überlegen. Ich habe einmal einen Daemonizer für Pyro beigesteuert, würde aber wahrscheinlich Sanders Code verwenden, wenn ich es noch einmal machen müsste.

155
Jeff Bauer

Es gibt viele fummelige Dinge , um die man sich kümmern muss, wenn man ein gut erzogener Daemon-Prozess wird :

  • core-Dumps verhindern (viele Daemons werden als Root ausgeführt und Core-Dumps können vertrauliche Informationen enthalten)

  • verhalten Sie sich korrekt in einem chroot Gefängnis

  • stellen Sie UID, GID, Arbeitsverzeichnis, umask und andere Prozessparameter entsprechend dem Anwendungsfall ein

  • erhöhte suid, sgid Rechte aufgeben

  • schließen Sie alle geöffneten Dateideskriptoren mit Ausschlüssen, je nach Anwendungsfall

  • verhalten Sie sich korrekt, wenn Sie in einem bereits getrennten Kontext gestartet werden, z. B. init, inetd usw.

  • richten Sie Signalhandler für ein vernünftiges Dämonverhalten ein, aber auch für bestimmte Handler, die vom Anwendungsfall bestimmt werden

  • leiten Sie die Standard-Streams stdin, stdout, stderr um, da ein Daemon-Prozess kein steuerndes Terminal mehr hat

  • behandeln Sie eine PID-Datei als kooperative Beratungssperre. Dies ist eine ganze Dose Würmer an sich mit vielen widersprüchlichen, aber gültigen Verhaltensweisen

  • lassen Sie eine ordnungsgemäße Bereinigung zu, wenn der Prozess beendet ist

  • tatsächlich ein Daemon-Prozess werden, ohne zu Zombies zu führen

Einige davon sind Standard , wie in der kanonischen Unix-Literatur beschrieben ( Erweiterte Programmierung in der UNIX-Umgebung, spätestens W. Richard Stevens, Addison-Wesley, 1992). Andere, wie Stream Redirection und PID File Handling , sind konventionelles Verhalten , das die meisten Daemon-Benutzer aber erwarten würden das sind weniger standardisiert.

All dies wird durch die PEP 3143 Spezifikation „Standard Daemon Process Library“ abgedeckt. Die Python-Daemon Referenzimplementierung funktioniert mit Python 2.7 oder höher und Python= 3.2 oder höher.

162
bignose

Hier ist mein grundlegender 'Howdy World' Python Daemon, mit dem ich beginne, wenn ich eine neue Daemon-Anwendung entwickle.

#!/usr/bin/python
import time
from daemon import runner

class App():
    def __init__(self):
        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'
        self.pidfile_path =  '/tmp/foo.pid'
        self.pidfile_timeout = 5
    def run(self):
        while True:
            print("Howdy!  Gig'em!  Whoop!")
            time.sleep(10)

app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()

Beachten Sie, dass Sie das python-daemon Bibliothek. Sie können es installieren, indem Sie:

pip install python-daemon

Dann starte es einfach mit ./howdy.py start und hör auf mit ./howdy.py stop.

94
Dustin Kirkland

Beachten Sie das Paket python-daemon , das viele Probleme hinter Daemons von Anfang an löst.

Unter anderem ermöglicht es (aus der Debian-Paketbeschreibung):

  • Trennen Sie den Prozess in eine eigene Prozessgruppe.
  • Legen Sie die Prozessumgebung fest, die für die Ausführung in einer Chroot geeignet ist.
  • Verzichten Sie auf suid- und sgid-Privilegien.
  • Schließen Sie alle geöffneten Dateideskriptoren.
  • Ändern Sie das Arbeitsverzeichnis, uid, gid und umask.
  • Stellen Sie geeignete Signalhandler ein.
  • Öffnen Sie neue Dateideskriptoren für stdin, stdout und stderr.
  • Verwalten einer angegebenen PID-Sperrdatei.
  • Registrieren Sie Bereinigungsfunktionen für die Verarbeitung beim Beenden.
42
Viliam

Eine Alternative - erstelle ein normales, nicht dämonisiertes Python) - Programm und dämonisiere es dann extern mit supervisord . Dies kann eine Menge Kopfschmerzen ersparen und ist * nix- und sprachtragbar.

30
Chris Johnson

Wahrscheinlich keine direkte Antwort auf die Frage, aber systemd kann verwendet werden, um Ihre Anwendung als Daemon auszuführen. Hier ist ein Beispiel:

[Unit]
Description=Python daemon
After=syslog.target
After=network.target

[Service]
Type=simple
User=<run as user>
Group=<run as group group>
ExecStart=/usr/bin/python <python script home>/script.py

# Give the script some time to startup
TimeoutSec=300

[Install]
WantedBy=multi-user.target

Ich bevorzuge diese Methode, da ein Großteil der Arbeit für Sie erledigt wird und sich Ihr Daemon-Skript dann ähnlich wie der Rest Ihres Systems verhält.

-Oder von

12
Luke Dupin

YapDi ist ein relativ neues python Modul, das in Hacker News aufgetaucht ist. Sieht ziemlich nützlich aus und kann verwendet werden, um ein python zu konvertieren Skript aus dem Skript in den Daemon-Modus.

6
Sergey R

da python-daemon noch nicht unterstützt hat python 3.x, und was auf der Mailingliste zu lesen ist, wird es möglicherweise nie, ich habe eine neue Implementierung von PEP 3143 geschrieben: - pep3143daemon

pep3143daemon sollte mindestens python 2.6, 2.7 und 3.x unterstützen

Es enthält auch eine PidFile-Klasse.

Die Bibliothek hängt nur von der Standardbibliothek und von den sechs Modulen ab.

Es kann als Ersatz für Python-Daemon verwendet werden.

Hier ist die Dokumentation .

6

Diese Funktion wandelt eine Anwendung in einen Daemon um:

import sys
import os

def daemonize():
    try:
        pid = os.fork()
        if pid > 0:
            # exit first parent
            sys.exit(0)
    except OSError as err:
        sys.stderr.write('_Fork #1 failed: {0}\n'.format(err))
        sys.exit(1)
    # decouple from parent environment
    os.chdir('/')
    os.setsid()
    os.umask(0)
    # do second fork
    try:
        pid = os.fork()
        if pid > 0:
            # exit from second parent
            sys.exit(0)
    except OSError as err:
        sys.stderr.write('_Fork #2 failed: {0}\n'.format(err))
        sys.exit(1)
    # redirect standard file descriptors
    sys.stdout.flush()
    sys.stderr.flush()
    si = open(os.devnull, 'r')
    so = open(os.devnull, 'w')
    se = open(os.devnull, 'w')
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())
4
Ivan Kolesnikov

Ich fürchte, das von @Dustin erwähnte Daemon-Modul hat bei mir nicht funktioniert. Stattdessen habe ich python-daemon installiert und den folgenden Code verwendet:

# filename myDaemon.py
import sys
import daemon
sys.path.append('/home/ubuntu/samplemodule') # till __init__.py
from samplemodule import moduleclass 

with daemon.DaemonContext():
    moduleclass.do_running() # I have do_running() function and whatever I was doing in __main__() in module.py I copied in it.

Laufen ist einfach

> python myDaemon.py

der Vollständigkeit halber finden Sie hier den Inhalt des Beispielmodulverzeichnisses

>ls samplemodule
__init__.py __init__.pyc moduleclass.py

Der Inhalt von moduleclass.py kann sein

class moduleclass():
    ...

def do_running():
    m = moduleclass()
    # do whatever daemon is required to do.
3
Somum

Eine Sache, über die Sie nachdenken sollten, wenn Sie in Python dämonisieren:

Wenn Sie python logging verwenden und es nach der Dämonisierung weiterhin verwenden möchten, müssen Sie close() auf den Handlern (insbesondere der Datei) aufrufen Handler).

Wenn Sie dies nicht tun, kann der Handler immer noch denken, dass Dateien geöffnet sind und Ihre Nachrichten einfach verschwinden - mit anderen Worten, stellen Sie sicher, dass der Logger weiß, dass seine Dateien geschlossen sind!

Dies setzt voraus, dass Sie beim Daemonisieren ALLE geöffneten Dateideskriptoren wahllos schließen. Stattdessen können Sie versuchen, alle Dateien außer den Protokolldateien zu schließen (in der Regel ist es jedoch einfacher, alle zu schließen und die gewünschten Dateien erneut zu öffnen).

2

Ich habe ein paar Zeilen in Sander Marechals Codebeispiel (von @JeffBauer in die akzeptierte Antwort erwähnt) geändert, um eine quit() -Methode hinzuzufügen, die ausgeführt wird, bevor der Dämon gestoppt wird. Dies ist manchmal sehr nützlich.

Hier ist es.

Hinweis: Ich verwende das "Python-Daemon" -Modul nicht, da die Dokumentation noch fehlt (siehe auch viele andere SO Fragen) und ziemlich dunkel ist (Wie starte/beende ich mit diesem Modul einen Daemon von der Kommandozeile aus?)

1
Basj