Python 创建守护进程时执行双分叉的原因是什么?
我正在尝试用python创建一个守护进程。我已经找到了,它有一些很好的资源,我现在正在关注,但是我很好奇为什么需要双叉。我在谷歌搜索了一下,找到了大量的资源,宣称这是必要的,但并不是为什么 有人提到这是为了防止守护进程获取控制终端。如果没有第二个叉子,它将如何做到这一点?影响是什么?来自: “在某些Unix版本上,为了进入守护程序模式,您必须在启动时执行双分叉。这是因为单分叉不能保证从控制终端分离。”如果成功,守护程序()调用将具有父调用_exit()。最初的动机可能是允许家长在孩子进行后台监控时做一些额外的工作 这也可能是基于一种错误的信念,即为了确保守护进程没有父进程并且重新租给init,这是必要的——但是,一旦父进程在单fork情况下死亡,这种情况无论如何都会发生Python 创建守护进程时执行双分叉的原因是什么?,python,unix,daemon,Python,Unix,Daemon,我正在尝试用python创建一个守护进程。我已经找到了,它有一些很好的资源,我现在正在关注,但是我很好奇为什么需要双叉。我在谷歌搜索了一下,找到了大量的资源,宣称这是必要的,但并不是为什么 有人提到这是为了防止守护进程获取控制终端。如果没有第二个叉子,它将如何做到这一点?影响是什么?来自: “在某些Unix版本上,为了进入守护程序模式,您必须在启动时执行双分叉。这是因为单分叉不能保证从控制终端分离。”如果成功,守护程序()调用将具有父调用_exit()。最初的动机可能是允许家长在孩子进行后台监控
因此,我想这一切最终都可以归结为传统——只要父代在短时间内死去,一个分支就足够了。根据Stephens和Rago的“Unix环境中的高级编程”,第二个分支更像是一个建议,这样做是为了保证守护进程不会在基于SystemV的系统上获取控制终端 看看问题中引用的代码,理由是: 叉第二个孩子,并立即退出,以防止僵尸。这 导致第二个子进程孤立,使init 负责其清理的进程。而且,因为第一个孩子是 一个没有控制终端的会话领导者,可以 它可以通过在将来打开一个终端来获得一个(系统V- 基于数据库的系统)。第二个fork保证子对象不是 会话领导者的时间更长,阻止守护进程获取 控制终端
因此,这是为了确保守护进程被重新设置为init的父级(以防启动守护进程的进程存在很长时间),并消除守护进程重新获取控制tty的任何机会。因此,如果这两种情况都不适用,那么一个叉子就足够了。“”在这方面有一个很好的章节。原因之一是父进程可以立即等待子进程,然后忘记它。当孙子死后,它的父代是init,它将等待()并将它从僵尸状态中解救出来
结果是,父进程不需要知道分叉的子进程,它还可以从libs等中分叉长时间运行的进程 引用mlampkin的话: …将setsid()调用视为“新”的处理方式(与终端解除关联),将其之后的[second]fork()调用视为处理SVr4的冗余
我试图理解双叉,在这里偶然发现了这个问题。经过大量研究,我发现了这一点。希望它能帮助有同样问题的人更好地澄清问题 在Unix中,每个进程都属于一个组,而该组又属于一个会话。这是层次结构 会话(SID)→ 进程组(PGID)→ 过程(PID) 流程组中的第一个流程成为流程组组长,会话中的第一个流程成为会话组长。每个会话都可以有一个与之关联的TTY。只有会话负责人才能控制TTY。为了使进程真正被守护(在后台运行),我们应该确保会话负责人被杀死,这样会话就不可能控制TTY 我在Ubuntu上运行了Sander Marechal的python示例守护程序。以下是我的评论结果
1`父项`=PID:28084,PGID:28084,SID:28046
2. `Fork#1`=PID:28085,PGID:28084,SID:28046
3. `解耦#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
其他感兴趣的链接:
- Unix进程-
init
的子进程重新设置为父进程无关。重新设置子对象的父对象所需的只是父对象必须退出。这只能用一个叉子来完成。另外,单独执行双fork不会将守护进程重新设置为init的父进程;守护进程的父进程必须退出。换言之,在派生一个适当的守护进程时,父进程总是退出,这样守护进程就被重新设置为init
的父进程
那为什么要用双叉呢?第11.1.3节“”,给出了答案(添加了强调):
会话的控制终端由会话负责人以实现定义的方式分配。如果会话引导者没有控制终端,并且在不使用O_NOCTTY
选项(请参见open()
)的情况下打开尚未与会话关联的终端设备文件,则终端是否成为会话引导者的控制终端由实现定义。如果不是会话引导的进程打开终端文件,或者open()
上使用了O_NOCTTY
选项,则
int fd = open("/dev/console", O_RDWR);