创建守护进程时执行双叉的原因是什么?

我想在python中创建一个守护进程。 我发现了以下问题 ,其中有一些好的资源,我目前正在跟踪,但我很好奇,为什么一个双叉是必要的。 我已经抓住了谷歌,发现大量的资源宣布一个是必要的,但不是为什么。

有人提到防止守护进程获得控制终端。 如果没有第二个叉子,它会怎么做呢? 什么影响?

看问题中引用的代码,理由是:

 # Fork a second child and exit immediately to prevent zombies. This # causes the second child process to be orphaned, making the init # process responsible for its cleanup. And, since the first child is # a session leader without a controlling terminal, it's possible for # it to acquire one by opening a terminal in the future (System V- # based systems). This second fork guarantees that the child is no # longer a session leader, preventing the daemon from ever acquiring # a controlling terminal. 

所以要确保守护进程重新启动到init(以防万一启动守护进程的进程长期存在),并且消除了守护进程重新获得控制tty的机会。 所以如果这两种情况都不适用,那么一个分支就足够了。 “ Unix网络编程 – 史蒂文斯 ”有一个很好的部分。

我试图理解双叉,并在这里偶然发现了这个问题。 经过大量的研究,我发现了这一点。 希望这将有助于为有相同问题的任何人澄清事情。

在Unix中,每个进程都属于一个属于一个会话的组。 这是等级…

会话(SID)→进程组(PGID)→进程(PID)

流程组中的第一个流程成为流程组组长,会话中的第一个流程成为会话组长。 每个会话可以有一个与之相关的TTY。 只有一个会议领导者可以控制一个TTY。 为了一个真正的守护进程(在后台运行),我们应该确保会话负责人被杀死,这样会话就不可能控制TTY。

我在我的Ubuntu上运行了Sander Marechal的python示例守护程序。 这里是我的评论的结果。

 1. `Parent` = PID: 28084, PGID: 28084, SID: 28046 2. `Fork#1` = PID: 28085, PGID: 28084, SID: 28046 3. `Decouple#1`= PID: 28085, PGID: 28085, SID: 28085 4. `Fork#2` = PID: 28086, PGID: 28085, SID: 28085 

请注意,该进程是Decouple#1之后的会话负责人,因为它是PID = SID 。 它仍然可以控制一个TTY。

请注意, Fork#2不再是会议领导者PID != SID 。 这个过程永远不能控制一个TTY。 真正的守护。

我个人发现术语叉两次混淆。 更好的习惯用法可能是fork-decouple-fork。

其他感兴趣的链接:

严格地说,双叉与作为init子的重新守护守护进程无关。 重新抚养孩子的一切必须是父母必须离开。 这只能用一个叉子完成。 另外,自己做一个双叉不会将守护进程重新init , 守护进程的父节点必须退出。 换句话说,当分派一个适当的守护进程时,父进程总是退出,这样守护进程就重新被init

那么为什么双叉? POSIX.1-2008第11.1.3节“ 控制终端 ”有答案(强调增加):

会话控制终端由会话负责人以实现定义的方式分配 。 如果会话领导者没有控制终端,并且在不使用O_NOCTTY选项(参见open())的情况下打开尚未与会话关联的终端设备文件,则实现定义终端是否成为会话的控制终端领导。 如果一个不是会话首领的进程打开了一个终端文件,或者在open()上使用了O_NOCTTY选项, 那么这个终端不应该成为调用进程的控制终端

这告诉我们,如果守护进程做了这样的事情…

 int fd = open("/dev/console", O_RDWR); 

…然后守护进程可能会获取/dev/console作为其控制终端,具体取决于守护进程是否是会话负责人,并取决于系统实现。 该程序可以保证 ,如果程序首先确保它不是会话领导者,则上述呼叫将不会获得控制终端。

通常,在启动守护进程时,会调用setsid (从调用fork之后的子进程中)将守护进程与其控制终端分离。 但是,调用setsid也意味着调用进程将成为新会话的会话负责人,这使得守护进程可能重新获得控制终端。 双叉技术确保守护进程不是会话负责人,然后保证open呼叫,如上例所示,不会导致守护进程重新获得控制终端。

双叉技术有点偏执。 如果您知道该守护程序永远不会打开终端设备文件,则可能不需要。 另外,在某些系统上,即使守护进程打开终端设备文件,也可能不需要,因为该行为是实现定义的。 然而,有一点不是实现定义的,只有会话负责人可以分配控制终端。 如果一个流程不是会议的领导者,它不能分配一个控制终端。 因此,如果你想偏执一点,并确定守护进程不会无意中获得一个控制终端,不管任何实现定义的细节,那么双叉技术是必不可少的。

采取从坏CTK :

“在Unix的某些方面,为了进入守护进程模式,你不得不在启动时执行双叉,这是因为单个分叉不能保证与控制终端分离。

根据Stephens和Rago的“Unix环境下的高级编程”,第二个分支更多的是建议,这样做是为了保证守护进程不会在基于System V的系统上获得控制终端。

一个原因是,父进程可以立即wait_pid()的孩子,然后忘掉它。 当大孩子死的时候,它的父母就是init,并且会等待(),并把它从僵尸状态中解救出来。

其结果是父进程不需要知道分叉的子进程,也可以从库中分叉长时间运行的进程。

如果守护进程成功,守护进程()调用将具有父调用_exit()。 原来的动机可能是让孩子在守护孩子时做一些额外的工作。

也可能是基于错误的信念,即为了确保守护进程没有父进程并被重新初始化,这是必要的 – 但是一旦父进程死在单个fork中,这种情况就会发生。

所以我想这一切都归结为传统 – 只要父母在短期内死亡,一个分叉就足够了。

一个体面的讨论似乎在http://www.developerweb.net/forum/showthread.php?t=3025

从那里引用mlampkin:

…把setsid()调用看作是处理SVr4冗余的“新事物”(与终端分离)和[second] fork()调用之后的…