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


244

在Google上搜索会发现x2代码段。第一个结果是该代码配方的内容,其中包含大量文档和说明,并在下面进行了一些有用的讨论。

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

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


1
我总是发现不需要守护程序代码。为什么不让外壳做呢?
emil.p.stanchev

17
因为它不执行setid或setpgrp。
bmargulies

4
使用supervisord.org。这样,您无需fork()或重定向您的stdin / stderr。只需编写一个普通程序即可。
guettli 2013年

Answers:


169

当前解决方案

PEP 3143(标准守护程序进程库)的参考实现现已作为python-daemon提供

历史答案

Sander Marechal的代码示例优于最初于2004年发布的原始代码示例。我曾经为Pyro提供了一个守护程序,但如果不得不这样做,可能会使用Sander的代码。


72
编辑:自从我最初发布此答复以来,PEP 3143的参考实现现在可用:pypi.python.org/pypi/python-daemon
Jeff Bauer

@JeffBauer原始链接已消失,我记得它很有用,您不会偶然知道一个实时链接吗?
CrazyCasta 2014年

1
@CrazyCasta:桑德马雷夏尔的版本仍然可以在Wayback机器
杰夫·鲍尔

1
@JeffBauer:Sander的代码仍然比更好http://pypi.python.org/pypi/python-daemon。更可靠。只是一个示例:尝试 使用:big ugly error 启动两次相同的守护程序python-daemon。使用Sander的代码:一个不错的通知“守护程序已经运行”。
巴斯基

2
由于“ python-daemon”模块文档仍然缺失(另请参见其他许多SO问题),并且相当晦涩(如何使用此模块从命令行正确启动/停止守护程序?),我修改了Sander Marechal的代码示例以添加quit()在守护程序停止之前执行的方法。这里是。
Basj

163

成为行为良好的守护进程时,有很多事情要注意:

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

  • chroot监狱中表现正确

  • 根据使用情况适当设置UID,GID,工作目录,umask和其他过程参数

  • 放弃升高suidsgid特权

  • 关闭所有打开的文件描述符,并根据用例进行排除

  • 正确的行为,如果启动一个已经脱离上下文中,如initinetd

  • 为明智的守护程序行为设置信号处理程序,还可以根据用例确定特定的处理程序

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

  • 将PID文件作为合作咨询锁处理,它本身就是蠕虫完整罐,具有许多相互矛盾但有效的行为方式

  • 在进程终止时允许适当的清理

  • 实际上成为守护进程而不会导致僵尸

其中一些是标准的,如规范的UNIX文献所述(UNIX环境中的Advanced Programming,由已故的W. Richard Stevens,Addison-Wesley,1992年出版)。其他(例如流重定向和PID文件处理)是大多数守护程序用户期望的常规行为,但标准化程度较低。

PEP 3143 “标准守护程序进程库”规范涵盖了所有这些内容。该蟒蛇守护参考实现工作在Python 2.7版或更高版本,和Python 3.2或更高版本。


26
“监狱”的拼写正确无误,因为W. Richard Stevens就是这样拼写的:-)
bignose

7
监狱是英国人的事情。海报来自澳大利亚,因此很有意义。
devin 2012年

1
有任何计划制作py3k友好版本吗?
Tim Tisdall 2013年

97

在开发新的守护程序应用程序时,这是我的基本“ 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


5
daemon您导入模块而不是Python(还)标准的一部分。它需要与pip install python-daemon或等效安装。
Nate 2012年

6
我按照您的描述安装了python-daemon,但是当我尝试运行我的应用程序(与您的最后3行相同)时,出现ImportError:无法导入名称运行器
Nostradamnit 2012年

您可以检查其安装是否正确吗?$ dpkg -L python守护程序| grepRunner /usr/share/pyshared/daemon/runner.py
Dustin Kirkland'2

4
这个建议似乎已经过时了-截至2013年9月,无论如何,python.org/dev/peps/pep-3143都没有提及可以导入的“运行程序”。这当然可以解释@Nostradamnit的观察。
2013年

2
在2013年9月,我在Ubuntu 13.04上对我仍然可以正常工作,并安装了库存的Python软件包,python2.7和python-daemon。但是,使用python3,我看到错误,“来自守护程序导入运行程序ImportError:没有名为'daemon'的模块”
Dustin Kirkland 2013年

42

请注意python-daemon软件包,该软件包可立即解决守护程序背后的许多问题。

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

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

35

一种替代方法-创建一个普通的,非守护进程的Python程序,然后使用supervisor在外部对其进行守护进程。这可以节省很多麻烦,并且* nix和语言可移植。


1
我认为这是最好的方法。特别是如果要在一个操作系统上运行多个守护程序。不编码,重复使用。
guettli 2013年

它简化了很多问题。我已经编写了真正的守护程序-它们并不容易。
克里斯·约翰逊

1
最好的答案隐藏在这里:)
kawing-chiu

1
这是黄金。在花了几个小时尝试运行python-daemon之后,这是适用于我的开箱即用的解决方案。出色的文档和示例使我的守护程序在几分钟内启动并运行。
Nikhil Sahu

17

可能不是该问题的直接答案,但是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

我喜欢这种方法,因为为您完成了很多工作,然后守护程序脚本的行为与系统其余部分的行为类似。

-奥比


这是正确而理智的方法。1)需要保存到/etc/systemd/system/control.service 2)托管sudosystemctl start control.service
jimper,

7

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


6

由于python-daemon尚不支持python 3.x,并且从邮件列表中可以读取的内容来看,它可能永远不会支持,因此我编写了PEP 3143的新实现:pep3143daemon

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

它还包含一个PidFile类。

该库仅取决于标准库和六个模块。

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

这是文档


6

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

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

恐怕@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.

2

在python守护进程中要考虑的一件事:

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

如果您不这样做,那么处理程序仍然可以认为它已打开文件,并且您的消息将完全消失-换句话说,请确保记录器知道其文件已关闭!

这假定您在守护程序时无差别地关闭所有打开的文件描述符-相反,您可以尝试关闭除日志文件以外的所有文件(但是通常先关闭所有文件然后再重新打开所需的文件更简单)。


您是否认为打开新的日志记录处理程序比使用DaemonContext的files_preserve选项将日志记录处理程序传递给守护程序更好?
HeyWatch'14年

您仅关闭记录器,没有创建新的记录器(需要时将其重新打开)。但是,尽管这样做确实很容易,但最好还是使用DaemonContext,因为它可能正在做其他一些聪明的事情(假设保留仍然允许正确的守护进程)。
马修·威尔考克森

2

尽管您可能更喜欢python-daemon模块提供的纯Python解决方案,但至少在BSDLinux上有一个daemon(3)功能libc-可以正确执行操作。

从python调用它很容易:

import ctypes

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

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


1

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

这里是。

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


-1

经过几年的尝试(我尝试了这里给出的所有答案,但最后都没有什么缺点),现在我意识到,有比直接从Python启动,停止,重新启动守护程序更好的方法:改用OS工具。

例如,对于Linux,我这样做不是启动python myapp start和,而是python myapp stop启动应用程序:

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

screen -dmS myapp python myapp.py一个命令启动并分离它

然后:

screen -r myapp

再次连接到此终端。进入终端后,可以使用CTRL + C停止它。


-2

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


5
实在太大了,无法使用。大多数人只想运行作为守护程序编写的简短Python脚本。如上所述,python-daemon是正确的答案。
汤姆·史威利

2
尽管这个答案很自大,但却很有用。
fiatjaf 2012年

-28

80%的情况下,当人们说“守护程序”时,他们只想要一台服务器。由于这一点上的问题尚不清楚,因此很难说出答案的可能范围。由于服务器已足够,因此从那里开始。如果确实需要一个实际的“守护程序”(这种情况很少见),请继续阅读nohup作为守护服务器的一种方式。

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

另请参阅WSGI参考实现。

另请参阅简单HTTP服务器

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


12
这些库甚至都没有做一个fork(),更不用说两个了。它们与守护进程无关。
布兰登·罗兹

8
在Unix操作系统上,“守护程序”过程(如希腊人称为“守护程序”的空中服务员)是“站在一边”的过程。守护程序不是通过该用户的TTY直接为单个用户提供服务,而是不属于TTY,而是可以回答系统上许多用户的请求,或者像- crondsyslogd-为整个系统提供客房服务。要创建一个守护进程,必须至少在fork()关闭所有文件描述符的情况下执行double- ,以便一个进程不受来自所有控制终端(包括系统控制台)的信号的影响。参见bignose的答案。
布兰登·罗兹

5
@S Lott —“服务器”描述了进程的功能(侦听传入的请求,而不是启动自己的操作);“守护程序”描述了进程如何运行(没有窗口或控制终端)。SimpleHTTPServer确实是一台服务器,但是它本身并不知道如何守护它(例如,您可以Ctrl-C来控制它)。nohup是一个用于守护天真进程的实用程序-因此,正如您所声称的那样,您已停顿的服务器确实既是守护程序是服务器。这个堆栈溢出问题本质上是在问:“如何nohup在Python中实现?”
布兰登·罗兹

5
是的,的确如此,但是我对OP问题的理解是他想从自己的python程序中进行守护进程,而无需使用其他操作。
Noufal Ibrahim 2011年

4
@S Lott-您无需打动!其他所有答案的作者都知道“守护程序”的含义,因此我解释这个问题的能力并非独有。:)您从哪里得到我希望作者重新发明轮子的想法?我认为这nohup是个很好的工具,如果您只是将有用的想法移到您的实际答案中,我将删除-1票。实际上,如果您提到supervisord了它,以及它将如何使作者免于必须进行日志记录,起止脚本以及重新启动调节的麻烦,那么我什至会为您+1。:)
布兰登·罗德斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.