Come si crea un demone in Python?


244

La ricerca su Google rivela frammenti di codice x2. Il primo risultato è questa ricetta di codice che ha molta documentazione e spiegazione, insieme ad alcune utili discussioni di seguito.

Tuttavia, un altro esempio di codice , sebbene non contenga tanta documentazione, include un codice di esempio per passare comandi come start, stop e restart. Crea anche un file PID che può essere utile per verificare se il daemon è già in esecuzione, ecc.

Entrambi questi esempi spiegano come creare il demone. Ci sono altre cose che devono essere considerate? Un campione è migliore dell'altro e perché?


1
Ho sempre trovato il codice di demonizzazione non necessario. Perché non lasciare che la shell lo faccia?
emil.p.stanchev,

17
Perché non fa setsid o setpgrp.
bmargulies

4
Usa supervisord.org . In questo modo non è necessario fork () o reindirizzare lo stdin / stderr. Basta scrivere un programma normale.
Guettli,

Risposte:


169

Soluzione attuale

Un'implementazione di riferimento di PEP 3143 (libreria dei processi daemon standard) è ora disponibile come demone python .

Risposta storica

L' esempio di codice di Sander Marechal è superiore all'originale, originariamente pubblicato nel 2004. Una volta ho contribuito con un demone per Pyro, ma probabilmente avrei usato il codice di Sander se avessi dovuto farlo.


72
Modifica: da quando ho pubblicato inizialmente questa risposta, è ora disponibile un'implementazione di riferimento di PEP 3143: pypi.python.org/pypi/python-daemon
Jeff Bauer,

@JeffBauer Il link originale è morto, ricordo che è utile, non ti capita di conoscere un link live per quello, vero?
CrazyCasta,

1
@CrazyCasta: la versione di Sander Marechal è ancora disponibile sulla Wayback Machine
Jeff Bauer,

1
@JeffBauer: il codice di Sander è ancora meglio di http://pypi.python.org/pypi/python-daemon. Più affidabile. Solo un esempio: prova a avviare due volte lo stesso demone con python-daemon: grosso brutto errore. Con il codice di Sander: un bel avviso "Daemon già in esecuzione".
Basj,

2
Poiché la documentazione del modulo "python-daemon" è ancora mancante (vedi anche molte altre domande SO) ed è piuttosto oscura (come avviare / arrestare correttamente un demone dalla riga di comando con questo modulo?), Ho modificato l'esempio di codice di Sander Marechal per aggiungere quit()metodo che viene eseguito prima dell'arresto del daemon. Ecco qui.
Basj,

163

Ci sono molte cose difficili da gestire quando si diventa un processo demone ben educato :

  • prevenire core dump (molti daemon vengono eseguiti come root e i core dump possono contenere informazioni riservate)

  • comportarsi correttamente all'interno di una chrootprigione

  • impostare UID, GID, directory di lavoro, umask e altri parametri di processo in modo appropriato per il caso d'uso

  • rinunciare elevati suid, sgidprivilegi

  • chiudi tutti i descrittori di file aperti, con esclusioni a seconda del caso d'uso

  • comportarsi correttamente se avviato all'interno di un contesto già a schiera, ad esempio init, inetdecc

  • impostare gestori di segnale per comportamenti demoni sensibili, ma anche con gestori specifici determinati dal caso d'uso

  • reindirizzare i flussi standard stdin, stdout, stderrdal momento che un processo demone non ha un terminale di controllo

  • gestire un file PID come blocco consultivo cooperativo, che è un'intera lattina di worm in sé con molti modi contraddittori ma validi per comportarsi

  • consentire un'adeguata pulizia al termine del processo

  • in realtà diventa un processo daemon senza portare a zombi

Alcuni di questi sono standard , come descritto nella letteratura canonica Unix ( Advanced Programming in UNIX Environment , del compianto W. Richard Stevens, Addison-Wesley, 1992). Altri, come il reindirizzamento dello stream e la gestione dei file PID , sono comportamenti convenzionali che la maggior parte degli utenti di demoni si aspettano, ma che sono meno standardizzati.

Tutti questi sono coperti dalla specifica PEP 3143 "Standard daemon process library" . L' implementazione di riferimento python-daemon funziona su Python 2.7 o versioni successive e Python 3.2 o versioni successive.


26
"Gaol" è scritto correttamente, perché è così che W. Richard Stevens l'ha scritto :-)
bignose

7
Gaol è una cosa inglese . Il poster viene dall'Australia, quindi ha senso.
Devin,

1
Hai intenzione di realizzare una versione amichevole di py3k?
Tim Tisdall,

97

Ecco il mio demone Python 'Howdy World' di base con cui inizio, quando sto sviluppando una nuova applicazione demone.

#!/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()

Nota che avrai bisogno della python-daemonbiblioteca. Puoi installarlo tramite:

pip install python-daemon

Quindi basta avviarlo con ./howdy.py starte fermarlo con ./howdy.py stop.


5
Quel daemonmodulo che importi non è una parte standard di Python (ancora). Deve essere installato con pip install python-daemono equivalente.
Nate

6
Ho installato python-daemon come hai descritto, ma quando provo a eseguire la mia app (come le tue ultime 3 righe), ottengo ImportError: impossibile importare il nome runner
Nostradamnit

Puoi verificare se è installato correttamente? $ dpkg -L python-daemon | grep runner /usr/share/pyshared/daemon/runner.py
Dustin Kirkland

4
Questo suggerimento sembra essere obsoleto - a partire da settembre 2013, comunque, python.org/dev/peps/pep-3143 non fa menzione di un "corridore" che può essere importato. Questo ovviamente spiegherebbe l'osservazione di @ Nostradamnit.
offby1

2
Questo funziona ancora bene per me, a settembre 2013, su Ubuntu 13.04, con i pacchetti Python di serie, python2.7 e python-daemon installati. Con python3, tuttavia, vedo l'errore "dal daemon import runner ImportError: nessun modulo chiamato 'daemon'"
Dustin Kirkland,

42

Nota il pacchetto python-daemon che risolve immediatamente molti problemi dietro i demoni.

Tra le altre funzionalità che abilita (dalla descrizione del pacchetto Debian):

  • Scollegare il processo nel proprio gruppo di processi.
  • Impostare l'ambiente di processo appropriato per l'esecuzione all'interno di un chroot.
  • Rinuncia ai privilegi di suid e sgid.
  • Chiudi tutti i descrittori di file aperti.
  • Cambia la directory di lavoro, uid, gid e umask.
  • Impostare i gestori di segnale appropriati.
  • Apri nuovi descrittori di file per stdin, stdout e stderr.
  • Gestire un file di blocco PID specificato.
  • Registrare le funzioni di pulizia per l'elaborazione all'uscita.

35

Un'alternativa: crea un normale programma Python non demonizzato e poi demonimizzalo esternamente usando supervisord . Questo può risparmiare un sacco di mal di testa ed è * nix- e linguaggio-portatile.


1
Penso che questo sia il modo migliore. Soprattutto se si desidera eseguire diversi daemon su un sistema operativo. Non codificare, riutilizzare.
Guettli,

Semplifica molti problemi. Ho scritto demoni veri: non sono facili.
Chris Johnson,

1
La risposta migliore è nascosta qui :)
kawing-chiu

1
Questo è oro Dopo aver passato ore a cercare di eseguire Python-Demone, questa è la soluzione pronta all'uso che funziona per me. Ottima documentazione ed esempi hanno reso il mio demone attivo e funzionante in pochi minuti.
Nikhil Sahu,

17

Probabilmente non è una risposta diretta alla domanda, ma systemd può essere utilizzato per eseguire l'applicazione come demone. Ecco un esempio:

[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

Preferisco questo metodo perché gran parte del lavoro è fatto per te, e quindi il tuo script daemon si comporta in modo simile al resto del tuo sistema.

-Orby


Questo è il modo corretto e sano. 1) Deve essere salvato in /etc/systemd/system/control.service 2) gestito sudosystemctl start control.service
jimper

7

YapDi è un modulo Python relativamente nuovo che è apparso in Hacker News. Sembra abbastanza utile, può essere usato per convertire uno script Python in modalità demone dall'interno dello script.


6

dal momento che python-daemon non ha ancora supportato python 3.xe da ciò che può essere letto sulla mailing list, potrebbe non esserlo mai, ho scritto una nuova implementazione di PEP 3143: pep3143daemon

pep3143daemon dovrebbe supportare almeno python 2.6, 2.7 e 3.x

Contiene anche una classe PidFile.

La libreria dipende solo dalla libreria standard e dal modulo sei.

Può essere usato come drop in sostituzione di python-daemon.

Ecco la documentazione .


6

Questa funzione trasformerà un'applicazione in un demone:

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())

5

Temo che il modulo demone menzionato da @Dustin non abbia funzionato per me. Invece ho installato python-daemon e ho usato il seguente codice:

# 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.

Correre è facile

> python myDaemon.py

solo per completezza ecco il contenuto della directory samplemodule

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

Il contenuto di moduleclass.py può essere

class moduleclass():
    ...

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

2

Un'altra cosa a cui pensare quando demonizzare in Python:

Se stai usando la registrazione Python e vuoi continuare a usarla dopo la demonizzazione, assicurati di chiamare close()i gestori (in particolare i gestori di file).

Se non lo fai, il gestore può ancora pensare che i file siano aperti e i tuoi messaggi scompariranno semplicemente, in altre parole assicurati che il logger sappia che i suoi file sono chiusi!

Ciò presuppone che quando si esegue la demonizzazione si stanno chiudendo TUTTI i descrittori di file aperti indiscriminatamente - invece si potrebbe provare a chiudere tutti tranne i file di registro (ma di solito è più semplice chiudere tutti quindi riaprire quelli desiderati).


Pensi che aprire un nuovo gestore di registrazione sia meglio che passare il gestore di registrazione al demone usando ad esempio l'opzione files_preserve di DaemonContext?
HeyWatch, questo

Stai solo chiudendo il logger, non ne stai creando uno nuovo (lo riaprirà quando è necessario). Ma anche se è davvero facile farlo, potrebbe essere meglio usare DaemonContext poiché probabilmente sta facendo alcune altre cose intelligenti (supponendo che la conservazione consenta ancora una corretta demonizzazione).
Matthew Wilcoxson,

2

Anche se potresti preferire la pura soluzione Python fornita dal modulo python-daemon, esiste una daemon(3)funzione libc, almeno su BSD e Linux , che farà la cosa giusta.

Chiamarlo da Python è facile:

import ctypes

ctypes.CDLL(None).daemon(0, 0) # Read the man-page for the arguments' meanings

L'unica cosa rimasta da fare è la creazione (e il blocco) del file PID. Ma che puoi gestire te stesso ...


1

Ho modificato alcune righe nell'esempio di codice di Sander Marechal (menzionato da @JeffBauer nella risposta accettata ) per aggiungere un quit()metodo che viene eseguito prima che il demone venga arrestato. Questo a volte è molto utile.

Ecco qui.

Nota: non uso il modulo "python-daemon" perché manca ancora la documentazione (vedi anche molte altre domande SO) ed è piuttosto oscuro (come avviare / arrestare correttamente un demone dalla riga di comando con questo modulo?)


-1

Dopo alcuni anni e molti tentativi (ho provato tutte le risposte fornite qui, ma alla fine tutti hanno avuto piccoli inconvenienti), ora mi rendo conto che esiste un modo migliore di voler avviare, arrestare, riavviare un demone direttamente da Python : utilizzare invece gli strumenti del sistema operativo.

Ad esempio, per Linux, invece di farlo python myapp starte python myapp stop, lo faccio per avviare l'app:

screen -S myapp python myapp.py    
CTRL+A, D to detach

o screen -dmS myapp python myapp.pyper avviarlo e staccarlo in un solo comando .

Poi:

screen -r myapp

per ricollegarsi a questo terminale. Una volta nel terminal, è possibile utilizzare CTRL + C per fermarlo.


-2

Il modo più semplice per creare un demone con Python è usare il framework Twisted basato sugli eventi. Gestisce tutte le cose necessarie per la demonizzazione per te. Utilizza il modello di reattore per gestire le richieste simultanee.


5
È un martello troppo grande da usare. Molte persone vogliono semplicemente eseguire un breve script Python che hanno scritto come demone. python-daemon, come descritto sopra, è la risposta corretta.
Tom Swirly,

2
Sebbene questa risposta fosse piuttosto arrogante, era utile.
fiatjaf,

-28

L'80% delle volte, quando la gente dice "demone", vuole solo un server. Poiché la domanda non è perfettamente chiara su questo punto, è difficile dire quale possa essere il possibile dominio di risposte. Poiché un server è adeguato, inizia da lì. Se è effettivamente necessario un vero "demone" (questo è raro), continua a leggere nohupcome un modo per demonizzare un server.

Fino a quando non è effettivamente richiesto un demone reale, basta scrivere un semplice server.

Guarda anche l' implementazione di riferimento WSGI .

Guarda anche il server HTTP semplice .

"Ci sono altre cose che devono essere considerate?" Sì. Circa un milione di cose. Quale protocollo? Quante richieste? Quanto tempo per soddisfare ogni richiesta? Con quale frequenza arriveranno? Userai un processo dedicato? Le discussioni? Sottoprocessi? Scrivere un demone è un grosso lavoro.


12
Nessuna di queste librerie ne fa nemmeno una fork(), figuriamoci due. Non hanno nulla a che fare con la demonizzazione.
Brandon Rhodes,

8
Sui sistemi operativi Unix, un processo "daemon" - come gli assistenti aerei che i Greci chiamavano "daemon" - è un processo che "sta di lato". Invece di servire direttamente un singolo utente attraverso il TTY di quell'utente, un demone non appartiene a nessun TTY, ma può rispondere alle richieste di molti utenti sul sistema o - come crondo syslogd- fa servizi di pulizia per l'intero sistema. Per creare un processo daemon, è necessario eseguire almeno un doppio fork()con tutti i descrittori di file chiusi, in modo da essere immuni ai segnali da tutti i terminali di controllo, compresa la console di sistema. Vedi la risposta di bignose.
Brandon Rhodes,

5
@S Lott - “server” descrive ciò che un processo fa (ascolta le richieste in arrivo, invece di avviare proprie azioni); "Un demone" descrive come viene eseguito un processo (senza una finestra o un terminale di controllo). SimpleHTTPServerè davvero un server, ma uno che non sa nativamente come demonizzare se stesso (puoi Ctrl-C, per esempio). nohupè un'utilità per demonizzare un processo ingenuo, quindi il tuo server senza supporto è effettivamente sia un demone che un server, esattamente come dici tu. Questa domanda Stack Overflow stava essenzialmente chiedendo: "Come posso implementare nohupin Python?"
Brandon Rhodes,

5
Sì, ma la mia comprensione della domanda dei PO è che vuole fare la demonizzazione dal suo programma Python e senza usare qualcos'altro.
Noufal Ibrahim,

4
@S Lott - Non devi essere colpito! L'autore di ogni altra risposta sapeva cosa significava "demone", quindi la mia capacità di interpretare questa domanda è quasi unica. :) E da dove hai avuto l'idea che io volessi che l'autore reinventasse una ruota? Penso che nohupsia uno strumento eccellente e rimuoverò il mio voto -1 se sposterai semplicemente quell'idea utile nella tua risposta effettiva. In effetti, se dici supervisorde come salverà anche l'autore dal dover fare la registrazione, uno script start-stop e riavviare la limitazione, allora ti farò anche +1. :)
Brandon Rhodes,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.