我在Python中有一些任务(包括大量的sleep
:做一些需要一两秒钟或三秒钟的事情,然后等待几分钟或几小时。 我想在这个睡眠时间将控制权交给操作系统(Linux)。 为此,我应该对这些任务进行守护。 一种方法是使用Python的标准守护进程库。
但是守护进程并不那么容易理解。 根据PEP 3143的理由,一个行为良好的守护进程应该做以下的事情。
对于像我这样的Linux / Unix新手来说,其中的一些并不是一个解释。 但是我想知道我为什么要做我所做的事情。 那么这个基本原理的基本原理是什么呢?
PEP 3142采用了由W. Richard Stevens提出的Unix网络编程 ('UNP')的这些要求。 下面的解释是从那本书引用或总结的。 在网上很难找到它,下载可能是非法的。 所以我从图书馆借了。 所引用的页面在第二版,第1卷(1998)中。 (PEP指的是1990年的第一版)
关闭所有打开的文件描述符。
“我们关闭任何从执行守护进程(即shell)的进程继承的描述符。[…]一些守护进程打开
/dev/null
来读写,并将描述符复制到标准输入,标准输出和标准错误。
(这个“Howdy World”Python守护进程证明了这一点。)
“这保证了公共描述符是开放的,并且从这些描述符中的任何一个读取都返回0(文件结束),并且内核只是丢弃写入这三个描述符中的任何一个的任何东西,打开这些描述符的原因是任何库守护进程调用的函数假定它可以从标准输入读取或写入标准输出或标准错误,不会失败。或者,一些守护进程打开一个日志文件,在运行时它们将写入日志文件,并将其描述符复制到标准输出和标准错误”。 (联合国版第337页)
更改当前的工作目录
“打印机守护程序可能会更改为打印机的假脱机目录,它将完成所有工作。[…]守护进程可能已经在文件系统中的任何地方启动,如果它仍在那里,则无法卸载该文件系统。 (联合国议会第337页)
你为什么要卸载一个文件系统? 两个原因:
1.您想要分离(并能够挂载和卸载)目录,这些目录可以填充专用于操作系统的目录中的用户数据。
2.如果你从一个USB棒开始一个守护进程,你希望能够在不干扰守护进程的情况下卸载这个守护进程。
重置文件访问创建掩码。
“因此,如果守护进程创建自己的文件,则继承的文件模式创建掩码中的权限位不会影响新文件的权限位。” (联合国计划,第337页)
在后台运行。
根据定义,
“守护进程是一个在后台运行的进程,与所有终端的控制无关”。 (联合国教科文第331页)
从进程组中解除关联。
为了理解这个,你需要了解一个流程组是什么,这意味着你需要知道fork
是做什么的。
什么叉子呢
fork
是创建新进程的唯一方法(在Unix中)。 (在Linux中,也有clone
)。 理解fork
关键在于它在被调用(一次)时返回两次 :在调用过程(= parent)中有新创建的进程(= child)的进程ID,以及在子进程中一次。 “分叉时父母知道的所有描述符在分叉返回时与孩子共享。” (联合国教科文第102页)。 当进程想要执行另一个程序时,它会通过调用fork来创建一个新进程,fork创建一个自己的副本。 然后,他们中的一个(通常是孩子)称为新程序。 (联合国计划,第102页)
为什么从流程组中解除关联
关键是会议领导人可能会收购一个控制终端。 守护进程不应该这样做,它必须保持在后台。 这是通过调用fork
两次来实现的:父母分叉创造一个孩子,孩子叉子创造一个孙子。 父母和孩子被终止,但孙子仍然存在。 但因为是孙子,所以不是会议的领导,因此不能获得控制终端。 (从联合国教科文组织第12.4页第335页总结)
双叉在这里更详细的讨论,并在下面的评论。
忽略终端I / O信号。
“终端密钥生成的信号不得影响从此终端启动的守护进程”。 (联合国教科文第331页)
与控制终端分离,不要重新控制终端。
到目前为止,原因很明显:
“如果守护进程是从终端启动的,我们希望能够在稍后使用该终端执行其他任务。例如,如果我们从终端启动守护进程,注销终端,并且其他人登录该终端,我们不希望在下一个用户的终端会话期间出现任何守护程序错误消息。“ (联合国教科文第331页)
正确处理以下情况:
由System V init进程启动
守护进程通过SIGTERM信号终止
儿童产生SIGCLD信号
最后一点,当我开始在联合国人居署找到我的问题的答案时,很快就让我觉得我应该读更多的东西。 从1998年(!)开始,这是900多页(!)页面,但我相信UNP的概念和解释经受住了时间的考验,光荣。 史蒂文斯不仅非常了解他在说什么,他还明白了什么是困难的,并使其更容易理解。 这真的很少见