Como você cria um daemon em Python?

244

A pesquisa no Google revela trechos de código x2. O primeiro resultado é essa receita de código, que contém muita documentação e explicação, além de algumas discussões úteis abaixo.

No entanto, outro exemplo de código , embora não contenha muita documentação, inclui código de exemplo para passar comandos como iniciar, parar e reiniciar. Ele também cria um arquivo PID que pode ser útil para verificar se o daemon já está em execução etc.

Essas amostras explicam como criar o daemon. Existem coisas adicionais que precisam ser consideradas? Uma amostra é melhor que a outra e por quê?

davidmytton
fonte
1
Eu sempre achei o código de daemonização desnecessário. Por que não deixar a concha fazer isso?
emil.p.stanchev
17
Porque ele não faz setsid ou setpgrp.
bmargulies
4
Use supervisord.org . Dessa forma, você não precisa bifurcar () ou redirecioná-lo stdin / stderr. Basta escrever um programa normal.
guettli

Respostas:

169

Solução atual

Uma implementação de referência do PEP 3143 (biblioteca de processos daemon padrão) agora está disponível como python-daemon .

Resposta histórica

O exemplo de código de Sander Marechal é superior ao original, publicado originalmente em 2004. Certa vez, contribuí com um daemonizer para o Pyro, mas provavelmente usaria o código de Sander se precisasse fazê-lo novamente.

Jeff Bauer
fonte
72
Edit: Desde que eu originalmente publicado esta resposta, uma implementação de referência do PEP 3143 agora disponível: pypi.python.org/pypi/python-daemon
Jeff Bauer
@JeffBauer O link original morreu, eu lembro que era útil, você não saberia um link ao vivo para isso, não é?
27630 CrazyCasta
1
@CrazyCasta: A versão de Sander Marechal ainda está disponível na Wayback Machine
Jeff Bauer
1
@ JeffBauer: O código de Sander ainda é melhor que http://pypi.python.org/pypi/python-daemon. Mais confiável. Apenas um exemplo: tente iniciar duas vezes o mesmo daemon com python-daemon: erro grande e feio. Com o código de Sander: um bom aviso "Daemon já está em execução".
Basj 17/01/16
2
Como a documentação do módulo "python-daemon" ainda está ausente (consulte também muitas outras questões de SO) e é bastante obscura (como iniciar / parar corretamente um daemon da linha de comando com este módulo?), Modifiquei a amostra de código de Sander Marechal para adicionar quit()método que é executado antes que o daemon seja parado. Aqui está.
Basj
163

muitas coisas complicadas a serem resolvidas ao se tornar um processo de daemon bem-comportado :

  • impedir core dumps (muitos daemons são executados como raiz, e core dumps podem conter informações confidenciais)

  • se comportar corretamente dentro de uma chrootprisão

  • configure UID, GID, diretório de trabalho, umask e outros parâmetros de processo adequadamente para o caso de uso

  • renunciar elevados suid, sgidprivilégios

  • feche todos os descritores de arquivo aberto, com exclusões dependendo do caso de uso

  • comportar correctamente se começou dentro de um contexto já em banda, tais como init, inetd, etc.

  • configurar manipuladores de sinal para um comportamento sensível do daemon, mas também com manipuladores específicos determinados pelo caso de uso

  • redirecionar os fluxos padrão stdin, stdout, stderruma vez que um processo de daemon já não tem um terminal de controle

  • manipular um arquivo PID como um bloqueio consultivo cooperativo, que é uma lata inteira de worms com muitas maneiras contraditórias, mas válidas, de se comportar

  • permitir limpeza adequada quando o processo for finalizado

  • realmente se tornar um processo daemon sem levar a zumbis

Alguns deles são padrão , conforme descrito na literatura canônica do Unix ( Programação Avançada no Ambiente UNIX , do falecido W. Richard Stevens, Addison-Wesley, 1992). Outros, como redirecionamento de fluxo e manipulação de arquivos PID , são comportamentos convencionais que a maioria dos usuários do daemon esperaria, mas que são menos padronizados.

Tudo isso é coberto pela especificação PEP 3143 "Biblioteca de processos daemon padrão" . A implementação de referência do python-daemon funciona no Python 2.7 ou posterior e no Python 3.2 ou posterior.

nariz grande
fonte
26
“Prisão” está escrito corretamente, porque é assim que W. Richard Stevens soletrou :-)
Bignose
7
Gaol é uma coisa inglesa . O pôster é da Austrália, então faz sentido.
Devin #
1
Algum plano de criar uma versão amigável para py3k?
precisa
97

Aqui está meu daemon Python básico 'Howdy World' com o qual começo, quando estou desenvolvendo um novo aplicativo daemon.

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

Observe que você precisará da python-daemonbiblioteca. Você pode instalá-lo:

pip install python-daemon

Em seguida, basta iniciar ./howdy.py starte parar com ./howdy.py stop.

Dustin Kirkland
fonte
5
Esse daemonmódulo que você importa ainda não é uma parte padrão do Python. Ele precisa ser instalado com pip install python-daemonou equivalente.
Nate
6
Eu instalei o python-daemon como você descreveu, mas quando tento executar meu aplicativo (igual às suas últimas 3 linhas), obtenho ImportError: não é possível importar o nome runner
Nostradamnit
Você pode verificar se está instalado corretamente? $ dpkg -L python-daemon | grep runner /usr/share/pyshared/daemon/runner.py
Dustin Kirkland
4
Essa sugestão parece obsoleta - a partir de setembro de 2013, de qualquer forma, python.org/dev/peps/pep-3143 não faz menção a um "corredor" que pode ser importado. Obviamente, isso explicaria a observação de @ Nostradamnit.
precisa
2
Isso ainda funciona bem para mim, em setembro de 2013, no Ubuntu 13.04, com os pacotes Python padrão, python2.7 e python-daemon instalados. Com python3, no entanto, vejo erro "do daemon import runner ImportError: nenhum módulo chamado 'daemon'"
Dustin Kirkland
42

Observe o pacote python-daemon que resolve muitos problemas atrás dos daemons imediatamente.

Entre outros recursos, ele permite (a partir da descrição do pacote Debian):

  • Desanexe o processo em seu próprio grupo de processos.
  • Defina o ambiente do processo apropriado para execução dentro de um chroot.
  • Renuncie aos privilégios suid e sgid.
  • Feche todos os descritores de arquivos abertos.
  • Altere o diretório de trabalho, uid, gid e umask.
  • Defina manipuladores de sinal apropriados.
  • Abra novos descritores de arquivo para stdin, stdout e stderr.
  • Gerenciar um arquivo de bloqueio PID especificado.
  • Registre as funções de limpeza para processamento na saída.
Viliam
fonte
35

Uma alternativa - crie um programa Python normal, não daemonizado e depois daemonize externamente usando supervisord . Isso pode economizar muitas dores de cabeça e é portátil * nix e idioma.

Chris Johnson
fonte
1
Eu acho que esse é o melhor caminho. Especialmente se você quiser executar vários daemons em um sistema operacional. Não codifique, reutilize.
guettli
Simplifica muitos problemas. Eu escrevi verdadeiros daemons - eles não são fáceis.
Chris Johnson
1
A melhor resposta está escondida aqui :)
kawing-chiu
1
Isto é ouro. Depois de passar horas tentando executar o python-daemon, esta é a solução pronta para uso que funciona para mim. Ótima documentação e exemplos fizeram meu daemon em funcionamento em poucos minutos.
Nikhil Sahu 11/11
17

Provavelmente não é uma resposta direta à pergunta, mas o systemd pode ser usado para executar seu aplicativo como um daemon. Aqui está um exemplo:

[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

Prefiro esse método porque muito trabalho é feito para você e, em seguida, o script daemon se comporta de maneira semelhante ao resto do seu sistema.

-Ou pela

Luke Dupin
fonte
Este é o caminho correto e são. 1) Precisa ser salvo no /etc/systemd/system/control.service 2) managed sudosystemctl start control.service
jimper
7

O YapDi é um módulo python relativamente novo que apareceu no Hacker News. Parece bastante útil, pode ser usado para converter um script python no modo daemon de dentro do script.

Sergey R
fonte
6

Como o python-daemon ainda não suporta o python 3.xe, pelo que pode ser lido na lista de discussão, talvez nunca o seja, escrevi uma nova implementação do PEP 3143: pep3143daemon

pep3143daemon deve suportar pelo menos python 2.6, 2.7 e 3.x

Ele também contém uma classe PidFile.

A biblioteca depende apenas da biblioteca padrão e dos seis módulos.

Ele pode ser usado como um substituto para o python-daemon.

Aqui está a documentação .

stephan schultchen
fonte
6

Esta função transformará um aplicativo em um daemon:

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
fonte
5

Receio que o módulo daemon mencionado por @Dustin não funcionou para mim. Em vez disso, instalei o python-daemon e usei o seguinte 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 é fácil

> python myDaemon.py

apenas para completar, aqui está o conteúdo do diretório samplemodule

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

O conteúdo de moduleclass.py pode ser

class moduleclass():
    ...

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

Mais uma coisa a se pensar ao daemonizar em python:

Se você estiver usando o log python e quiser continuar usando-o após daemonizing, chame close()os manipuladores (principalmente os manipuladores de arquivos).

Se você não fizer isso, o manipulador ainda poderá achar que possui arquivos abertos e suas mensagens simplesmente desaparecerão - em outras palavras, verifique se o criador de logs sabe que seus arquivos estão fechados!

Isso pressupõe que quando você daemon você está fechando TODOS os descritores de arquivos abertos indiscriminadamente - em vez disso, você pode tentar fechar todos, exceto os arquivos de log (mas geralmente é mais fácil fechar tudo e reabrir os que você deseja).

Matthew Wilcoxson
fonte
Você acha que abrir um novo manipulador de log é melhor do que passar o manipulador de log para o daemon usando a opção files_preserve do DaemonContext, por exemplo?
HeyWatchThis
Você está apenas fechando o logger, não está criando um novo (ele será reaberto quando necessário). Mas, embora seja realmente fácil fazer isso, pode ser melhor usar o DaemonContext, pois provavelmente está fazendo outras coisas inteligentes (supondo que a preservação ainda permita a daemonização adequada).
Matthew Wilcoxson
2

Embora você possa preferir a solução Python pura fornecida pelo módulo python-daemon, existe uma daemon(3)função libc- pelo menos no BSD e Linux - que fará a coisa certa.

É fácil chamá-lo de python:

import ctypes

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

A única coisa que resta a fazer é a criação (e o bloqueio) do arquivo PID. Mas que você pode se controlar ...

Mikhail T.
fonte
1

Modifiquei algumas linhas no exemplo de código de Sander Marechal (mencionado por @JeffBauer na resposta aceita ) para adicionar um quit()método que é executado antes que o daemon seja parado. Isso às vezes é muito útil.

Aqui está.

Nota: Eu não uso o módulo "python-daemon" porque a documentação ainda está ausente (consulte também muitas outras questões de SO) e é bastante obscura (como iniciar / parar adequadamente um daemon da linha de comando com este módulo?)

Basj
fonte
-1

Após alguns anos e muitas tentativas (tentei todas as respostas fornecidas aqui, mas todas tinham pequenas desvantagens no final), agora percebo que há uma maneira melhor do que querer iniciar, parar, reiniciar um daemon diretamente do Python : use as ferramentas do sistema operacional.

Por exemplo, para Linux, em vez de fazer python myapp starte python myapp stop, faço isso para iniciar o aplicativo:

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

ou screen -dmS myapp python myapp.pypara iniciar e desanexá-lo em um comando .

Então:

screen -r myapp

para conectar a este terminal novamente. Uma vez no terminal, é possível usar CTRL + C para pará-lo.

Basj
fonte
-2

A maneira mais fácil de criar daemon com Python é usar a estrutura orientada a eventos Twisted . Ele lida com todas as coisas necessárias para daemonization para você. Ele usa o padrão do reator para lidar com solicitações simultâneas.

Travis B. Hartwell
fonte
5
É um martelo muito grande para usar. A maioria das pessoas só deseja executar um pequeno script Python que eles escreveram como daemon. python-daemon, como descrito acima, é a resposta correta.
Tom Swirly
2
Embora essa resposta fosse bastante arrogante, foi útil.
Fiatjaf 17/11/2012
-28

80% do tempo, quando as pessoas dizem "daemon", elas querem apenas um servidor. Como a pergunta ainda não está totalmente esclarecida, é difícil dizer qual poderia ser o possível domínio das respostas. Como um servidor é adequado, comece por aí. Se um "daemon" real for realmente necessário (isso é raro), leia-o nohupcomo uma maneira de daemonizar um servidor.

Até que um daemon real seja realmente necessário, basta escrever um servidor simples.

Veja também a implementação de referência WSGI .

Veja também o Simple HTTP Server .

"Existem coisas adicionais que precisam ser consideradas?" Sim. Cerca de um milhão de coisas. Qual protocolo? Quantos pedidos? Quanto tempo atende a cada solicitação? Com que frequência eles chegarão? Você usará um processo dedicado? Tópicos? Subprocessos? Escrever um daemon é um grande trabalho.

S.Lott
fonte
12
Nenhuma dessas bibliotecas faz sequer uma única fork(), muito menos duas. Eles não têm nada a ver com daemonização.
Brandon Rhodes
8
Nos sistemas operacionais Unix, um processo de "daemon" - como os atendentes aéreos que os gregos chamavam de "daemons" - é aquele que "fica do lado". Em vez de atender diretamente a um único usuário através do TTY desse usuário, um daemon não pertence a nenhum TTY, mas pode responder a solicitações de muitos usuários no sistema ou - como crondou syslogd- faz serviços de limpeza para todo o sistema. Para criar um processo daemon, é necessário pelo menos executar um duplo fork()com todos os descritores de arquivo fechados, para que seja imune a sinais de todos os terminais de controle, incluindo o console do sistema. Veja a resposta da bignose.
Brandon Rhodes
5
@S Lott - “um servidor” descreve o que um processo faz (ouve solicitações recebidas em vez de iniciar suas próprias ações); "Um daemon" descreve como um processo é executado (sem uma janela ou um terminal de controle). SimpleHTTPServeré realmente um servidor, mas um servidor que não sabe nativamente como se daemonizar (você pode pressionar Ctrl-C, por exemplo). nohupé um utilitário para daemonize um processo ingênuo - para que o seu servidor nohupped é realmente tanto um daemon e um servidor, exatamente como você diz. Esta questão do Stack Overflow estava essencialmente perguntando: "Como posso implementar nohupno Python?"
Brandon Rhodes
5
Sim, mas meu entendimento da questão dos OPs é que ele deseja fazer a daemonização de dentro de seu programa python e sem usar outra coisa.
Noufal Ibrahim
4
@ S Lott - Você não precisa ficar impressionado! O autor de todas as outras respostas sabia o que “daemon” significava, então minha capacidade de interpretar essa pergunta dificilmente é única. :) E de onde você tirou a ideia de que eu quero que o autor reinvente uma roda? Eu acho que nohupé uma boa ferramenta, e removerei meu voto de -1 se você simplesmente mover essa ideia útil para a sua resposta real. De fato, se você mencionar supervisorde como isso também evitará que o autor precise fazer o log, um script start-stop e reiniciar a regulagem, então eu adicionarei +1 a você. :)
Brandon Rhodes