fork()之后杀死父进程只留下子进程运行的目的是什么?

3
我正在阅读Nginx开源代码,并且想知道为什么有些人会杀死父进程并让子进程处理程序的其余部分?为什么不仅让父进程处理呢?我非常感谢您的帮助。
我使用Eclipse CDT调试程序,但这会导致我的调试陷入僵局,因为它继续调试父进程而不是实际处理程序其余部分的子进程。
以下是代码片段:
ngx_int_t
ngx_daemon(ngx_log_t *log)
{
    int  fd;
    switch (fork()) {
    case -1:
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
        return NGX_ERROR;

    case 0:

        break;

    default:
    exit(0);

    }
/* Do stuff*/
}

编辑:我知道这个过程是为了将程序变成守护进程,但我仍然想知道为什么我们要在一开始就这样做呢?


7
这是一种将守护进程与父进程的执行环境分离的方法。同时,这也是一种让程序在后台继续运行,而用户可以分别继续工作的方式,尽管在今天的shell中有后台作业功能,但这种分离仍然有其必要性,只留下第一个原因。 - Some programmer dude
2
如果你为每个查询创建一个新进程,当你改变配置时,你想让这些新进程完成它们的工作并杀死父进程,然后启动一个新的进程使用新的配置。 - Ôrel
@Someprogrammerdude,感谢您的回复。不过,您介意解释一下为什么我们应该“将守护进程与父进程的执行环境分离”吗?我已经阅读了有关Linux中守护进程的资料,但仍然没有完全理解。非常感谢 :) - Toan Tran
@Ôrel 哦,我明白了,在 Nginx 的上下文中这很有道理。 - Toan Tran
1个回答

2

守护进程化程序的主要部分在于将其与控制终端断开连接。

为此,您需要调用setsid()

setsid()要求调用者不是进程组长(直接从shell中运行带作业控制的进程)。

如果您进行fork并继续在子进程中执行,则子进程肯定不会成为进程组长,这使得setsid()调用成功。

之后,您应该重复fork+exit过程,以确保继续的孙子进程也不是会话领导者,从而确保它没有控制终端(会话领导者(由setsid()设置)具有获取控制终端的能力,甚至可能意外地通过打开终端文件来获取控制终端)。


我认为守护进程程序的目的是使其在后台持续运行并等待请求。为了实现这一点,我们不使用代码中的循环,而是通过fork()和在每个循环中exit()父进程来将其守护化,这样理解正确吗? - Toan Tran
2
守护进程化一个程序主要就是切断它与控制终端的连接。该守护进程所执行的操作(无论它只是将“Hello World”打印到日志并退出,还是进入服务器循环)对于其守护进程状态来说都不重要(没有控制终端加上一些其他细节)。请参阅https://dev59.com/mXA75IYBdhLWcg3wy8fr - Petr Skocik
嗨@PSkocik,我现在理解了程序守护进程的过程。然而,我仍然困惑于一开始守护化程序的目的。根据您的回复,我认为我的第一个假设是错误的。那么我们为什么需要“将程序与其控制终端断开连接”呢? - Toan Tran
2
@ToanTran 这是一个非常好的问题。据我所知,这样做是为了防止进程在原终端关闭时收到 SIGHUP 信号。当然也可以通过忽略 SIGHUP 信号来实现,但是守护进程通常会重新分配该信号以重新加载配置。此外,控制终端会被继承并可通过 /dev/tty 访问。守护化能够避免这种情况的发生。 - Petr Skocik
嗨@PSkocik,只是想澄清一下: -即使我们忽略了它(因为我们希望程序在后台继续运行),守护进程仍会收到SIGHUP信号。=======> 我们需要做的是:1/将当前进程变成守护进程以防止这种情况发生,2/用其子进程替换当前进程。然后我们将对子进程执行相同的操作,下一步用孙子进程替换它,以此类推。 我现在理解得对吗? - Toan Tran

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