这个问题是 3 年前提出的,虽然答案的基本细节没有改变,但鉴于它在“Windows Python 守护进程”搜索中的普遍性,我认为添加一些讨论可能有助于未来谷歌的到来。
这个问题实际上有两个部分:
- Python 脚本能否生成一个可以无限期运行的独立进程?
- Python 脚本可以像 Windows 系统上的 Unix 守护进程一样工作吗?
第一个答案是肯定的。正如已经指出的那样;使用关键字就足够subprocess.Popen
了:creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
import subprocess
independent_process = subprocess.Popen(
'python /path/to/file.py',
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
)
请注意,至少根据我的经验,CREATE_NEW_CONSOLE
这里没有必要。
话虽如此,这种策略的行为与您对 Unix 守护程序的期望并不完全相同。什么是行为良好的 Unix 守护进程在别处有更好的解释,但总结一下:
- 关闭打开的文件描述符(通常是全部,但某些应用程序可能需要保护某些描述符不被关闭)
- 将进程的工作目录更改为合适的位置,以防止出现“Directory Busy”错误
- 更改文件访问创建掩码(
os.umask
在 Python 世界中)
- 将应用程序移至后台并使其与启动进程分离
- 完全脱离终端,包括重定向、、、
STDIN
和STDOUT
到STDERR
不同的流(经常DEVNULL
),防止重新获取控制终端
- 处理信号,特别是
SIGTERM
.
实际情况是,Windows 作为一个操作系统,确实不支持守护进程的概念:从终端启动(或在任何其他交互式上下文中,包括从资源管理器启动等)启动的应用程序将继续运行具有可见窗口,除非控制应用程序(在此示例中为 Python)包含无窗口 GUI。此外,Windows 信号处理严重不足,并且尝试将信号发送到独立的Python 进程(与无法在终端关闭后幸存的子进程相反)几乎总是会导致该 Python 进程立即退出而没有任何清理(没有finally:
, 不atexit
, 不__del__
, 等等)。
将您的应用程序滚动到 Windows 服务中,虽然在许多情况下是一种可行的替代方案,但也不太适合。使用pythonw.exe
(所有最近的 Windows Python 二进制文件附带的无窗口 Python 版本)也是如此。特别是,它们无法改善信号处理的情况,并且它们无法轻松地从终端启动应用程序并在启动期间与之交互(例如,向脚本传递动态启动参数,例如密码、文件路径等),之前“恶魔化”。此外,Windows 服务需要安装,尽管当您第一次调用“守护程序”时可以在运行时快速完成,但它会修改用户的系统(注册表等),如果您来自一个 Unix 世界。
鉴于此,我认为启动pythonw.exe
子进程 usingsubprocess.CREATE_NEW_PROCESS_GROUP
可能是 Python 进程模拟传统 Unix 守护进程最接近的 Windows 等价物。然而,这仍然给您带来了信号处理和启动通信的额外挑战(更不用说让您的代码依赖于平台,这总是令人沮丧)。
话虽如此,对于将来遇到此问题的任何人,我推出了一个名为daemoniker的库,它包装了正确的 Unix 守护进程和上述策略。它还实现了信号处理(适用于 Unix 和 Windows 系统),并允许您使用 pickle 将对象传递给“守护进程”进程。最重要的是,它有一个跨平台的 API:
from daemoniker import Daemonizer
with Daemonizer() as (is_setup, daemonizer):
if is_setup:
# This code is run before daemonization.
do_things_here()
# We need to explicitly pass resources to the daemon; other variables
# may not be correct
is_parent, my_arg1, my_arg2 = daemonizer(
path_to_pid_file,
my_arg1,
my_arg2
)
if is_parent:
# Run code in the parent after daemonization
parent_only_code()
# We are now daemonized, and the parent just exited.
code_continues_here()