Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 3143 – 标准守护进程库

作者:
Ben Finney <ben+python at benfinney.id.au>
状态:
推迟
类型:
标准跟踪
创建日期:
2009年1月26日
Python 版本:
3.x
发布历史:


目录

摘要

编写一个程序使其成为一个行为良好的Unix守护进程,过程有些复杂且容易出错,但无论程序还需要做些什么,这些步骤对于任何守护进程来说都大同小异。

本PEP向Python标准库引入了一个包,它为成为守护进程提供了简单的接口。

PEP 延期

对本 PEP 中涵盖概念的进一步探索已被推迟,因为目前缺乏一位有兴趣推广 PEP 目标、收集和整合反馈,并有足够可用时间有效完成此任务的倡导者。

规范

使用示例

直接使用DaemonContext的简单示例

import daemon

from spam import do_main_program

with daemon.DaemonContext():
    do_main_program()

更复杂的用法示例

import os
import grp
import signal
import daemon
import lockfile

from spam import (
    initial_program_setup,
    do_main_program,
    program_cleanup,
    reload_program_config,
    )

context = daemon.DaemonContext(
    working_directory='/var/lib/foo',
    umask=0o002,
    pidfile=lockfile.FileLock('/var/run/spam.pid'),
    )

context.signal_map = {
    signal.SIGTERM: program_cleanup,
    signal.SIGHUP: 'terminate',
    signal.SIGUSR1: reload_program_config,
    }

mail_gid = grp.getgrnam('mail').gr_gid
context.gid = mail_gid

important_file = open('spam.data', 'w')
interesting_file = open('eggs.data', 'w')
context.files_preserve = [important_file, interesting_file]

initial_program_setup()

with context:
    do_main_program()

接口

一个新包daemon被添加到标准库中。

定义了一个类DaemonContext,用于表示作为守护进程运行的程序的设置和进程上下文。

DaemonContext 对象

一个DaemonContext实例表示程序成为守护进程时的行为设置和进程上下文。在调用open方法之前,通过设置实例上的选项来自定义行为和环境。

每个选项都可以作为关键字参数传递给DaemonContext构造函数,或者在调用open之前的任何时间通过赋值给实例上的属性来更改。也就是说,对于名为wibblewubble的选项,以下调用

foo = daemon.DaemonContext(wibble=bar, wubble=baz)
foo.open()

等价于

foo = daemon.DaemonContext()
foo.wibble = bar
foo.wubble = baz
foo.open()

定义了以下选项。

files_preserve
默认值:

启动守护进程时不应关闭的文件列表。如果为None,则所有打开的文件描述符都将被关闭。

列表中的元素是文件描述符(由文件对象的fileno()方法返回)或Python file对象。每个元素都指定一个在守护进程启动期间不关闭的文件。

chroot_directory
默认值:

要设置为进程有效根目录的完整路径。如果为None,则表示根目录不应更改。

working_directory
默认值:
'/'

守护进程启动时进程应更改到的工作目录的完整路径。

如果进程的当前工作目录位于某个文件系统上,则该文件系统无法卸载,因此应将此项保留为默认值,或设置为守护进程运行时的合理“主目录”的目录。

umask
默认值:
0

守护进程启动时为进程设置的文件访问创建掩码(“umask”)。

由于进程从其父进程继承其umask,因此启动守护进程会将umask重置为该值,以便守护进程创建的文件具有其期望的访问模式。

pidfile
默认值:

PID锁文件的上下文管理器。当守护进程上下文打开和关闭时,它会进入和退出pidfile上下文管理器。

detach_process
默认值:

如果为True,则在打开守护进程上下文时分离进程上下文;如果为False,则不分离。

如果在实例初始化期间未指定(None),则默认将其设置为True,并且仅当确定分离进程是冗余时才设置为False;例如,在进程由initinitdinetd启动的情况下。

signal_map
默认值:
系统相关

操作系统信号到回调动作的映射。

映射在守护进程上下文打开时使用,并确定每个信号处理程序的动作

  • None将忽略信号(通过将信号动作设置为signal.SIG_IGN)。
  • 字符串值将用作DaemonContext实例上属性的名称。属性的值将用作信号处理程序的动作。
  • 任何其他值将用作信号处理程序的动作。

默认值取决于运行系统上定义的信号。以下列表中其信号实际在signal模块中定义的每个项都将出现在默认映射中

  • signal.SIGTTIN: None
  • signal.SIGTTOU: None
  • signal.SIGTSTP: None
  • signal.SIGTERM: 'terminate'

根据程序如何与其子进程交互,它可能需要指定一个包含signal.SIGCHLD信号(当子进程退出时接收)的信号映射。有关如何确定需要信号处理程序的具体情况的更多详细信息,请参阅特定操作系统的文档。

uid
默认值:
os.getuid()
gid
默认值:
os.getgid()

守护进程启动时要将进程切换到的用户ID(“UID”)值和组ID(“GID”)值。

默认值,即进程的实际UID和GID,将放弃进程继承的任何有效权限提升。

prevent_core
默认值:

如果为true,则防止生成核心文件,以避免从以root身份运行的守护进程泄露敏感信息。

stdin
默认值:
stdout
默认值:
stderr
默认值:

stdinstdoutstderr中的每一个都是一个文件类对象,将分别用作标准I/O流sys.stdinsys.stdoutsys.stderr的新文件。因此,该文件应处于打开状态,对于stdin,最小模式为“r”,对于stdoutstderr,最小模式为“w+”。

如果对象具有返回文件描述符的fileno()方法,则在守护进程启动期间将排除相应文件不被关闭(即,它将被视为已列入files_preserve)。

如果为None,则相应的系统流将重新绑定到由os.devnull命名的文件。

定义了以下方法。

open()
返回:

打开守护进程上下文,将当前程序转换为守护进程。这执行以下步骤

  • 如果此实例的is_open属性为true,则立即返回。这使得在实例上多次调用open是安全的。
  • 如果prevent_core属性为true,则设置进程的资源限制以防止进程产生任何核心转储。
  • 如果chroot_directory属性不为None,则将进程的有效根目录设置为该目录(通过os.chroot)。

    这允许在“chroot监狱”中运行守护进程,作为限制系统暴露于进程恶意行为的一种手段。请注意,指定的目录需要为此目的而设置好。

  • 将进程UID和GID设置为uidgid属性值。
  • 关闭所有打开的文件描述符。这不包括列在files_preserve属性中的文件,以及与stdinstdoutstderr属性相对应的文件。
  • 将当前工作目录更改为由working_directory属性指定的路径。
  • 将文件访问创建掩码重置为由umask属性指定的值。
  • 如果detach_process选项为true,则将当前进程分离到其自己的进程组中,并与任何控制终端解除关联。
  • 根据signal_map属性设置信号处理程序。
  • 如果stdinstdoutstderr中的任何属性不为None,则将系统流sys.stdinsys.stdout和/或sys.stderr绑定到由相应属性表示的文件。如果属性具有文件描述符,则复制描述符(而不是重新绑定名称)。
  • 如果pidfile属性不为None,则进入其上下文管理器。
  • 将此实例标记为打开(用于将来的openclose调用)。
  • 注册close方法以便在Python退出处理期间调用。

当函数返回时,正在运行的程序是一个守护进程。

关闭()
返回:

关闭守护进程上下文。这执行以下步骤

  • 如果此实例的is_open属性为false,则立即返回。这使得在实例上多次调用close是安全的。
  • 如果pidfile属性不为None,则退出其上下文管理器。
  • 将此实例标记为已关闭(用于将来的openclose调用)。
is_open
返回:
如果实例已打开,则为True,否则为False

此属性公开了指示实例当前是否打开的状态。如果实例的open方法已被调用,并且close方法尚未随后被调用,则它为True

terminate(signal_number, stack_frame)
返回:

signal.SIGTERM信号的信号处理程序。执行以下步骤

  • 引发一个解释信号的SystemExit异常。

该类还通过__enter____exit__方法实现了上下文管理器协议。

__enter__()
返回:
DaemonContext实例

调用实例的open()方法,然后返回实例。

__exit__(exc_type, exc_value, exc_traceback)
返回:
由上下文管理器协议定义的TrueFalse

调用实例的close()方法,然后如果异常已处理则返回True,否则返回False

动机

大多数编写为Unix守护进程的程序要么实现与规范中非常相似的行为,要么按照正确的守护进程行为是行为不佳的守护进程。

由于这些步骤在大多数实现中都应大同小异,但它们非常特殊且容易遗漏或错误实现,因此它们是标准库中经过良好测试的标准实现的理想目标。

基本原理

正确的守护进程行为

根据Stevens在[stevens] §2.6中的说法,程序应执行以下步骤以成为Unix守护进程。

  • 关闭所有打开的文件描述符。
  • 更改当前工作目录。
  • 重置文件访问创建掩码。
  • 在后台运行。
  • 与进程组解除关联。
  • 忽略终端I/O信号。
  • 与控制终端解除关联。
  • 不要重新获取控制终端。
  • 正确处理以下情况
    • 由System V init进程启动。
    • SIGTERM信号终止守护进程。
    • 子进程生成SIGCLD信号。

daemon工具[slack-daemon]列出了(在其功能摘要中)在将程序转换为行为良好的Unix守护进程时应执行的行为。它与本PEP的意图不同,因为它调用一个单独的程序作为守护进程。以下功能适用于程序已经运行后启动自身的守护进程

  • 为守护进程设置正确的进程上下文。
  • initd(8)inetd(8)启动时行为合理。
  • 撤销任何suid或sgid权限,以减少守护进程安装不当具有特殊权限时的安全风险。
  • 防止生成核心文件,以防止从以root身份运行的守护进程泄露敏感信息(可选)。
  • 通过创建和锁定PID文件来命名守护进程,以保证在任何给定时间只能执行一个具有给定名称的守护进程(可选)。
  • 设置运行守护进程的用户和组(可选,仅限root)。
  • 创建chroot监狱(可选,仅限root)。
  • 捕获守护进程的stdout和stderr并将其定向到syslog(可选)。

守护进程不是服务

本PEP仅涉及Unix风格的守护进程,其上述正确行为是相关的,而不是其他操作系统上的可比行为。

在许多系统中有一个相关的概念,称为“服务”。服务与本PEP中的模型不同,服务不是让当前程序继续作为守护进程运行,而是启动一个额外的进程在后台运行,并且当前进程通过某些定义的通道与该额外进程通信。

本PEP中的Unix风格守护进程模型可以用于(除其他外)实现服务中的后台进程部分;但本PEP不涉及设置和管理服务的其他方面。

参考实现

python-daemon[python-daemon]

其他守护进程实现

在本PEP之前,一些现有的第三方Python库或工具已经实现了本PEP的正确守护进程行为中的一部分。

参考实现是以下实现的相当直接的继承者

与其他PEP不同的其他Python守护进程实现

  • zdaemon工具[zdaemon]是为Zope项目编写的。像[slack-daemon]一样,它与本规范不同,因为它用于将另一个程序作为守护进程运行。
  • Python库daemon [clapper-daemon](根据其主页)不再维护。截至1.0.1版本,它实现了[stevens]中的基本步骤。
  • daemonize[seutter-daemonize]也实现了[stevens]中的基本步骤。
  • Ray Burr的daemon.py模块[burr-daemon]提供了[stevens]过程以及PID文件处理和输出重定向到syslog。
  • Twisted [twisted]包括(或许不足为奇)一个与Twisted框架其他部分集成的进程守护化API实现;它与本PEP中的API显著不同。
  • Python initd[dagitses-initd]使用[clapper-daemon],实现了Unix initd(8)的等效功能,用于控制守护进程。

参考资料

[stevens] (1, 2, 3, 4)
Unix 网络编程, W. Richard Stevens, 1994 Prentice Hall。
[slack-daemon] (1, 2)
“raf” <raf@raf.org>的(非Python)“libslack”实现的daemon工具http://www.libslack.org/daemon/
[python-daemon] (1, 2)
Ben Finney等人编写的python-daemonhttp://pypi.python.org/pypi/python-daemon/
[cookbook-66012] (1, 2)
Python Cookbook配方66012,“在Unix上fork一个守护进程”http://code.activestate.com/recipes/66012/
[cookbook-278731]
Python Cookbook配方278731,“用Python方式创建守护进程”http://code.activestate.com/recipes/278731/
[bda.daemon]
Robert Niederreiter等人编写的bda.daemonhttp://pypi.python.org/pypi/bda.daemon/
[zdaemon]
Guido van Rossum等人编写的zdaemon工具http://pypi.python.org/pypi/zdaemon/
[clapper-daemon] (1, 2)
Brian Clapper编写的daemonhttp://pypi.python.org/pypi/daemon/
[seutter-daemonize]
Jerry Seutter编写的daemonizehttp://daemonize.sourceforge.net/
[burr-daemon]
Ray Burr编写的daemon.py模块http://www.nightmare.com/~ryb/code/daemon.py
[twisted]
Glyph Lefkowitz等人编写的Twisted应用程序框架http://pypi.python.org/pypi/Twisted/
[dagitses-initd]
Michael Andreas Dagitses编写的Python initdhttp://pypi.python.org/pypi/initd/

来源:https://github.com/python/peps/blob/main/peps/pep-3143.rst

最后修改:2025-02-01 08:59:27 GMT