如何在Python中创建守护进程?

时间:2009-01-23 16:48:07

标签: python daemon

Searching on Google显示x2代码段。第一个结果是this code recipe,其中包含大量文档和解释,以及下面的一些有用的讨论。

但是,another code sample虽然不包含太多文档,但包含用于传递启动,停止和重启等命令的示例代码。它还会创建一个PID文件,可以方便地检查守护程序是否已在运行等。

这些示例都解释了如何创建守护进程。还有其他事情需要考虑吗?一个样本比另一个好,为什么?

16 个答案:

答案 0 :(得分:160)

成为well-behaved daemon process时需要处理许多繁琐的事情

  • 阻止核心转储(许多守护程序以root身份运行,核心转储可能包含敏感信息)

  • chroot gaol

  • 内行为正确
  • 为用例适当设置UID,GID,工作目录,umask和其他流程参数

  • 放弃提升的suidsgid权限

  • 关闭所有打开的文件描述符,具有取决于用例的排除

  • 如果在已经分离的上下文中启动,则行为正确,例如initinetd等。

  • 为合理的守护程序行为设置信号处理程序,但也使用由用例确定的特定处理程序

  • 重定向标准流stdinstdoutstderr,因为守护程序进程不再具有控制终端

  • 将PID文件作为合作咨询锁处理,这是一个whole can of worms in itself,有许多相互矛盾但有效的行为方式

  • 在流程终止时允许正确清理

  • 实际上成为守护进程而没有导致zombies

其中一些标准,如规范的Unix文献( UNIX环境中的高级编程,已故W. Richard Stevens,Addison-Wesley,1992)所述)。其他方面,例如流重定向和PID file handling传统行为大多数守护程序用户都会期望,但标准化程度较低。

所有这些都包含在 PEP 3143“标准守护进程库”规范中。 python-daemon参考实现适用于Python 2.7或更高版本以及Python 3.2或更高版本。

答案 1 :(得分:151)

当前解决方案

PEP 3143 (Standard daemon process library)的参考实施现已提供为python-daemon

历史回答

Sander Marechal的code sample优于原版,最初发布于2004年。我曾经为Pyro贡献了一个守护程序,但是如果我不得不这样做的话可能会使用Sander的代码。

答案 2 :(得分:91)

这是我开始使用的基本“Howdy World”Python守护程序,当我开发一个新的守护程序应用程序时。

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

请注意,您需要python-daemon库。您可以通过以下方式安装它:

pip install python-daemon

然后只需使用./howdy.py start启动它,然后使用./howdy.py stop停止。

答案 3 :(得分:42)

请注意python-daemon包,它解决了守护进程背后的许多问题。

它支持的其他功能(来自Debian软件包描述):

  • 将流程分离到自己的流程组。
  • 设置适合在chroot内运行的进程环境。
  • 放弃suid和sgid权限。
  • 关闭所有打开的文件描述符。
  • 更改工作目录,uid,gid和umask。
  • 设置适当的信号处理程序。
  • 打开stdin,stdout和stderr的新文件描述符。
  • 管理指定的PID锁定文件。
  • 注册清理功能以进行退出处理。

答案 4 :(得分:28)

另一种方法 - 创建一个普通的非守护程序Python程序,然后使用supervisord从外部守护它。这可以节省很多麻烦,并且* nix-和语言可移植。

答案 5 :(得分:12)

可能不是问题的直接答案,但systemd可用于将您的应用程序作为守护程序运行。这是一个例子:

[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

我更喜欢这种方法,因为很多工作都是为你完成的,然后你的守护进程脚本的行为与你系统的其他部分类似。

-Orby

答案 6 :(得分:6)

YapDi是一个相对较新的python模块,在Hacker News中出现。看起来很有用,可以用来从脚本内部将python脚本转换为守护进程模式。

答案 7 :(得分:5)

因为python-daemon还没有支持python 3.x,并且从邮件列表中可以读取的内容,它可能永远不会,我已经编写了PEP 3143的新实现:pep3143daemon

pep3143daemon应该至少支持python 2.6,2.7和3.x

它还包含一个PidFile类。

该库仅依赖于标准库和六个模块。

它可以用作python-daemon的替代品。

这是documentation

答案 8 :(得分:4)

此函数将应用程序转换为守护程序:

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

答案 9 :(得分:3)

我担心@Dustin提到的守护程序模块对我不起作用。相反,我安装了python-daemon并使用了以下代码:

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

轻松跑步

> python myDaemon.py

这里只是为了完整性是samplemodule目录内容

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

moduleclass.py的内容可以是

class moduleclass():
    ...

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

答案 10 :(得分:2)

在python中进行守护时需要考虑的事情:

如果您正在使用python 日志记录,并且希望在守护进程后继续使用它,请确保在处理程序(尤其是文件处理程序)上调用close()

如果你不这样做,处理程序仍然认为它有文件打开,你的消息就会消失 - 换句话说,确保记录器知道它的文件已关闭!

这假设你守护你正在不加区别地关闭所有打开的文件描述符 - 而你可以尝试关闭除日志文件以外的所有文件(但通常更简单地关闭所有文件然后重新打开你想要的文件描述符。)

答案 11 :(得分:1)

我在Sander Marechal的代码示例中修改了几行(由@JeffBauer在the accepted answer中提到),以添加在守护程序停止之前执行的quit()方法。这有时非常有用。

Here it is.

注意:我没有使用“python-daemon”模块,因为文档仍然缺失(参见许多其他SO问题)并且相当模糊(如何从命令行正确启动/停止守护进程)使用此模块?)

答案 12 :(得分:0)

尽管您可能更喜欢python-daemon模块提供的纯Python解决方案,但daemon(3)中有一个libc函数-至少在BSD和{{3}上} –会做正确的事。

从python调用很容易:

import ctypes

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

剩下的唯一要做的就是创建(和锁定)PID文件。但是,你可以应付自己...

答案 13 :(得分:-2)

使用Python创建守护进程的最简单方法是使用Twisted事件驱动框架。它为您处理守护进程所需的所有内容。它使用Reactor Pattern来处理并发请求。

答案 14 :(得分:-2)

经过几年多次尝试(我尝试了这里给出的所有答案,但最后都有一些小缺点),现在我意识到有一种比想要启动,停止,重启一个守护进程更好的方法直接来自Python:改用OS工具。

例如,对于Linux,我执行此操作来启动应用程序,而不是执行python myapp startpython myapp stop

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

screen -dmS myapp python myapp.pystart and detach it in one command

然后:

screen -r myapp

再次连接到此终端。一旦进入终端,就可以使用CTRL + C来阻止它。

答案 15 :(得分:-26)

80%的时候,当人们说“守护进程”时,他们只想要一台服务器。由于在这一点上完全不清楚这个问题,很难说答案的可能领域是什么。由于服务器足够,从那里开始。如果实际需要实际的“守护进程”(这种情况很少见),请阅读nohup作为守护服务器的方法。

在实际需要实际守护进程之前,只需编写一个简单的服务器。

另请参阅WSGI reference实施。

另请查看Simple HTTP Server

“还有其他事情需要考虑吗?”是的。大约一百万件事。什么协议?请求多少?服务每个请求多长时间?他们多久到达一次?你会使用专门的流程吗?主题?子进程?编写守护进程是一项艰巨的任务。