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

时间:2010-02-13 15:38:13

标签: python cross-platform daemon

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

5 个答案:

答案 0 :(得分:10)

在Windows中,它被称为“服务”,您可以非常轻松地实现它,例如使用win32serviceutil模块,pywin32的一部分。不幸的是,两个“心理模型” - 服务与守护进程 - 在细节上有很大的不同,即使它们用于类似的目的,我知道没有Python外观试图将它们统一到一个框架中。

答案 1 :(得分:4)

有两种选择:

  1. 将您的计划移植到a windows service。您可以在两个实现之间共享大部分代码。

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

答案 2 :(得分:4)

这个问题是6岁,但我遇到了同样的问题,现有的答案对于我的用例来说并不是跨平台的。虽然Windows服务通常以与Unix守护程序类似的方式使用,但在一天结束时它们大不相同,并且“细节中的魔鬼”。长话短说,我开始尝试找到能让我在Unix和Windows上运行完全相同的应用程序代码的东西,同时满足对行为良好的Unix守护进程(better explained elsewhere)的期望。尽可能在两个平台上:

  1. 关闭打开的文件描述符(通常是所有这些描述符,但某些应用程序可能需要保护某些描述符不被关闭)
  2. 将流程的工作目录更改为合适的位置,以防止“目录繁忙”错误
  3. 更改文件访问创建掩码(Python世界中的os.umask
  4. 将应用程序移至后台并使其与启动过程分离
  5. 完全与终端离婚,包括将STDINSTDOUTSTDERR重定向到不同的流(通常为DEVNULL),并阻止重新获取控制终端
  6. 处理信号,尤其是SIGTERM
  7. 跨平台守护进程的根本问题在于,Windows作为一个操作系统,实际上不支持守护进程的概念:从终端开始的应用程序(或任何其他交互式环境中的应用程序,包括从资源管理器启动,除非控制应用程序(在本例中为Python)包含无窗口GUI,否则将继续以可见窗口运行。此外,Windows信号处理严重不足,并且尝试将信号发送到独立的 Python进程(与子进程相反,后者无法在终端关闭后继续存在)几乎总会导致立即退出Python流程没有任何清理(没有finally:,没有atexit,没有__del__等等。

    Windows服务(虽然在许多情况下是一种可行的替代方案)对我来说基本上是不可能的:它们不是跨平台的,它们将需要代码修改。 pythonw.exe(与所有最近的Windows Python二进制文件一起提供的windowless version of Python更接近,但它仍然没有完全削减:特别是,它无法改善信号处理的情况,而你仍然无法轻松地从终端启动pythonw.exe应用程序并在启动期间与其进行交互(例如,为您的脚本提供动态启动参数,例如,密码,文件路径等),之前“daemonizing”。

    最后,我决定使用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. 的重要部分
    3. 将其存储在tempfile
    4. 在启动
    5. 之前,在子进程的环境中添加该文件的路径
    6. 从“daemonization”函数
    7. 中提取并返回命名空间

      对于信号处理,我必须更有创意。在“守护进程”过程中:

      1. 忽略守护程序进程中的信号,因为如上所述,它们都会立即终止进程而不进行清理
      2. 创建一个新线程来管理信号处理
      3. 该线程启动子女信号处理流程并等待他们完成
      4. 外部应用程序向子信号处理过程发送信号,使其终止并完成
      5. 然后,这些进程使用信号编号作为返回码
      6. 信号处理线程读取返回码,然后调用用户定义的信号处理程序,或使用cytpes API在Python主线程中引发适当的异常
      7. 冲洗并重复以获取新信号
      8. 所有人都说,对于将来遇到此问题的任何人,我已经推出了一个名为daemoniker的库,它将上述Windows策略的正确Unix守护程序包装成统一的正面。 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()
        

答案 3 :(得分:2)

通常,守护进程的概念是特定于Unix的,特别是关于文件创建掩码,进程层次结构和信号处理的预期行为。

您可能会发现PEP 3143很有用,其中对于Python 3.2考虑了python-daemon的建议延续,并讨论了许多related daemonizing modules and implementations

答案 4 :(得分:0)

它仅仅是unix的原因是daemons是一个特定于Unix的概念,即由os启动的后台进程,通常作为根PID的子进程运行。
Windows没有直接等效的unix守护进程,我能想到的最接近的是Windows服务。 有一个名为pythonservice.exe的程序用于Windows。不确定它是否支持所有版本的python但