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

2024-05-13 11:33:11 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图用python创建一个守护进程。我找到了following question,其中有一些很好的资源,我目前正在跟踪,但我很好奇为什么双叉是必要的。我在google上搜索了一下,找到了大量的资源来声明一个是必要的,但不是为什么。

有人提到它是为了防止守护进程获取控制终端。如果没有第二个叉子怎么办?有什么影响?


Tags: 终端声明进程google叉子资源followingquestion
3条回答

严格地说,double fork与将守护进程重新作为init的子进程无关。要使子对象重新成为父对象,只需要父对象必须退出。这只能用一把叉子。此外,单独执行double fork不会使守护进程成为init的父进程;守护进程的父进程必须退出。换言之,当派生一个合适的守护进程时,父进程始终存在,以便守护进程重新成为init的父进程。

那为什么要用双叉呢?POSIX.1-2008第11.1.3节,“The Controlling Terminal”给出了答案(添加了重点):

The controlling terminal for a session is allocated by the session leader in an implementation-defined manner. If a session leader has no controlling terminal, and opens a terminal device file that is not already associated with a session without using the O_NOCTTY option (see open()), it is implementation-defined whether the terminal becomes the controlling terminal of the session leader. If a process which is not a session leader opens a terminal file, or the O_NOCTTY option is used on open(), then that terminal shall not become the controlling terminal of the calling process.

这告诉我们,如果一个守护进程执行类似的操作。。。

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

。。。然后,守护进程可能会获取/dev/console作为其控制终端,这取决于守护进程是否是会话领导者,也取决于系统实现。如果程序首先确保它不是会话领导者,则程序可以保证上述调用不会获得控制终端。

通常,在启动守护进程时,会调用setsid(在调用fork之后从子进程中)来将守护进程与其控制终端分离。然而,调用setsid也意味着调用进程将成为新会话的会话领导者,这使得守护进程有可能重新获取控制终端。double-fork技术确保守护进程不是会话领导者,从而保证对open的调用(如上面的示例所示)不会导致守护进程重新获取控制终端。

双叉技术有点偏执。如果您知道守护进程永远不会打开终端设备文件,则可能不需要这样做。此外,在某些系统上,即使后台程序确实打开了终端设备文件,也可能不需要,因为该行为是由实现定义的。但是,有一件事不是实现定义的,那就是只有会话负责人可以分配控制终端。如果进程不是会话引导者,则它无法分配控制终端。因此,如果您想变得多疑,并确保守护进程不会无意中获取控制终端,而不管实现定义了什么细节,那么双分叉技术是必不可少的。

我正试着去理解双叉,无意中发现了这个问题。经过大量的研究,我发现了这一点。希望这将有助于澄清更好的任何人谁有相同的问题。

在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。

其他感兴趣的链接:

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

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.

因此,这是为了确保守护进程重新成为in it的父进程(以防启动守护进程的进程是长寿命的),并消除守护进程重新获取控制tty的任何机会。因此,如果这两种情况都不适用,那么一个叉子就足够了。”Unix Network Programming - Stevens“对此有一个很好的部分。

相关问题 更多 >