正确的守护进程行为(来自PEP 3143)解释

时间:2014-05-07 10:23:59

标签: python linux daemons

我在Python中有一些[针对我的RPi]的任务涉及很多sleep:做一些需要一两秒钟或三秒钟的事情,然后等待几分钟或几小时。 我希望在睡眠时间内将控制权传递给操作系统(Linux)。为此,我应该躲避那些任务。一种方法是使用Python的标准守护程序进程库。

但守护进程并不那么容易理解。根据{{​​3}}的基本原理段落,一个表现良好的守护进程应该执行以下操作。

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

对于像我这样的Linux / Unix新手,其中一些不是解释。但我想知道为什么我做我做的事。那么这个理由背后的理由是什么?

1 个答案:

答案 0 :(得分:10)

PEP 3142接受了已故W. Richard Stevens的Unix Network Programming(' UNP')的这些要求。下面的解释是从该书中引用或总结的。它不是那么容易在网上找到,下载可能是违法的。所以我从图书馆借了它。提到的页面是第二版,第1卷(1998年)。 (PEP指的是第一版,1990年。)

关闭所有打开的文件描述符。

  

"我们关闭从执行守护进程的进程(即shell)继承的所有开放描述符。 [..]一些守护进程打开/dev/null进行读写,并将描述符复制到标准输入,标准输出和标准错误。"

(这'Howdy World' Python daemon证明了这一点。)

  

"这保证了公共描述符是开放的,并且从这些描述符中的任何一个读取都返回0(文件结束),并且内核只丢弃写入这三个描述符中的任何一个的任何内容。打开这些描述符的原因是,守护程序调用的任何库函数(假定它可以从标准输入读取或写入标准输出或标准错误)都不会失败。或者,一些守护进程打开一个日志文件,在运行时将写入,并将其描述符复制到标准输出和标准错误"。 (UNP第337页)

更改当前工作目录

  

"打印机守护程序可能会更改为打印机的假脱机目录,它可以完成所有工作。 [...]守护程序可以在文件系统的任何地方启动,如果它仍然存在,则无法卸载该文件系统。" (UNP第337页)

为什么要卸载文件系统?两个原因:
1.您希望分离(并能够安装和卸载)可以填充操作系统专用目录中的用户数据的目录。
2.如果你从USB棒开始一个守护进程,你希望能够在不干扰守护进程的情况下卸下那根棒。

重置文件访问权限创建掩码。

  

"因此,如果守护程序创建自己的文件,则继承的文件模式创建掩码中的权限位不会影响新文件的权限位。" (UNP,第337页)

在后台运行。
根据定义,

  

"守护进程是一个在后台运行的进程,与所有终端的控制无关。#34;。 (UNP第331页)

与流程组取消关联。
为了理解这一点,您需要了解流程组是什么,这意味着您需要知道fork的作用。

什么分叉

fork是创建新进程的唯一方法(在Unix中)。 (在Linux中,还有clone)。理解fork的关键是它在调用(一次)时返回两次:一次在调用进程(= parent)中,使用新创建的进程(= child)的进程ID,并且一次在孩子身上。 "父项在分叉时已知的所有描述符都在fork返回时与子项共享。" (UNP第102页)。 当一个进程想要执行另一个程序时,它会通过调用fork创建一个新进程,fork会创建一个自己的副本。然后其中一个(通常是孩子)调用新程序。 (UNP,第102页)

为何与进程组取消关联

关键是会话领导者可以获得控制终端。守护进程永远不应该这样做,它必须留在后台。这是通过两次调用fork来实现的:父母分叉创建一个孩子,孩子分叉创建一个孙子。父母和孩子被终止,但孙子仍然存在。但由于它是一个孙子,它不是会话领导者,因此无法获得控制终端。 (摘自UNP第12.4节第335页)

双叉更详细地讨论了here,以及下面的评论。

忽略终端I / O信号。

  

"从终端密钥生成的信号不得影响之前从该终端启动的任何守护进程"。 (UNP第331页)

取消与控制终端的关联,不要重新获取控制终端。
到目前为止,原因很明显:

  

"如果守护程序是从终端启动的,我们希望以后能够将该终端用于其他任务。例如,如果我们从终端启动守护程序,注销终端,并且其他人登录该终端,我们不希望在下一个用户的终端会话期间出现任何守护程序错误消息。" (UNP第331页)

正确处理以下情况:

  • 由System V init进程启动

    • 显然,守护进程应该在启动时启动。
  • 通过SIGTERM信号终止守护进程

    • SIGTERM表示信号终止。在关闭时,init进程通常将SIGTERM发送到所有进程,通常等待5到20秒,以便给他们时间来清理和终止。 (UNP,第135页)此外,当孩子的父母应该停止做他们正在做的事情时,孩子可以将SIGTERM发送给其父母。 (UNP第408页)
  • 儿童产生SIGCLD信号

    • Stevens讨论SIGCHLD,而不是SIGCLD。它们之间的区别对于理解守护进程行为并不重要。如果孩子终止,它会向其父母发送SIGCHLD。如果父母没有抓住它,孩子就变成了僵尸(UNP第118页)。哦,好玩。

最后一点,当我开始在UNP中找到我的问题的答案时,很快就让我感到震惊,我真的应该阅读更多内容。从1998年开始,这是900多页(!),但我相信UNP的概念和解释经得起时间的考验,光荣地说。史蒂文斯不仅非常清楚他所谈论的是什么,他也理解了它的难点,并使其更容易理解。这真的很少见。