在 Google 上搜索会显示 x2 代码片段。第一个结果是这个代码配方,它有很多文档和解释,以及下面的一些有用的讨论。
但是,另一个代码示例虽然没有包含太多文档,但包含用于传递命令(例如启动、停止和重新启动)的示例代码。它还创建一个 PID 文件,可以方便地检查守护程序是否已经在运行等。
这些示例都说明了如何创建守护程序。还有其他需要考虑的事情吗?一个样本比另一个样本好,为什么?
在 Google 上搜索会显示 x2 代码片段。第一个结果是这个代码配方,它有很多文档和解释,以及下面的一些有用的讨论。
但是,另一个代码示例虽然没有包含太多文档,但包含用于传递命令(例如启动、停止和重新启动)的示例代码。它还创建一个 PID 文件,可以方便地检查守护程序是否已经在运行等。
这些示例都说明了如何创建守护程序。还有其他需要考虑的事情吗?一个样本比另一个样本好,为什么?
当前解决方案
PEP 3143(标准守护进程库)的参考实现现在以python-daemon 的形式提供。
历史答案
Sander Marechal 的代码示例优于最初于 2004 年发布的原始代码示例。我曾经为 Pyro 贡献了一个守护程序,但如果我不得不重新做一遍,可能会使用 Sander 的代码。
当成为一个表现良好的守护进程时,有许多繁琐的事情需要处理:
防止核心转储(许多守护进程以 root 身份运行,核心转储可能包含敏感信息)
为用例适当设置 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 或更高版本。
这是我在开发新的守护程序应用程序时开始使用的基本“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
。
请注意python-daemon包,它解决了开箱即用的守护程序背后的许多问题。
除了其他功能之外,它还支持(来自 Debian 软件包描述):
另一种选择——创建一个普通的、非守护进程的 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
我更喜欢这种方法,因为很多工作已经为您完成,然后您的守护程序脚本的行为与系统的其余部分类似。
YapDi是一个 python 包。它可用于从脚本内部将 python 脚本转换为守护程序模式。
由于 python-daemon 尚未支持 python 3.x,并且从邮件列表中可以阅读的内容来看,它可能永远不会支持,我编写了 PEP 3143 的新实现:pep3143daemon
pep3143daemon 应至少支持 python 2.6、2.7 和 3.x
它还包含一个 PidFile 类。
该库仅依赖于标准库和六个模块。
它可以用作 python-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())
恐怕@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.
在 python 中进行守护程序时要考虑的另一件事:
如果您正在使用 python日志记录并且您想在守护进程后继续使用它,请确保调用close()
处理程序(特别是文件处理程序)。
如果您不这样做,处理程序仍然可以认为它打开了文件,并且您的消息将简单地消失 - 换句话说,确保记录器知道它的文件已关闭!
这假设当您进行守护程序时,您正在不加选择地关闭所有打开的文件描述符 - 相反,您可以尝试关闭除日志文件之外的所有文件(但通常关闭所有文件然后重新打开您想要的文件更简单)。
经过几年和多次尝试(我尝试了这里给出的所有答案,但最后都有一些小缺点),现在我意识到有比直接从 Python 启动、停止、重新启动守护程序更好的方法:改用操作系统工具。
例如,对于 Linux,我没有执行python myapp start
and 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 来停止它。
80% 的时间,当人们说“守护进程”时,他们只想要一个服务器。由于这个问题在这一点上完全不清楚,因此很难说可能的答案范围是什么。既然一台服务器就足够了,那就从那里开始吧。如果实际需要一个实际的“守护程序”(这种情况很少见),请继续阅读nohup
作为守护服务器的一种方式。
在实际需要实际守护程序之前,只需编写一个简单的服务器即可。
另请查看WSGI 参考实现。
另请查看Simple HTTP Server。
“有什么额外的事情需要考虑吗?” 是的。大约一百万件事情。什么协议?多少请求?为每个请求提供服务多长时间?他们多久到达一次?你会使用专门的流程吗?线程?子进程?编写守护进程是一项艰巨的工作。