一尘不染

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

linux

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

但是守护程序并不是那么容易理解。根据PEP3143的“基本原理”段落,行为良好的守护程序应执行以下操作。

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

对于像我这样的Linux / Unix新手来说,其中有些很难解释。但是我想知道为什么我要做什么。那么,这个原理背后的原理是什么?


阅读 265

收藏
2020-06-07

共1个答案

一尘不染

PEP 3142从已故W. Richard Stevens的UnixNetworkProgramming(’UNP’)中获得了这些要求。该书引用或总结了以下解释。在网上很难找到它,并且下载可能是非法的。所以我从图书馆借来的。涉及的页面在第二版,第1卷(1998)中。(PEP指的是第一版,1990年。)

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

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

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

更改当前工作目录

“打印机守护程序可能会更改到打印机的假脱机目录,在其中执行所有工作。[…]守护程序可能已在文件系统中的任何位置启动,如果保留在该文件系统中,则无法卸载该文件系统。”
(UNP p 337)

您为什么要卸载文件系统?原因有两个:
1.您想从专用于OS的目录中分离(并能够挂载和卸载)可以填充用户数据的目录。
2.如果从USB记忆棒启动守护程序,则希望能够卸载该记忆棒而不干扰守护程序。

重置文件访问创建掩码。

“因此,如果守护程序创建自己的文件,则继承文件模式创建掩码中的权限位不会影响新文件的权限位。” (联合国开发计划署,p 337)

在后台运行。
根据定义,

“守护程序是在后台运行并且独立于所有终端的控制的进程”。(联合国开发计划署p 331)

与流程组分离。
为了理解这一点,您需要了解什么是流程组,这意味着您需要知道它是什么fork

叉子做什么

fork(在Unix中)是创建新进程的唯一方法。(在Linux中,也有clone)。理解的关键fork在于,它在被调用时(一次)返回 两次
:一次在调用过程(=父进程)中,具有新创建的进程的进程ID(=子进程),一次在子进程中。“分叉返回时,父级知道的所有描述符都与子级共享。” (UNP p
102)。当一个进程要执行另一个程序时,它将通过调用fork创建一个新进程,而fork将创建其自身的副本。然后其中一个(通常是孩子)调用新程序。(联合国开发计划署,p
102)

为什么要脱离流程组

关键是会话主持人可以获取控制终端。守护程序永远不要这样做,它必须留在后台。这可以通过调用fork两次来实现:父叉创建一个孩子,子叉创建一个孙。父母子女被终止,但是孙子女仍然存在。但是因为是孙子,所以不是会话负责人,因此无法获得控制终端。

忽略端子I / O信号。

“从终端键生成的信号不得影响从该终端先前启动的任何守护程序”。(联合国开发计划署第331页)

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

“如果守护程序是从终端启动的,我们希望以后能够使用该终端执行其他任务。例如,如果我们从终端启动该守护程序,请注销该终端,然后其他人登录该终端,我们不希望在下一个用户的终端会话期间出现任何守护程序错误消息。”
(联合国开发计划署p 331)

正确处理以下情况:

  • 由System V初始化过程开始

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

    • SIGTERM表示信号终止。在关闭时,init进程通常会将SIGTERM发送给所有进程,通常等待5至20秒,以使它们有时间清理和终止。(UNP,p 135)而且,当父母停止做事时,孩子可以将SIGTERM发送给父母。(UNP p 408)

    • 儿童产生SIGCLD信号

    • 史蒂文斯(Stevens)讨论的是SIGCHLD,而不是SIGCLD。它们之间的区别对于了解守护程序的行为并不重要。如果子项终止,则它将SIGCHLD发送给其父项。如果父母没有抓住它,孩子就会变成僵尸(UNP p 118)。哦,真有趣。

最后,当我开始在联合国开发计划署中找到问题的答案时,它很快使我震惊,我真的应该多读一些。从1998年(!)开始,它有900多个(!)页面,但我相信UNP中的概念和解释会经受住时间的考验,光彩夺目。史蒂文斯不仅很清楚自己在说什么,而且还了解它的难处,并使之更易于理解。真的很少见

2020-06-07