261

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

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

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

4

16 回答 16

192

当前解决方案

PEP 3143(标准守护进程库)的参考实现现在以python-daemon 的形式提供。

历史答案

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

于 2009-01-23T17:06:11.230 回答
169

当成为一个表现良好的守护进程时,有许多繁琐的事情需要处理:

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

  • 在监狱内行为正确chroot

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

  • 放弃提升suid,sgid特权

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

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

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

  • 重定向标准流stdin,因为守护进程不再有控制stdout终端stderr

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

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

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

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

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

于 2009-03-27T03:38:06.247 回答
106

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

于 2012-01-28T17:33:44.723 回答
43

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

除了其他功能之外,它还支持(来自 Debian 软件包描述):

  • 将进程分离到它自己的进程组中。
  • 设置适合在 chroot 内运行的进程环境。
  • 放弃 suid 和 sgid 特权。
  • 关闭所有打开的文件描述符。
  • 更改工作目录、uid、gid 和 umask。
  • 设置适当的信号处理程序。
  • 为标准输入、标准输出和标准错误打开新的文件描述符。
  • 管理指定的 PID 锁定文件。
  • 为退出处理注册清理函数。
于 2011-03-23T23:28:03.340 回答
42

另一种选择——创建一个普通的、非守护进程的 Python 程序,然后使用supervisord对其进行外部守护进程。这可以省去很多麻烦,并且是 *nix 和语言可移植的。

于 2013-06-22T20:50:01.287 回答
24

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

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

于 2015-05-18T23:11:12.707 回答
8

YapDi是一个 python 包。它可用于从脚本内部将 python 脚本转换为守护程序模式。

于 2011-12-06T06:11:26.273 回答
7

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

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

它还包含一个 PidFile 类。

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

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

这是文档

于 2014-09-15T16:43:37.000 回答
7

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

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())
于 2016-09-14T08:54:22.400 回答
6

恐怕@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.
于 2016-09-12T02:43:11.263 回答
3

在 python 中进行守护程序时要考虑的另一件事:

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

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

这假设当您进行守护程序时,您正在不加选择地关闭所有打开的文件描述符 - 相反,您可以尝试关闭除日志文件之外的所有文件(但通常关闭所有文件然后重新打开您想要的文件更简单)。

于 2014-02-20T16:21:09.270 回答
3

尽管您可能更喜欢由 python-daemon 模块提供的纯 Python 解决方案,但至少在BSDLinuxdaemon(3)中,有一个函数可以做正确的事情。libc

从 python 调用它很简单:

import ctypes

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

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

于 2019-09-25T03:24:11.490 回答
2

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

这里是。

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

于 2016-11-12T10:33:01.117 回答
0

经过几年和多次尝试(我尝试了这里给出的所有答案,但最后都有一些小缺点),现在我意识到有比直接从 Python 启动、停止、重新启动守护程序更好的方法:改用操作系统工具。

例如,对于 Linux,我没有执行python myapp startand 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 来停止它。

于 2018-02-23T16:01:13.103 回答
-1

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

于 2009-01-24T05:37:07.643 回答
-30

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

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

另请查看WSGI 参考实现。

另请查看Simple HTTP Server

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

于 2009-01-23T22:06:27.270 回答