当前位置: 首页 > 面试题库 >

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

姬捷
2023-03-14
问题内容

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

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

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

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


问题答案:

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中的概念和解释会经受住时间的考验,光彩夺目。史蒂文斯不仅很清楚自己在说什么,而且还了解它的难处,并使之更易于理解。真的很少见



 类似资料:
  • 本文向大家介绍python实现守护进程、守护线程、守护非守护并行,包括了python实现守护进程、守护线程、守护非守护并行的使用技巧和注意事项,需要的朋友参考一下 守护进程 1、守护子进程 主进程创建守护进程 其一:守护进程会在主进程代码执行结束后就终止 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allow

  • 问题内容: 我正在做Java试卷,并且遇到了以下使我感到困惑的问题。 以下哪项是正确的?(选择所有适用项。) 答:当应用程序开始运行时,会有一个守护线程,其任务是执行main()。 B.当一个应用程序开始运行时,有一个非守护进程线程,其工作是执行main()。 C.由守护程序线程创建的线程最初也是守护程序线程。 D.由非守护程序线程创建的线程最初也是非守护程序线程。 关键答案是B,C,D,谁能告诉

  • 问题内容: 我编写了一个作为守护程序运行的小型Python应用程序。它利用线程和队列。 我正在寻找更改此应用程序的常规方法,以便可以在其运行时与其进行通信。通常,我希望能够监控其健康状况。 简而言之,我希望能够执行以下操作: 稍后,我希望能够进行以下操作: 明确地说,实现Django启发式语法没有任何问题。我不知道该怎么做是将信号发送到守护进程(启动),或者如何编写守护进程来处理和响应此类信号。

  • 如何确定我的 Gradle 守护进程死亡的原因?我收到的唯一消息是L 这发生在活动版本中。几个步骤将完成,一个步骤将显示为活动状态,然后生成失败。 这是在将我们的内存参数(< code > Xmx < code > Xms < code > perm gen )从一个调用< code>gradlew的shell脚本移动到< code>gradle.properties并直接调用< code>gra

  • 问题内容: 我有一个查询,就是我在多个线程下面开发了一个代码,这些线程分别称为线程一和线程二,下面是代码.. 类multip实现Runnable { 现在,我希望将线程2设置为守护程序线程,它应该为线程1提供某种服务,它可以是任何类型的服务,但最后我试图实现的是,守护程序线程向非守护程序线程提供某种服务。请告知如何实现。问题是有关守护程序线程将如何向非守护程序线程提供服务的问题。 问题答案: 问题

  • 问题内容: 我有一个脚本,该脚本每隔X次运行一次我的PHP脚本: 如何将其作为守护程序启动? 问题答案: 要将其作为完整的守护程序从Shell运行,您需要使用并重定向其输出。您可以将输出重定向到日志文件,也可以将其丢弃。假设您的脚本名为myscript.sh,请使用以下命令: 这会将进程与当前的shell(stdin,stdout和stderr)完全分开。如果要将输出保留在日志文件中,请用/ pa