我在源码中发现了如下函数猫眼(简约的窗口管理器):
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);
}
}
我不明白为什么不简单地
void spawn(const Arg arg) {
if(fork() == 0) {
if(dis)
close(ConnectionNumber(dis));
setsid();
execvp((char*)arg.com[0],(char**)arg.com);
}
}
?在这里使用 double 有什么好处吗fork()
?
答案1
以下段落,引自 Stevens 和 RagoUNIX 环境中的高级编程,描述编写守护程序的六个编码规则中的两个。具体来说,他们在图 13.1 中列出的单个daemonize
函数中实现了它们,以防您想查找它。
- 调用 fork 并让父级退出。这有几件事。首先,如果守护进程作为简单的 shell 命令启动,则让父进程终止会使 shell 认为该命令已完成。其次,子进程继承父进程的进程组 ID,但获得一个新的进程 ID,因此我们可以保证子进程不是进程组领导者。这是接下来调用setsid 的先决条件。
- 调用setsid创建一个新会话。发生第 9.5 节中列出的三个步骤。进程 (a) 成为新会话的领导者,(b) 成为新进程组的领导者,并且 (c) 与其控制终端解除关联。
- 在基于 System V 的系统下,有些人建议在此时再次调用 fork,终止父进程,并继续子进程中的守护进程。这保证了守护进程不是会话领导者,从而阻止它根据 System V 规则获取控制终端(第 9.6 节)。或者,为了避免获取控制终端,请确保在打开终端设备时指定 O_NOCTTY。
在您更改的代码中,父级不会,它将在调用exit()
后继续执行;spawn()
确切的行为取决于spawn()
调用后的内容。