为什么守护进程要进行分叉?

18

我知道一些(或者全部?)守护进程在启动时会进行分叉。我认为这是为了将子进程以较低的特权用户身份运行,特别是如果守护进程是像 HTTP 服务器这样的东西。

不过这样做是必要的吗?一个进程不能在不分叉子进程的情况下启动并降低其权限吗?这是否“强制”需要进行分叉操作,还是有其他特殊理由(除了运行多个子工作进程)?

我对此很新,并且希望得到所有可以得到的帮助。

4个回答

20

我认为守护进程会出于以下几个原因而进行分叉:

  1. 一个原因是将进程与启动它的任何shell分离。某些shell(例如Bash)在退出时会杀死子进程,除非采取特殊的针对该shell的预防措施。分叉是一种通用的逃避方法。

  2. 另一个原因是报告守护进程已成功启动

    假设不进行分叉。你如何知道守护进程已成功启动?你不能只读取和解析守护进程的输出,因为守护进程管理程序应该以通用的方式执行此操作。因此,唯一的方法是获得程序的返回代码。

    事实上,如果一个守护进程无法启动(例如找不到配置文件),你会立刻知道。但是,如果守护进程已经成功启动,则可能永远不会返回!所以你的守护进程管理程序无法知道守护进程是否仍在尝试启动,或者已经启动并正在工作。分叉将解决这个问题,并且如果分叉成功,分叉程序将返回成功。

  3. 至于权限,在execve之后降低权限远不如在execve之前这样做安全。这是分叉很方便的另一个原因。


1
晚来了一点,请让我在systemd的上下文中回答你的问题。对于第一点:systemd启动进程,但不关闭它。对于第二点:如果进程退出,则systemd将认为它处于失败状态,而如果它保持运行,则一切正常。鉴于这两个要点,对于在systemd下运行的编程守护程序,仍然建议使用forking吗? - mur

9
守护进程必须进行两次fork,因为它们必须独立于其他进程,也就是说,没有办法通过杀死另一个进程来杀死守护进程,并且必须在后台运行,也就是不附加到终端上。
在启动时,守护进程进行一次fork并且父进程退出。这使得派生出的进程成为init的子进程,基本上它独立于其他进程。
第二次fork后,子进程不再是进程组长,并且与终端分离。
还有其他良好的实践方法可供使用,阅读简单守护进程的源代码可能会很有启发性。

2
有没有推荐一些简单易懂的守护进程代码?我敢打赌,肯定也有一些需要避免的。 - Jonathan Day
这个答案肯定被接受了,因为它解释了双重分叉,这是关于守护进程的一个非常重要的概念。 - th3an0maly

3

我认为这样做是为了使守护进程完全不依附于任何其他进程(如shell或类似进程)。通过fork并退出父进程,孤儿进程将被系统init进程“收养”。


3
为了支持daramarak所说的,来自维基百科文章
在Unix环境中,守护进程的父进程通常是init进程(PID=1),但不总是如此。进程通常通过fork一个子进程,然后让其父进程立即退出,从而使init接管该子进程而成为守护进程。这是一个比较简化的过程,因为通常还会执行其他操作,例如将守护进程与任何控制tty分离。某些UNIX系统存在用于此目的的方便例程,例如daemon(3)。
基本上,该进程需要与其他进程和终端等分离。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接