这个问题是 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 守护进程的期望并不完全相同。 better explained elsewhere 构成了一个行为良好的 Unix 守护进程,但总结一下:
- 关闭打开的文件描述符(通常是全部,但某些应用程序可能需要保护某些描述符不被关闭)
- 将进程的工作目录更改为合适的位置,以防止出现“Directory Busy”错误
- 更改文件访问创建掩码(Python 世界中的
os.umask)
- 将应用程序移至后台并使其与启动进程分离
- 完全脱离终端,包括将
STDIN、STDOUT、STDERR重定向到不同的流(通常是DEVNULL),并防止重新获取控制终端
- 处理信号,尤其是
SIGTERM。
实际情况是,作为操作系统,Windows 确实不支持守护进程的概念:从终端(或在任何其他交互式上下文中,包括从资源管理器启动等)启动的应用程序将继续使用可见窗口运行,除非控制应用程序(在此示例中为 Python)包含无窗口 GUI。此外,Windows 信号处理严重不足,并且尝试将信号发送到 独立 Python 进程(与无法在终端关闭后幸存的子进程相反)几乎总是会导致该进程立即退出Python 进程没有任何清理(没有 finally:、没有 atexit、没有 __del__ 等)。
将您的应用程序滚动到 Windows 服务中,虽然在许多情况下是一种可行的替代方案,但也不太适合。使用pythonw.exe(所有最近的Windows Python 二进制文件附带的windowless version of Python)也是如此。特别是,它们无法改善信号处理的情况,并且它们无法轻松地从终端启动应用程序并在启动期间与之交互(例如,向脚本传递动态启动参数,例如密码、文件路径等),在“守护进程”之前。此外,Windows 服务需要安装,尽管当您第一次调用“守护程序”时可以在运行时快速完成,但它会修改用户的系统(注册表等),如果您来自一个 Unix 世界。
鉴于此,我认为使用subprocess.CREATE_NEW_PROCESS_GROUP 启动pythonw.exe 子进程可能是最接近Windows 的Python 进程模拟传统Unix 守护进程的等效项。 但是,这仍然给您带来信号处理和启动通信的额外挑战(更不用说让您的代码依赖于平台,这总是令人沮丧)。
话虽如此,对于将来遇到此问题的任何人,我推出了一个名为 daemoniker 的库,它包含了正确的 Unix 守护进程和上述策略。它还实现了信号处理(适用于 Unix 和 Windows 系统),并允许您使用 pickle 将对象传递给“守护进程”进程。最重要的是,它有一个cross-platform 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()