¿Cómo se crea un demonio en Python?

244

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 algunas discusiones útiles debajo.

Sin embargo, otra muestra de código , aunque no contiene tanta documentación, incluye código de muestra para pasar comandos como iniciar, detener y reiniciar. También crea un archivo PID que puede ser útil para verificar si el demonio ya se está ejecutando, etc.

Estas muestras explican cómo crear el demonio. ¿Hay cosas adicionales que deben considerarse? ¿Es una muestra mejor que la otra y por qué?

davidmytton
fuente
1
Siempre he encontrado el código de daemonización innecesario. ¿Por qué no dejar que la cáscara lo haga?
emil.p.stanchev
17
Porque no hace setsid o setpgrp.
bmargulies
44
Utilice supervisord.org . De esta manera no necesita bifurcar () o redirigir su stdin / stderr. Solo escribe un programa normal.
guettli

Respuestas:

169

Solución actual

Una implementación de referencia de PEP 3143 (biblioteca de proceso de daemon estándar) ahora está disponible como python-daemon .

Respuesta histórica

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

Jeff Bauer
fuente
72
Editar: desde que originalmente publiqué esta respuesta, una implementación de referencia de PEP 3143 ahora disponible: pypi.python.org/pypi/python-daemon
Jeff Bauer
@JeffBauer El enlace original ha muerto, recuerdo que fue útil, no conocerías un enlace en vivo para eso, ¿verdad?
CrazyCasta
1
@CrazyCasta: la versión de Sander Marechal todavía está disponible en Wayback Machine
Jeff Bauer
1
@JeffBauer: el código de Sander sigue siendo mejor que http://pypi.python.org/pypi/python-daemon. Más confiable. Solo un ejemplo: intente iniciar dos veces el mismo demonio con python-daemon: gran error feo. Con el código de Sander: un buen aviso "Daemon ya se está ejecutando".
Basj
2
Dado que todavía falta la documentación del módulo "python-daemon" (consulte también muchas otras preguntas SO) y es bastante oscura (¿cómo iniciar / detener correctamente un demonio desde la línea de comandos con este módulo?), Modifiqué el ejemplo de código de Sander Marechal para agregar quit()Método que se ejecuta antes de detener el demonio. Aquí está.
Basj
163

Hay muchas cosas complicadas a tener en cuenta al convertirse en un proceso de demonio con buen comportamiento :

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

  • comportarse correctamente dentro de una chrootcárcel

  • establecer UID, GID, directorio de trabajo, umask y otros parámetros de proceso de manera adecuada para el caso de uso

  • renunciar elevado suid, sgidprivilegios

  • cierre todos los descriptores de archivos abiertos, con exclusiones según el caso de uso

  • comportará correctamente si se inicia dentro de un contexto individual ya, tal como init, inetd, etc.

  • configurar controladores de señal para un comportamiento sensible del demonio, pero también con controladores específicos determinados por el caso de uso

  • redirigir los flujos estándares stdin, stdout, stderrya que un proceso de demonio ya no tiene un terminal de control

  • manejar un archivo PID como un bloqueo de asesoramiento cooperativo, que es una lata completa de gusanos en sí mismo con muchas formas contradictorias pero válidas de comportarse

  • permitir una limpieza adecuada cuando finaliza el proceso

  • en realidad convertirse en un proceso demonio sin conducir a zombies

Algunos de estos son estándar , como se describe en la literatura canónica de Unix ( Programación avanzada en el entorno UNIX , por el difunto W. Richard Stevens, Addison-Wesley, 1992). Otros, como la redirección de flujo y el manejo de archivos PID , son comportamientos convencionales que la mayoría de los usuarios de demonios esperarían pero que están menos estandarizados.

Todos estos están cubiertos por la especificación PEP 3143 "Biblioteca de proceso de daemon estándar" . La implementación de referencia python-daemon funciona en Python 2.7 o posterior, y Python 3.2 o posterior.

nariz grande
fuente
26
"Gaol" está escrito correctamente, porque así lo escribió W. Richard Stevens :-)
bignose
77
La cárcel es una cosa inglesa . El póster es de Australia, por lo que tiene sentido.
devin
1
¿Algún plan para hacer una versión amigable para py3k?
Tim Tisdall
97

Aquí está mi demonio básico de Python 'Howdy World' con el que comienzo, 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 python-daemonbiblioteca. Puedes instalarlo de la siguiente manera:

pip install python-daemon

Luego solo comience con ./howdy.py starty pare con ./howdy.py stop.

Dustin Kirkland
fuente
55
Ese daemonmódulo que importa no es una parte estándar de Python (todavía). Necesita ser instalado con pip install python-daemono equivalente.
Nate
66
Instalé python-daemon como lo describió, pero cuando intento ejecutar mi aplicación (igual que sus últimas 3 líneas), obtengo ImportError: no puedo importar el nombre del corredor
Nostradamnit
¿Puedes verificar si está instalado correctamente? $ dpkg -L python-daemon | grep runner /usr/share/pyshared/daemon/runner.py
Dustin Kirkland
44
Esta sugerencia parece ser obsoleta: a partir de septiembre de 2013, de todos modos, python.org/dev/peps/pep-3143 no menciona un "corredor" que pueda importarse. Por supuesto, esto explicaría la observación de @ Nostradamnit.
offby1
2
Esto todavía funciona bien para mí, en septiembre de 2013, en Ubuntu 13.04, con paquetes de Python, python2.7 y python-daemon instalados. Con python3, sin embargo, veo un error, "del daemon import runner ImportError: Ningún módulo llamado 'daemon'"
Dustin Kirkland
42

Tenga en cuenta el paquete python-daemon que resuelve muchos problemas detrás de los demonios listos para usar.

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

  • Separe el proceso en su propio grupo de procesos.
  • Establezca el entorno de proceso apropiado para ejecutarse dentro de un chroot.
  • Renuncia a los privilegios suid y sgid.
  • Cierre todos los descriptores de archivos abiertos.
  • Cambie el directorio de trabajo, uid, gid y umask.
  • Establecer manejadores de señal apropiados.
  • Abra nuevos descriptores de archivo para stdin, stdout y stderr.
  • Gestionar un archivo de bloqueo PID especificado.
  • Registre las funciones de limpieza para el procesamiento al salir.
Viliam
fuente
35

Una alternativa: crear un programa Python normal, no demonizado, y luego demonizarlo externamente mediante la supervisión . Esto puede ahorrar muchos dolores de cabeza y es * nix- e idioma portátil.

Chris Johnson
fuente
1
Creo que esta es la mejor manera. Especialmente si quieres ejecutar varios demonios en un sistema operativo. No codifique, reutilice.
guettli
Simplifica muchos problemas. He escrito verdaderos demonios, no son fáciles.
Chris Johnson
1
La mejor respuesta está oculta aquí :)
kawing-chiu
1
Esto es oro Después de pasar horas tratando de ejecutar python-daemon, esta es la solución lista para usar que funciona para mí. Excelente documentación y ejemplos hicieron que mi demonio funcionara en pocos minutos.
Nikhil Sahu
17

Probablemente no sea una respuesta directa a la pregunta, pero systemd se puede usar para ejecutar su aplicación como un demonio. Aquí hay 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 gran parte del trabajo está hecho para usted, y luego su script daemon se comporta de manera similar al resto de su sistema.

-O por

Luke Dupin
fuente
Esta es la forma correcta y sensata. 1) Necesita guardarse en /etc/systemd/system/control.service 2) sudo administradosystemctl start control.service
jimper
7

YapDi es un módulo de 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.

Sergey R
fuente
6

dado que python-daemon aún no es compatible con 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 admitir 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 de los seis módulos.

Se puede usar como un reemplazo directo para python-daemon.

Aquí está la documentación .

Stephan Schultchen
fuente
6

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())
Ivan Kolesnikov
fuente
5

Me temo que el módulo demonio 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.
Somum
fuente
2

Una cosa más a tener en cuenta al demonizar en python:

Si está utilizando el registro de Python y desea continuar utilizándolo después de demonizar, asegúrese de llamar close()a los controladores (particularmente a los controladores de archivos).

Si no hace esto, el controlador aún 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 supone que cuando daemoniza está cerrando TODOS los descriptores de archivos abiertos indiscriminadamente; en su lugar, puede intentar cerrar todos los archivos excepto los de registro (pero generalmente es más sencillo cerrarlos y volver a abrir los que desee).

Matthew Wilcoxson
fuente
¿Crees que abrir un nuevo controlador de registro es mejor que pasar el controlador de registro al demonio usando la opción files_preserve de DaemonContext, por ejemplo?
HeyWatchThis
Solo está cerrando el registrador, no está creando uno nuevo (simplemente lo volverá a abrir cuando sea necesario). Pero a pesar de que es realmente fácil hacer eso, podría ser mejor usar el DaemonContext ya que probablemente esté haciendo otras cosas inteligentes (suponiendo que la preservación aún permita la demonización adecuada).
Matthew Wilcoxson
2

Aunque puede preferir la solución Python pura proporcionada por el módulo python-daemon, hay una daemon(3)función en libc, al menos, BSD y Linux , que hará lo correcto.

Llamarlo desde Python es fácil:

import ctypes

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

Lo único que queda por hacer es crear (y bloquear) el archivo PID. Pero que puedes manejar tu mismo ...

Mikhail T.
fuente
1

Modifiqué algunas líneas en el ejemplo de código de Sander Marechal (mencionado por @JeffBauer en la respuesta aceptada ) para agregar unquit() método 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 todavía falta la documentación (ver también muchas otras preguntas SO) y es bastante oscura (¿cómo iniciar / detener correctamente un demonio desde la línea de comandos con este módulo?)

Basj
fuente
-1

Después de algunos años y muchos intentos (probé todas las respuestas aquí, pero todas tenían inconvenientes menores al final), ahora me doy cuenta de que hay una mejor manera que querer iniciar, detener y reiniciar un demonio directamente desde Python : utilice las herramientas del sistema operativo en su lugar.

Por ejemplo, para Linux, en lugar de hacer python myapp starty 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.pypara comenzar y separarlo en un comando .

Luego:

screen -r myapp

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

Basj
fuente
-2

La forma más fácil de crear demonios con Python es usar el marco de trabajo Twisted . Maneja todas las cosas necesarias para la demonización para ti. Utiliza el patrón del reactor para manejar solicitudes concurrentes.

Travis B. Hartwell
fuente
55
Es un martillo demasiado grande para usar. La mayoría de las personas solo quieren ejecutar un breve script de Python que escribieron como demonio. python-daemon, como se describió anteriormente, es la respuesta correcta.
Tom Swirly
2
Aunque esta respuesta fue bastante arrogante, fue útil.
fiatjaf
-28

El 80% del tiempo, cuando la gente dice "demonio", solo quieren un servidor. Dado que la pregunta no está clara en este punto, es difícil decir cuál podría ser el posible dominio de respuestas. Como un servidor es adecuado, comience allí. Si realmente se necesita un "demonio" real (esto es raro), sigue leyendonohup siga como una forma de demonizar un servidor.

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

También mire la implementación de referencia WSGI .

También mire el servidor HTTP simple .

"¿Hay algo adicional que deba considerarse?" Sí. Alrededor de un millón de cosas. Que protocolo Cuantas solicitudes ¿Cuánto tiempo para atender cada solicitud? ¿Con qué frecuencia llegarán? ¿Usarás un proceso dedicado? ¿Hilos? Subprocesos? Escribir un demonio es un gran trabajo.

S.Lott
fuente
12
Ninguna de esas bibliotecas hace ni una sola fork(), y mucho menos dos. No tienen nada que ver con la demonización.
Brandon Rhodes
8
En los sistemas operativos Unix, un proceso de "demonio", como los asistentes aéreos que los griegos llamaron "demonios", es uno que "se mantiene a un lado". En lugar de servir directamente a un solo usuario a través de TTY de ese usuario, un demonio no pertenece a ningún TTY, pero puede responder a las solicitudes de muchos usuarios en el sistema, o, al igual que, crondo syslogdhace servicios de limpieza para todo el sistema. Para crear un proceso de demonio, uno debe realizar al menos un doble fork()con todos los descriptores de archivo cerrados, de modo que uno sea inmune a las señales de todos los terminales de control, incluida la consola del sistema. Ver la respuesta de bignose.
Brandon Rhodes
55
@S Lott - “un servidor” describe lo que es un proceso que hace (escucha las solicitudes entrantes en vez de iniciar sus propias acciones); "Un demonio" describe cómo se ejecuta un proceso (sin una ventana o un terminal de control). SimpleHTTPServeres de hecho un servidor, pero uno que no sabe de forma nativa cómo demonizarse a sí mismo (puede Ctrl-C, por ejemplo). nohupes una utilidad para demonizar un proceso ingenua - para que su servidor nohupped es de hecho tanto un demonio y un servidor, exactamente como usted afirma. Esta pregunta de desbordamiento de pila esencialmente preguntaba: "¿Cómo puedo implementar nohupen Python?"
Brandon Rhodes
55
Sí, pero mi comprensión de la pregunta de los OP es que quiere hacer la demonización desde su programa de Python y sin usar otra cosa.
Noufal Ibrahim
44
@S Lott - ¡No necesitas estar impresionado! El autor de cualquier otra respuesta sabía lo que significaba "demonio", por lo que mi capacidad para interpretar esta pregunta no es única. :) ¿Y de dónde sacaste la idea de que quiero que el autor reinvente una rueda? Creo que nohupes una buena herramienta, y eliminaré mi voto -1 si simplemente mueve esa idea útil a su respuesta real. De hecho, si menciona supervisordy cómo también evitará que el autor tenga que iniciar sesión, un script de inicio-detención y reiniciar el estrangulamiento, incluso le haré +1. :)
Brandon Rhodes