¿Cómo se crea un demonio en Python?


La búsqueda en Google revela fragmentos de código x2. El primer resultado es esta receta de código que tiene mucha documentación y explicación, junto con alguna discusión útil debajo.

Sin embargo, otro código de ejemplo, aunque no contiene tanta documentación, incluye código de ejemplo para pasar comandos como start, stop y restart. También crea un archivo PID que puede ser útil para comprobar si el demonio ya se está ejecutando, etc.

Estos las muestras explican cómo crear el demonio. ¿Hay alguna cosa adicional que necesite ser considerada? ¿Es una muestra mejor que la otra, y por qué?

 224
Author: DavidM, 2009-01-23

15 answers

Solución Actual

Una implementación de referencia de PEP 3143 (Standard daemon process library) ahora está disponible como python-daemon.

Respuesta Histórica

La muestra de código de Sander Marechal es superior a la original, que se publicó originalmente en 2004. Una vez contribuí con un daemonizer para Pyro, pero probablemente usaría el código de Sander si tuviera que hacerlo de nuevo.

 138
Author: Jeff Bauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 08:38:19

Hay muchas cosas incómodas para cuidar cuando se convierte en un proceso de demonio bien comportado :

  • Prevenir volcados de núcleo (muchos demonios se ejecutan como root, y los volcados de núcleo pueden contener información sensible)

  • Comportarse correctamente dentro de una chroot cárcel

  • Establezca UID, GID, directorio de trabajo, umask y otros parámetros de proceso apropiadamente para el caso de uso

  • Elevada suid, sgid privilegios

  • Cierre todos los descriptores de archivo abiertos, con exclusiones dependiendo del caso de uso

  • Comportarse correctamente si se inicia dentro de un contexto ya separado, como init, inetd, etc.

  • Configure manejadores de señales para el comportamiento sensible de los demonios, pero también con manejadores específicos determinados por el caso de uso

  • Redireccionar los flujos estándarstdin, stdout, stderr dado que un proceso daemon ya no tiene un control terminal

  • Manejar un archivo PID como un bloqueo consultivo cooperativo, que es una lata de gusanos en sí misma con muchas formas contradictorias pero válidas de comportarse

  • Permitir una limpieza adecuada cuando se termina el proceso

  • En realidad convertirse en un proceso demonio sin llevar a zombies

Algunos de estos son estándar , como se describe en la literatura canónica de UNIX (Programación avanzada en UNIX Medio ambiente , por el fallecido W. Richard Stevens, Addison-Wesley, 1992). Otros, como la redirección de flujos y el manejo de archivos PID, son el comportamiento convencional que la mayoría de los usuarios de daemon esperarían, pero que están menos estandarizados.

Todos estos están cubiertos por el PEP 3143 "Standard daemon process library" specification. La implementación de referencia python-daemon funciona en Python 2.7 o posterior, y Python 3.2 o posterior.

 155
Author: bignose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-23 23:29:19

Aquí está mi demonio Python básico 'Howdy World' con el que empiezo, cuando estoy desarrollando una nueva aplicación de demonio.

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

Tenga en cuenta que necesitará la biblioteca python-daemon. Puede instalarlo por:

pip install python-daemon

Luego simplemente comienza con ./howdy.py start, y para con ./howdy.py stop.

 89
Author: Dustin Kirkland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-06 00:49:48

Tenga en cuenta el paquete python-daemon que resuelve muchos problemas detrás de los demonios fuera de la caja.

Entre otras características que permite (desde la descripción del paquete Debian):

  • Separar el proceso en su propio grupo de procesos.
  • Establezca un entorno de proceso apropiado para ejecutarse dentro de un chroot.
  • Renuncia a los privilegios suid y sgid.
  • Cierra todos los descriptores de archivo abiertos.
  • Cambie el directorio de trabajo, uid, gid y umask.
  • Conjunto manipuladores de señales apropiados.
  • Abra nuevos descriptores de archivo para stdin, stdout y stderr.
  • Administra un archivo de bloqueo PID especificado.
  • Registrar funciones de limpieza para el procesamiento al salir.
 42
Author: Viliam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-23 23:28:03

Una alternativa create crear un programa Python normal, no demonizado y luego externamente demonizarlo usando supervisord. Esto puede ahorrar muchos dolores de cabeza, y es * nix-y lenguaje-portable.

 24
Author: Chris Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-11 12:52:15

Probablemente no sea una respuesta directa a la pregunta, pero systemd se puede usar para ejecutar su aplicación como un demonio. He aquí un ejemplo:

[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

Prefiero este método porque mucho del trabajo se hace por usted, y luego su script daemon se comporta de manera similar al resto de su sistema.

-Orby

 12
Author: Luke Dupin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-18 23:11:12

YapDi es un módulo python relativamente nuevo que apareció en Hacker News. Parece bastante útil, se puede utilizar para convertir un script de python en modo demonio desde el interior del script.

 6
Author: Sergey R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-23 15:51:49

Desde que python-daemon aún no ha soportado python 3.x, y de lo que se puede leer en la lista de correo, puede que nunca lo haga, he escrito una nueva implementación de PEP 3143: pep3143daemon

Pep3143daemon debería soportar al menos python 2.6, 2.7 y 3.x

También contiene una clase PidFile.

La biblioteca solo depende de la biblioteca estándar y del módulo six.

Puede ser usado como un reemplazo para python-daemon.

Aquí está el documentación.

 5
Author: stephan schultchen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-15 16:43:37

Me temo que el módulo daemon mencionado por @Dustin no funcionó para mí. En su lugar instalé python-daemon y usé el siguiente código:

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

Correr es fácil

> python myDaemon.py

Solo para completar aquí está el contenido del directorio samplemodule

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

El contenido de moduleclass.py puede ser

class moduleclass():
    ...

def do_running():
    m = moduleclass()
    # do whatever daemon is required to do.
 3
Author: Somum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-12 02:43:11

Esta función transformará una aplicación en un demonio:

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())
 3
Author: Ivan Kolesnikov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-14 08:54:22

Una cosa más en la que pensar cuando se daemoniza en python:

Si está usando python logging y desea continuar usándolo después de la daemonización, asegúrese de llamar a close() en los controladores (particularmente los controladores de archivos).

Si no hace esto, el controlador todavía puede pensar que tiene archivos abiertos, y sus mensajes simplemente desaparecerán, en otras palabras, ¡asegúrese de que el registrador sepa que sus archivos están cerrados!

Esto asume que cuando usted daemonise está cerrando TODO los descriptores de archivos abiertos indiscriminadamente-en su lugar, podría intentar cerrar todos menos los archivos de registro (pero generalmente es más simple cerrar todos y luego volver a abrir los que desee).

 2
Author: Matthew Wilcoxson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-20 16:21:09

Modificé algunas líneas en el ejemplo de código de Sander Marechal (mencionado por @JeffBauer en la respuesta aceptada) para agregar un método quit() que se ejecuta antes de que se detenga el demonio. Esto a veces es muy útil.

Aquí está.

Nota: No uso el módulo "python-daemon" porque la documentación aún no está disponible (vea también muchas otras preguntas SO) y es bastante oscura (cómo iniciar/detener correctamente un demonio desde la línea de comandos con esto módulo?)

 1
Author: Basj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:31

Después de unos años y muchos intentos, ahora me doy cuenta de que hay una mejor manera que querer iniciar, detener, reiniciar un demonio directamente desde Python: ¡use las herramientas del sistema operativo en su lugar!

En resumen, en lugar de hacer python myapp start y python myapp stop, hago esto para iniciar la aplicación:

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

O screen -dmS myapp python myapp.py a iniciar y separar en un comando.

Entonces:

screen -r myapp

Para adjuntar a este terminal de nuevo. Una vez en el terminal, es posible usar CTRL+C para detenerlo.

 -1
Author: Basj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-23 16:01:13

La forma más fácil de crear daemon con Python es usar el framework basado en eventos Twisted. Se encarga de todo lo necesario para la daemonización para usted. Utiliza el patrón de reactor para manejar solicitudes concurrentes.

 -2
Author: Travis B. Hartwell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-24 05:37:07

El 80% de las veces, cuando la gente dice "demonio", solo quieren un servidor. Dado que la pregunta es perfectamente confusa en este punto, es difícil decir cuál podría ser el posible dominio de las respuestas. Dado que un servidor es adecuado, comience allí. Si realmente se necesita un "demonio" real (esto es raro), lea nohup como una forma de demonizar un servidor.

Hasta el momento en que se requiera un demonio real, simplemente escriba un servidor simple.

También mira la referencia WSGI aplicación.

También mira el Servidor HTTP simple.

" ¿Hay alguna cosa adicional que necesite ser considerada? "Sí. Como un millón de cosas. ¿Qué protocolo? ¿Cuántas peticiones? ¿Cuánto tiempo para atender cada solicitud? ¿Con qué frecuencia llegarán? ¿Utilizará un proceso dedicado? Los hilos? Subprocesos? Escribir un demonio es un gran trabajo.

 -24
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-08 13:57:47