如何实现一个简单的跨平台 Python 守护进程?
Posted
技术标签:
【中文标题】如何实现一个简单的跨平台 Python 守护进程?【英文标题】:How do I implement a simple cross platform Python daemon? 【发布时间】:2013-02-28 01:13:09 【问题描述】:我想让我的 Python 程序作为后台程序在 Windows 或 Unix 上运行。我看到 python-daemon package 仅适用于 Unix;有没有跨平台的替代品?如果可能的话,我希望代码尽可能简单。
【问题讨论】:
我不知道答案,但我对此很感兴趣。 是的,如果有跨平台的方式来启动 Unix 守护程序或 Windows 服务,这取决于具有相同/相似代码的平台,这会很方便。也许不实用,我不知道...... 能详细说明一下你的程序是做什么的吗? @Adam,在这里描述可能太冗长和离题了 ;) 由于“守护进程”只是一个概念,并且实现是高度特定于平台的,所以这个问题没有意义。您是否在询问特定于平台的包装器,以便您的应用程序不会发生太大变化? 【参考方案1】:在 Windows 中,它被称为“服务”,您可以很容易地实现它,例如使用 win32serviceutil 模块,pywin32 的一部分。不幸的是,这两种“心理模型”——服务与守护程序——在细节上非常不同,尽管它们的用途相似,而且我知道没有任何 Python 外观试图将它们统一到一个框架中。
【讨论】:
啊,所以你实际上建议在 Windows 中走服务路线;我在想这有点矫枉过正......不过,我认为 Windows 不支持后台进程(仅支持隐藏窗口)——对吗? @Nick,对——对于 Windows 中的可靠性,服务是必经之路。这并不是真的矫枉过正——实际上,制作一个 Windows 服务相当简单,只是与制作一个 Unix 守护程序不同(除了脚手架之外,这是不同的,你的代码主体通常在两个用例之间基本上是不变的—— - Unix 守护进程和 Windows 服务)。【参考方案2】:它只是 unix 的原因是 daemons 是一个 Unix 特定的概念,即由操作系统启动的后台进程,通常作为根 PID 的子进程运行。 Windows 没有直接等效的 unix 守护程序,我能想到的最接近的是 Windows 服务。 windows 有一个名为 pythonservice.exe 的程序。不确定是否所有版本的python都支持它
【讨论】:
【参考方案3】:一般来说,守护进程的概念是 Unix 特定的,尤其是与文件创建掩码、进程层次结构和信号处理有关的预期行为。
您可能会发现 PEP 3143 很有用,其中考虑为 Python 3.2 提议的 python-daemon 的延续,并且讨论了许多 related daemonizing modules and implementations。
【讨论】:
【参考方案4】:想到两个选项:
将您的程序移植到a windows service。您可能可以在两个实现之间共享大部分代码。
您的程序真的使用任何守护程序功能吗?如果没有,您将其重写为在后台运行的简单服务器,通过套接字管理通信并执行其任务。它可能会比守护进程消耗更多的系统资源,但它会独立于引用平台。
【讨论】:
【参考方案5】:这个问题已有 6 年历史了,但我遇到了同样的问题,现有的答案对于我的用例来说还不够跨平台。尽管 Windows 服务通常以与 Unix 守护程序类似的方式使用,但归根结底,它们有很大的不同,而且“细节决定成败”。长话短说,我开始尝试找到一些东西,让我能够在 Unix 和 Windows 上运行完全相同的应用程序代码,同时尽可能地满足对行为良好的 Unix 守护进程(better explained elsewhere)的期望在两个平台上:
-
关闭打开的文件描述符(通常是全部,但某些应用程序可能需要保护某些描述符不被关闭)
将进程的工作目录更改为合适的位置,以防止出现“Directory Busy”错误
更改文件访问创建掩码(Python 世界中的
os.umask
)
将应用程序移至后台并使其与启动进程分离
完全脱离终端,包括将STDIN
、STDOUT
、STDERR
重定向到不同的流(通常是DEVNULL
),并防止重新获取控制终端
处理信号,尤其是SIGTERM
。
跨平台守护进程的根本问题在于,Windows 作为操作系统确实不支持守护进程的概念:从终端启动的应用程序(或在任何其他交互式上下文中,包括从资源管理器启动,等)将继续以可见窗口运行,除非控制应用程序(在此示例中为 Python)包含无窗口 GUI。此外,Windows 信号处理严重不足,并且尝试将信号发送到 独立 Python 进程(与无法在终端关闭后幸存的子进程相反)几乎总是会导致该进程立即退出Python 进程没有任何清理(没有finally:
、没有atexit
、没有__del__
等)。
Windows 服务(尽管在许多情况下是一种可行的替代方案)对我来说基本上是不可能的:它们不是跨平台的,而且它们需要修改代码。 pythonw.exe
(一个windowless version of Python,随所有最近的 Windows Python 二进制文件一起提供)更接近,但它仍然没有完全成功:特别是它无法改善信号处理的情况,你仍然无法轻松启动来自终端的pythonw.exe
应用程序并在启动期间与其交互(例如,为您的脚本提供动态启动参数,例如密码、文件路径等),before“守护进程”。
最后,我决定使用 subprocess.Popen
和 creationflags=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
)
但是,这仍然给我留下了启动通信和信号处理的额外挑战。无需赘述,对于前者,我的策略是:
pickle
启动进程命名空间的重要部分
将其存储在tempfile
在启动前在子进程的环境中添加该文件的路径
从“守护进程”函数中提取并返回命名空间
对于信号处理,我必须更有创意。在“守护进程”中:
-
忽略守护进程中的信号,因为如前所述,它们都会立即终止进程且无需清理
创建一个新线程来管理信号处理
该线程启动子信号处理进程并等待它们完成
外部应用程序向子信号处理进程发送信号,使其终止并完成
然后这些进程使用信号编号作为它们的返回码
信号处理线程读取返回码,然后调用用户定义的信号处理程序,或使用 cytpes API 在 Python 主线程中引发适当的异常
冲洗并重复新信号
话虽如此,对于将来遇到此问题的任何人,我已经推出了一个名为 daemoniker 的库,它将适当的 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()
【讨论】:
以上是关于如何实现一个简单的跨平台 Python 守护进程?的主要内容,如果未能解决你的问题,请参考以下文章