如何在Windows上使用Python启动守护进程?

14

我的Python脚本能否生成一个可以无限运行的进程?

我对Python和生成守护进程不太熟悉,因此我想到了这个:

si = subprocess.STARTUPINFO()
si.dwFlags = subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.CREATE_NEW_CONSOLE
subprocess.Popen(executable, close_fds = True, startupinfo = si)

该进程在python.exe进程关闭后继续运行,但一旦我关闭cmd窗口,它也会被关闭。


2
请查看这里:https://dev59.com/qE7Sa4cB1Zd3GeqP1jag#2974177 - Janne Karila
3个回答

22

使用 Janne Karila 提供的答案,以下是运行一个进程的方法,当父进程死亡时子进程不会死亡,而无需使用 win32process 模块。

DETACHED_PROCESS = 8
subprocess.Popen(executable, creationflags=DETACHED_PROCESS, close_fds=True)

DETACHED_PROCESS 是传递给底层的 CreateProcess 函数的一个 进程创建标志


你从哪里得到的DETACHED_PROCESS? 它看起来不像是subprocess常量之一: https://docs.python.org/2/library/subprocess.html#constants - rstackhouse
我在Python 3.7文档中找到了 DETACHED_PROCESS - 您可以使用 subprocess.DETACHED_PROCESS - KYL3R

18
这个问题是3年前提出的,虽然答案的基本细节没有改变,但考虑到它在“Windows Python守护进程”搜索中的普遍性,我认为增加一些讨论有助于未来的谷歌用户。这个问题实际上包含了两部分:
1. Python脚本可以生成一个独立的进程并运行无限长时间吗? 2. Python脚本能在Windows系统上像Unix守护进程一样运行吗?
第一个问题的答案是肯定的; 如已指出;使用带有`creationflags=subprocess.CREATE_NEW_PROCESS_GROUP`关键字的 `subprocess.Popen` 即可。
第二个问题需要更多的解释。
import subprocess

independent_process = subprocess.Popen(
    'python /path/to/file.py',
    creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
)

请注意,至少在我的经验中,此处不需要使用CREATE_NEW_CONSOLE
话虽如此,这种策略的行为与Unix守护进程的预期行为并不完全相同。什么是良好的Unix守护进程在其他地方更好地解释,但总结起来:
  1. 关闭打开的文件描述符(通常是所有文件描述符,但某些应用程序可能需要保护一些文件描述符以免被关闭)
  2. 将进程的工作目录更改为适当的位置,以防止“目录繁忙”错误
  3. 更改文件访问创建掩码(Python世界中的os.umask
  4. 将应用程序移动到后台,并使其与启动进程分离
  5. 完全脱离终端,包括将STDINSTDOUTSTDERR重定向到不同的流(通常是DEVNULL),并防止重新获取控制终端
  6. 处理信号,特别是SIGTERM
The reality of the situation is that Windows,作为一个操作系统,实际上并不支持守护进程的概念:从终端启动(或从资源管理器等任何交互式上下文中启动)的应用程序将继续显示窗口运行,除非控制应用程序(在此示例中为Python)包含无窗口GUI。此外,Windows信号处理功能非常不足,尝试向独立的Python进程(而不是子进程,后者在终端关闭后不会存活)发送信号几乎总会导致该Python进程立即退出,而没有任何清理(没有finally:,没有atexit,没有__del__等)。

将应用程序转换为Windows服务虽然在许多情况下是可行的替代方案,但并不完全适合。同样适用于使用pythonw.exe(一种与所有最近的Windows Python二进制文件捆绑在一起的无窗口版本的Python)。特别是它们不能改善信号处理的情况,并且不能轻松地从终端启动应用程序并在启动期间与其进行交互(例如,向脚本提供动态启动参数,例如密码、文件路径等),"daemonizing"之前。此外,Windows服务需要安装,在第一次调用"daemon"时可以快速运行时进行安装,但这会修改用户的系统(注册表等),如果您来自Unix世界,则这可能会令人意外。

考虑到这一点,我认为使用subprocess.CREATE_NEW_PROCESS_GROUP启动pythonw.exe子进程可能是Python进程在Windows上模拟传统Unix守护进程的最接近等效方式。但是,这仍然需要解决信号处理和启动通信的附加挑战(更不用说使您的代码平台相关,这总是令人沮丧的)。

话虽如此,对于将来遇到这个问题的任何人,我已经编写了一个名为 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()

高尼克,我要试一试将一个仅适用于Linux的脚本移植为跨平台依赖。之前使用的是python-daemon,等我试完了会回报结果!在你的帖子顶部添加一个注释可能是个好主意,这样寻找答案的人就可以更容易地看到它 :) - Cogito

6
为此,您可以将您的Python进程变成守护进程,或者由于您使用的是Windows环境,您希望将其作为Windows服务运行。
您知道我不喜欢只发布网页链接:
但是,根据您的要求,获取更多信息: 实现Windows服务的简单方法 阅读所有评论,它将解决任何疑问
如果您真的想了解更多信息
首先阅读这个
什么是守护进程或用Python创建守护进程 更新:
子进程不是实现这种事情的正确方式

谢谢,但我不需要服务,事实上我不能使用服务,因为有人告诉我不要使用。 - zzandy
很高兴知道,另一个答案的链接对你有用。但是为了让你完全了解它是如何完成的,实际上你需要打开subprocess.py(并从第599行开始阅读),你会发现它与创建守护进程相同,但现在我认为popen对你更好。 - Rahul Gautam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接