双叉() - 为什么?

Ale*_*lex 17 c fork daemon

我在catwm(一个简约的窗口管理器)的源代码中发现了以下函数:

void spawn(const Arg arg) {
    if(fork() == 0) {
        if(fork() == 0) {
            if(dis)
                close(ConnectionNumber(dis));

            setsid();
            execvp((char*)arg.com[0],(char**)arg.com);
        }
        exit(0);
    }
}
Run Code Online (Sandbox Code Playgroud)

在 github 上查看

我不明白为什么不简单地

void spawn(const Arg arg) {
    if(fork() == 0) {
        if(dis)
            close(ConnectionNumber(dis));

        setsid();
        execvp((char*)arg.com[0],(char**)arg.com);
    }
}
Run Code Online (Sandbox Code Playgroud)

?在这里使用 double 有什么好处吗fork()

don*_*man 21

以下段落引用了 Stevens 和 Rago 的UNIX 环境中的高级编程,描述了编写守护程序的六种编码规则中的两条。具体来说,他们在图 13.1 中列出的单个daemonize函数中实现了它们,以防您想查找它。

\n
\n
    \n
  1. 调用 fork 并让父进程退出。这有几件事。首先,如果守护进程作为简单的 shell 命令启动,则让父进程终止会使 shell 认为该命令已完成。其次,子进程继承父进程的进程组ID,但获得一个新的进程ID,因此我们\xe2\x80\x99保证子进程不是进程组领导者。这是接下来调用setsid 的先决条件。
  2. \n
  3. 调用setsid创建一个新会话。发生第 9.5 节中列出的三个步骤。进程 (a) 成为新会话的领导者,(b) 成为新进程组的领导者,(c) 与其控制终端解除关联。\n
      \n
    • 在基于 System V\xe2\x80\x93 的系统下,有人建议此时再次调用 fork,终止父进程,并继续子进程中的守护进程。这保证了守护进程不是会话领导者,从而阻止它根据 System V 规则获取控制终端(第 9.6 节)。或者,为了避免获取控制终端,请确保在打开终端设备时指定 O_NOCTTY。
    • \n
    \n
  4. \n
\n
\n

在您更改的代码中,父级不会,它将在调用exit()后继续执行;spawn()确切的行为取决于调用后的内容spawn()

\n

  • @MarcusMüller,这让我想起了一些例子,我听说 SysV 和基于 BSD 的系统之间存在一些内核级行为差异(无论如何)。就这些而言,Linux 仍将与 SysV 一如既往,因为类似的内核行为(对用户空间可见的行为)不会发生改变。即使大多数现代 Linux 放弃了 SysV 风格的 init 系统,转而在用户空间中使用 systemd。 (2认同)