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

标签 python linux daemons

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

但是守护程序并不是那么容易理解。根据PEP 3143的Rationale段落,行为良好的守护程序应执行以下操作。

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

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

    最佳答案

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

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

    "We close any open descriptors inherited from the process that executed the daemon (ie the shell). [..] Some daemons open /dev/null for reading and writing and duplicate the descriptor to standard input, standard output and standard error."



    (此'Howdy World' Python daemon演示了这一点。)

    "This guarantees that the common descriptors are open, and a read from any of these descriptors returns 0 (End Of File) and the kernel just discards anything written to any of these three descriptors. The reason for opening these descriptors is so that any library function called by the daemon that assumes it can read from standard input or write to standard output or standard error, will not fail. Alternately, some daemons open a log file that they will write to while running and duplicate its descriptor to standard output and standard error". (UNP p. 337)



    更改当前工作目录

    "A printer daemon might change to the printer's spool directory, where it does all its work. [...] The daemon could have been started anywhere in the filesystem, and if it remains there, that filesystem cannot be unmounted." (UNP p 337)



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

    重置文件访问创建掩码。

    "So that if the daemon creates its own files, permission bits in the inherited file mode creation mask do not affect the permission bits of the new files." (UNP, p 337)



    在后台运行。
    根据定义,

    "a daemon is a process that runs in the background and is independent of control from all terminals". (UNP p 331)



    与流程组解除关联。
    为了理解这一点,您需要了解什么是进程组,这意味着您需要知道fork的功能。

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

    为什么要脱离流程组

    关键是 session 主持人可以获取控制终端。守护程序永远不要这样做,它必须留在后台。这可以通过两次调用fork来实现:父派生创建一个子代,该子派生创建一个子代。 parent 子女被终止,但是孙子女仍然存在。但是因为是孙子,所以不是 session 负责人,因此无法获得控制终端。 (摘自UNP par 12.4 p 335)

    here和下面的注释中将更详细地讨论双叉。

    忽略终端I/O信号。

    "Signals generated from terminal keys must not affect any daemons started from that terminal earlier". (UNP p. 331)



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

    "If the daemon is started from a terminal, we want to be able to use that terminal for other tasks at a later time. For example, if we start the daemon from a terminal, log off the terminal, and someone else logs in on that terminal, we do not want any daemon error messages appearing during the next user's terminal session." (UNP p 331)



    正确处理以下情况:
  • 由System V初始化过程启动
  • 显然,守护程序应该在引导时启动。
  • 守护程序通过SIGTERM信号终止
  • SIGTERM表示信号终止。在关闭时,初始化进程通常将SIGTERM发送给所有进程,通常等待5到20秒,以使它们有时间清理并终止。 (UNP,p 135)而且,当 parent 停止做事时, child 也可以将SIGTERM发送给 parent 。 (UNP p 408)
  • child 产生SIGCLD信号
  • Stevens讨论的是SIGCHLD,而不是SIGCLD。它们之间的区别对于了解守护程序的行为并不重要。如果子项终止,则它将SIGCHLD发送给其父项。如果 parent 没有捕获它, child 就会变成僵尸(UNP p 118)。哦,真有趣。

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

    关于python - 解释了正确的守护程序行为(来自PEP 3143),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23515165/

    相关文章:

    java - Java包装器作为守护程序

    python - 在 python 中迭代列以生成单独的图

    Python argparse - 禁用子命令的帮助?

    hadoop - 无法运行 Hadoop 守护进程

    c - 文件存在时 fopen 失败

    linux - 如何使用find和for循环遍历每个文件

    ruby - 如何在停止时杀死 ruby​​ Thor 中的所有进程?

    python - 用于多类分类的 SVM(一对多)置信度估计

    python - CommandOnCooldown 错误处理程序未捕获错误!已经测试了一切

    c - 用于 uart 的 linux 驱动程序-总体设计