我正在尝试在python中创建一个守护进程。我找到了following question,它有一些我目前正在关注的好资源,但我很好奇为什么需要双叉。我抓住谷歌,发现有足够的资源宣称一个是必要的,但不是为什么。
有人提到它是为了防止守护进程获取控制终端。没有第二个叉子怎么做呢?有什么影响?
答案 0 :(得分:156)
我试图理解双叉,并在这里偶然发现了这个问题。经过大量的研究,这才是我想到的。希望它能帮助那些有同样问题的人更好地澄清事情。
在Unix中,每个进程都属于一个组,而该组又属于一个会话。这是层次结构......
会话(SID)→进程组(PGID)→进程(PID)
流程组中的第一个流程成为流程组负责人,会话中的第一个流程成为会话负责人。每个会话都可以有一个与之关联的TTY。只有会话负责人才能控制TTY。对于一个真正守护进程(在后台运行)的进程,我们应该确保会话负责人被杀死,这样会话就不可能控制TTY。
我在我的Ubuntu上从this site运行了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。
其他感兴趣的链接:
答案 1 :(得分:102)
查看问题中引用的代码,理由是:
分叉第二个孩子并立即退出以防止僵尸。这个 导致第二个子进程成为孤儿,进行初始化 负责清理的过程。而且,因为第一个孩子是 没有控制终端的会话负责人,有可能 通过在未来开设终端获得一个(系统V- 基于系统)。这第二个叉子保证孩子不行 更长的会话负责人,阻止守护进程获取 控制终端。
因此,确保将守护进程重新设置为init(以防万一启动守护进程的过程很长时间),并删除守护进程重新获取控制tty的任何机会。因此,如果这两种情况都不适用,那么一个分叉就足够了。 “Unix Network Programming - Stevens”对此有一个很好的部分。
答案 2 :(得分:98)
严格地说,双叉与将守护进程重新作为init
的子进程无关。重新生成孩子所需要的只是父母必须退出。这可以只使用一个fork来完成。此外,单独执行双分叉不会将守护进程重新父级转移到init
;守护进程的父必须退出。换句话说,在分配正确的守护进程时父进程总是退出,以便将守护进程重新设置为init
。
那为什么双叉呢? POSIX.1-2008第11.1.3节“The Controlling Terminal”有答案(强调补充):
会话的控制终端由会话负责人以实现定义的方式分配。如果会话负责人没有控制终端,并且在不使用
O_NOCTTY
选项的情况下打开尚未与会话关联的终端设备文件(请参阅open()
),则终端变为实现定义会话负责人的控制终端。如果非会话负责人的流程打开终端文件,或O_NOCTTY
上使用open()
选项,则该终端不会成为控制终端调用过程。
这告诉我们如果一个守护进程做了这样的事情......
int fd = open("/dev/console", O_RDWR);
...然后守护进程可能获取/dev/console
作为其控制终端,具体取决于守护程序进程是否为会话负责人,具体取决于系统实现。如果程序首先确保它不是会话领导者,则程序可以保证以上呼叫不会获得控制终端。
通常,在启动守护程序时,会调用setsid
(在调用fork
之后从子进程调用)以将守护程序与其控制终端分离。但是,调用setsid
也意味着调用进程将成为新会话的会话负责人,这使守护进程可能重新获取控制终端。双叉技术确保守护进程不是会话负责人,然后保证对open
的调用(如上例所示)不会导致守护进程重新获取控制终端。
双叉技术有点偏执。如果您知道守护程序永远不会打开终端设备文件,则可能没有必要。此外,在某些系统上,即使守护程序确实打开了终端设备文件,也可能没有必要,因为该行为是实现定义的。但是,没有实现定义的一件事是只有会话负责人才能分配控制终端。 如果进程不是会话负责人,则无法分配控制终端。因此,如果您想要偏执并确定守护程序进程无法无意中获取控制终端,则无论任何实现定义的细节,然后双叉技术是必不可少的。
答案 3 :(得分:11)
取自Bad CTK:
“在某些版本的Unix上,你被迫在启动时进行双分叉,以便进入守护进程模式。这是因为单个分叉不能保证从控制终端分离。”
答案 4 :(得分:7)
根据Stephens和Rago的“Unix环境中的高级编程”,第二个分支是一个推荐,它是为了保证守护进程不在基于System V的系统上获得控制终端。
答案 5 :(得分:3)
一个原因是父进程可以立即为子进程wait_pid(),然后忘记它。当那个盛大的孩子去世时,它的父母是init,它将等待()为它 - 并将其从僵尸状态中取出。
结果是父进程不需要知道分叉的子进程,它也可以从libs等分叉长时间运行的进程。
答案 6 :(得分:2)
如果daemon()调用成功,则调用父调用_exit()。最初的动机可能是允许父母在孩子守护的时候做一些额外的工作。
它也可能基于一种错误的信念,即为了确保守护进程没有父进程并且被重新设置为init是必要的 - 但是一旦父进程在单叉情况下死亡,这种情况就会发生。
所以我认为这一切最终归结为传统 - 只要父母在短期内死亡,单个分叉就足够了。
答案 7 :(得分:2)
对它的体面讨论似乎在http://www.developerweb.net/forum/showthread.php?t=3025
从那里引用mlampkin:
...想想setsid()调用作为“新的”做事情的方式(与终端分离)和[second] fork()调用之后作为冗余来处理SVr4 ... < / p>
答案 8 :(得分:-1)
通过这种方式可能更容易理解: