# 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.
双叉技术有点偏执 . 有可能如果您知道守护程序永远不会打开终端设备文件,则没有必要 . 此外,在某些系统上,即使守护程序确实打开了终端设备文件,也可能没有必要,因为该行为是实现定义的 . 但是,没有实现定义的一件事是只有会话负责人才能分配控制终端 . If a process isn't a session leader, it can't allocate a controlling terminal. 因此,如果你想要偏执并确定守护进程不会无意中获得一个控制终端,不管任何实现定义的细节,那么双叉技术是必不可少的 .
9 回答
以这种方式理解可能更容易:
第一个fork和setsid将创建一个新会话(但进程ID ==会话ID) .
第二个分支确保进程ID!=会话ID .
查看问题中引用的代码,理由是:
因此,确保将守护进程重新设置为init(以防止守护进程长时间运行),并消除守护进程重新获取控制tty的任何可能性 . 因此,如果这两种情况都不适用,那么一个分叉就足够了 . “Unix Network Programming - Stevens”对此有一个很好的部分 .
我试图理解双叉,并在这里偶然发现了这个问题 . 经过大量的研究,这才是我想到的 . 希望它能帮助那些有同样问题的人更好地澄清事情 .
在Unix中,每个进程都属于一个组,而该组又属于一个会话 . 这是层次结构......
会话(SID)→进程组(PGID)→进程(PID)
流程组中的第一个流程成为流程组负责人,会话中的第一个流程成为会话负责人 . 每个会话都可以有一个与之关联的TTY . 只有会话负责人才能控制TTY . 对于一个真正守护进程(在后台运行)的进程,我们应该确保会话负责人被杀死,这样会话就不可能控制TTY .
我在我的Ubuntu上从this site运行了Sander Marechal的python示例守护程序 . 以下是我的评论结果 .
请注意,该过程是
Decouple#1
之后的会话负责人,因为它是PID = SID
. 它仍然可以控制TTY .请注意,
Fork#2
不再是会话领导者PID != SID
. 此过程永远无法控制TTY . 真正守护进来 .我个人觉得术语分叉两次令人困惑 . 一个更好的习语可能是fork-decouple-fork .
其他感兴趣的链接:
严格来说,双叉与将守护进程重新作为
init
的子进程无关 . 重新生成孩子所需要的只是父母必须退出 . 这可以只使用一个fork来完成 . 另外,单独执行双分叉并不会将守护进程重新为父进程init
;守护进程的父进程必须退出 . 换句话说,在分配正确的守护进程时父进程总是退出,以便将守护进程重新设置为init
的父级 .那么为什么双叉呢? POSIX.1-2008第11.1.3节“The Controlling Terminal”有答案(重点补充):
这告诉我们,如果一个守护进程做了这样的事情......
...然后守护进程可能会获取
/dev/console
作为其控制终端,具体取决于守护进程是否为会话负责人,具体取决于系统实现 . 如果程序首先确保它不是会话领导者,则程序可以保证上述呼叫不会获得控制终端 .通常,在启动守护程序时,会调用
setsid
(在调用fork
之后从子进程调用)以将守护程序与其控制终端分离 . 但是,调用setsid
也意味着调用进程将成为新会话的会话负责人,这使守护进程可能重新获取控制终端 . 双叉技术确保守护进程不是会话负责人,然后保证对open
的调用(如上例所示)不会导致守护进程重新获取控制终端 .双叉技术有点偏执 . 有可能如果您知道守护程序永远不会打开终端设备文件,则没有必要 . 此外,在某些系统上,即使守护程序确实打开了终端设备文件,也可能没有必要,因为该行为是实现定义的 . 但是,没有实现定义的一件事是只有会话负责人才能分配控制终端 . If a process isn't a session leader, it can't allocate a controlling terminal. 因此,如果你想要偏执并确定守护进程不会无意中获得一个控制终端,不管任何实现定义的细节,那么双叉技术是必不可少的 .
取自Bad CTK:
“在Unix的某些版本中,你被迫在启动时进行双分叉,以便进入守护进程模式 . 这是因为单个分叉不能保证从控制终端分离 . ”
根据Stephens和Rago的“Unix环境中的高级编程”,第二个分支是一个推荐,它是为了保证守护进程不在基于System V的系统上获得控制终端 .
一个原因是父进程可以立即wait_pid()为子进程,然后忘记它 . 当那个盛大的孩子去世时,它的父母是init,它将等待()为它 - 并将其从僵尸状态中取出 .
结果是父进程不需要知道分叉的子进程,它还可以从libs等分叉长时间运行的进程 .
如果成功,则守护进程()调用具有父调用_exit() . 最初的动机可能是允许父母在孩子守护进行时做一些额外的工作 .
它也可能基于一种错误的信念,即为了确保守护进程没有父进程并且被重新设置为init是必要的 - 但是一旦父进程在单fork情况下死亡,这种情况就会发生 .
所以我想这一切最终归结为传统 - 只要父母在短期内死亡,单个分叉就足够了 .
对它的体面讨论似乎是在http://www.developerweb.net/forum/showthread.php?t=3025
从那里引用mlampkin: