如何实现一个简单的跨平台Python守护进程?

2024-05-18 17:42:14 发布

您现在位置:Python中文网/ 问答频道 /正文

我希望我的Python程序作为后台程序在Windows或Unix上运行。我看到python-daemon package仅适用于Unix;是否有跨平台的替代方案?如果可能的话,我希望代码尽可能简单。


Tags: 代码程序packagewindows跨平台方案unixdaemon
3条回答

这个问题已经有6年的历史了,但是我也遇到了同样的问题,而且现有的答案对于我的用例来说还不够跨平台。尽管Windows服务的使用方式通常与Unix守护进程类似,但归根结底,它们有很大的不同,而且“细节上是魔鬼”。长话短说,我开始尝试寻找一些东西,使我能够在Unix和Windows上运行完全相同的应用程序代码,同时尽可能在这两个平台上实现对性能良好的Unix守护进程(即better explained elsewhere)的期望:

  1. 关闭打开的文件描述符(通常是所有这些描述符,但某些应用程序可能需要保护某些描述符不被关闭)
  2. 将进程的工作目录更改为适当的位置,以防止出现“目录忙”错误
  3. 更改文件访问创建掩码(Python世界中的os.umask
  4. 将应用程序移到后台并使其与启动过程分离
  5. 完全脱离终端,包括将STDINSTDOUTSTDERR重定向到不同的流(通常是DEVNULL),并防止控制终端的重新获取
  6. 处理信号,特别是SIGTERM

跨平台守护进程的基本问题是,作为操作系统的Windows实际上不支持守护进程的概念:从终端(或在任何其他交互上下文中,包括从Explorer启动等)启动的应用程序将继续使用可见窗口运行,除非控制应用程序(在本例中为Python)包含无窗口GUI。此外,Windows信号处理严重不足,试图将信号发送到独立的Python进程(而不是子进程,子进程在终端关闭后将无法生存)几乎总是会导致Python进程立即退出而不进行任何清理(没有finally:,没有atexit,没有__del__等)。

Windows服务(尽管在很多情况下是一个可行的替代方案)对我来说基本上是不可能的:它们不是跨平台的,而且需要修改代码。pythonw.exe(一个与所有最新的Windows Python二进制文件一起发布的windowless version of Python)更接近,但它仍然没有完全成功:特别是,它未能改善信号处理的情况,而且您仍然无法轻松地从终端启动一个pythonw.exe应用程序并在启动期间与之交互(例如,要向脚本传递动态启动参数,例如,密码、文件路径等,请在“守护”之前执行

最后,我决定使用subprocess.Popencreationflags=subprocess.CREATE_NEW_PROCESS_GROUP关键字创建一个独立的、无窗口的进程:

import subprocess

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

然而,这仍然给我留下了启动通信和信号处理的额外挑战。对于前者,我的策略是:

  1. pickle启动进程命名空间的重要部分
  2. 将其存储在tempfile
  3. 在启动之前,在子进程的环境中添加该文件的路径
  4. 从“daemonization”函数中提取并返回命名空间

在信号处理方面,我得有点创意。在“守护”进程中:

  1. 忽略守护进程中的信号,因为如前所述,它们都会立即终止进程而不进行清理
  2. 创建一个新线程来管理信号处理
  3. 该线程启动子信号处理进程并等待它们完成
  4. 外部应用程序向子信号处理进程发送信号,使其终止并完成
  5. 然后这些进程使用信号号作为返回码
  6. 信号处理线程读取返回代码,然后调用用户定义的信号处理程序,或使用cytpes API引发适当的异常Python主线程中的操作
  7. 冲洗并重复新信号

尽管如此,对于将来遇到这个问题的人来说,我已经将一个名为daemoniker的库打包成一个统一的facade,它将适当的Unix守护上述Windows策略包装在一起。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()

在Windows中,它被称为“服务”,您可以很容易地实现它,例如使用win32serviceutil模块,它是pywin32的一部分。不幸的是,这两个“心智模型”(service vs daemon)在细节上非常不同,尽管它们的用途相似,而且我知道没有Python门面试图将它们统一到一个框架中。

我想到两个选择:

  1. 将程序导入a windows service。您可能可以在两个实现之间共享大部分代码。

  2. 你的程序真的使用任何守护程序功能吗?如果不是,则将其重写为在后台运行、通过套接字管理通信并执行其任务的简单服务器。它可能会比守护进程消耗更多的系统资源,但是它是独立于平台的。

相关问题 更多 >

    热门问题