你如何在Python中创build一个守护进程?

在Google上search会显示x2代码片段。 第一个结果是这个代码配方有很多的文档和解释,以及下面的一些有用的讨论。

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

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

Sander Marechal的代码示例优于最初发布于2004年的原始代码 。我曾经贡献过Pyro的一个守护进程,但是如果必须这样做的话,可能会使用Sander的代码。

当成为一个行为良好的守护进程时,有很多事情需要处理 :

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

  • chroot监控下正确运行

  • 为用例适当地设置UID,GID,工作目录,umask和其他进程参数

  • 放弃boost的suidsgid特权

  • closures所有打开的文件描述符,具体取决于用例

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

  • 设置明智的守护进程行为的信号处理程序,而且还会根据用例确定具体的处理程序

  • redirect标准streamstdinstdoutstderr因为守护进程不再有控制terminal

  • 处理PID文件作为一个合作咨询锁,这是一个蠕虫本身的许多矛盾但有效的方式来performance自己

  • 当进程终止时允许正确的清理

  • 实际上成为一个没有导致僵尸的守护进程

其中一些是标准的 ,正如Unix规范文献( 在UNIX环境下的高级编程, W. Richard Stevens,Addison-Wesley,1992)中所描述的。 其他如streamredirect和PID文件处理是大多数守护进程用户期望的常规行为,但是不那么标准化。

所有这些都由PEP 3143 “标准守护进程库”规范覆盖。 python-daemon参考实现在Python 2.7或更高版本以及Python 3.2或更高版本上运行。

当我开发一个新的守护进程应用程序时,这是我开始使用的基本的“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-deaemon库。 在Ubuntu中,您可以:

 sudo apt-get install python-daemon 

然后用./howdy.py start ,用./howdy.py stop

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

其他function(Debian软件包描述):

  • 将进程分离到自己的进程组中。
  • 设置适合在chroot中运行的进程环境。
  • 放弃suid和sgid特权。
  • closures所有打开的文件描述符。
  • 更改工作目录,uid,gid和umask。
  • 设置适当的信号处理程序。
  • 打开stdin,stdout和stderr的新文件描述符。
  • pipe理指定的PIDlocking文件。
  • 为退出处理注册清理函数。

另一种方法是创build一个普通的非守护进程的Python程序,然后使用supervisord从外部守护进程。 这可以节省很多头痛,并且是* nix和便携式的。

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

这里是一个相对较新的Python模块,在黑客新闻中popup。 看起来非常有用,可以用来从脚本内部将python脚本转换为守护进程模式。 链接

由于python-daemon尚未支持python 3.x,并且可以从邮件列表中读取什么,所以可能永远不会,我已经写了一个PEP 3143的新实现: pep3143daemon

pep3143守护进程至less应该支持python 2.6,2.7和3.x

它还包含一个PidFile类。

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

它可以用来替代python守护进程。

这里是文档 。

在python中进行守护进程时,还需要考虑一件事情:

如果您正在使用python 日志logging,并且想要在守护进程之后继续使用它,请确保在处理程序(特别是文件处理程序)上调用close() )。

如果你不这样做,处理程序仍然可以认为它已经打开了文件,并且你的消息将会消失 – 换句话说,确保logging器知道它的文件已经closures了!

这个假设是在你守护进程完全closures了所有打开的文件描述符的情况下 – 而不是closures所有日志文件(但是closures所有文件然后重新打开你想要的通常更简单)。

这个函数将把一个应用程序转换成一个守护进程:

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

恐怕@Dustin提到的守护进程模块不适合我。 相反,我安装了python守护进程,并使用下面的代码:

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

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

这里是。

注意:我不使用“python-daemon”模块,因为文档仍然丢失(另见许多其他问题),而且比较模糊(如何从命令行启动/停止守护进程)?

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

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

直到真正需要实际守护进程的时候,才写一个简单的服务器。

也看看WSGI参考实现。

也看看简单的HTTP服务器 。

“还有什么需要考虑的吗?”是的。 大约一百万件事 什么协议? 有多less个请求? 每个请求需要多长时间? 他们多久会抵达? 你会使用一个专门的过程? 主题? subprocess? 编写一个守护进程是一个很大的工作。