You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UNIX环境下守护进程示例中pause()函数执行疑问

为什么daemonize()后main()里的pause()还能执行?

先整理修正后的代码片段(修复了语法错误,比如转义的&改为&,拼写错误chile改为child):

#include <sys/resource.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

void daemonize(const char* cmd) {
 int i,fd0,fd1,fd2;
 pid_t pid;
 struct rlimit r1;
 struct sigaction sa;
 //clear all file masks
 umask(0);
 //get max number of file descriptors
 if(getrlimit(RLIMIT_NOFILE,&r1) < 0) {
  perror("error getting file descriptor size");
  return;
 }
 //become a session leader
 if((pid = fork()) < 0) {
  perror("error forking");
  return;
 } else if(pid == 0) {
  setsid();
 } else {
  exit(0); //parent exits
 }
 sa.sa_handler = SIG_IGN;
 sigemptyset(&sa.sa_mask);
 sa.sa_flags = 0;
 if(sigaction(SIGHUP,&sa,NULL) < 0) {
  return;
 }
 if((pid = fork()) < 0) {
  return;
 } else if(pid != 0) {
  exit(0); //parent
 }
 //child continues
 syslog(LOG_ERR,"child continuing with pid : %d",getpid());
 //change the working directory
 if(chdir("/") < 0) {
  return;
 }
 if(r1.rlim_max == RLIM_INFINITY)
  r1.rlim_max = 1024;
 for(i=0;i<r1.rlim_max;i++)
  close(i);
 //attach the file descriptors to /dev/null
 fd0 = open("/dev/null",O_RDWR);
 fd1 = dup(0);
 fd2 = dup(0);
 //initialize the log file
 openlog(cmd, LOG_CONS,LOG_DAEMON);
 if(fd0!=0 || fd1!=1 || fd2!=2) {
  syslog(LOG_ERR,"unexpected file descriptors %d %d %d\n",fd0,fd1,fd2);
  exit(1);
 }
}
int main() {
 daemonize("date");
 pause(); //how is this working???
}

你的困惑非常合理——毕竟第一次fork()后,调用daemonize()的初始进程确实退出了,但pause()依然能执行,核心原因藏在双fork()的守护进程模型里,我们一步步拆解整个执行流程:

  1. 初始启动:进程A执行main()
    程序启动后,进程A开始执行main(),第一个动作就是调用daemonize("date")。

  2. 第一次fork():创建进程B,进程A退出
    在daemonize()里的第一次fork():

    • 进程A(父进程)拿到子进程B的ID,进入else分支执行exit(0)直接退出。
    • 进程B(子进程)拿到pid=0,执行setsid()成为新会话的领导者,继续往下走。
  3. 第二次fork():创建进程C,进程B退出
    进程B执行到第二次fork():

    • 进程B(父进程)拿到子进程C的ID,进入else if(pid !=0)分支执行exit(0)退出。
    • 进程C(子进程)拿到pid=0,继续执行daemonize()剩下的逻辑:切换工作目录到/、关闭所有文件描述符、重定向标准输入输出到/dev/null、初始化syslog。
  4. 进程C回到main()执行pause()
    当daemonize()执行完毕后,进程C会从函数调用的位置返回——也就是回到main()里daemonize("date")之后的代码行,自然就执行了pause()。

简单来说:执行pause()的不是原来的进程A,而是第二次fork()出来的进程C。进程C继承了进程B的程序执行上下文,所以当daemonize()返回后,它会继续执行原程序中daemonize()之后的逻辑。

这也是双fork()模型的设计目的:第一次fork()脱离终端,第二次fork()确保进程不会意外获得终端(会话领导者可以打开终端,而第二次fork()后的进程不是会话领导者),同时让最终的守护进程成为孤儿进程(被init/systemd收养),持续运行后续逻辑。

内容的提问来源于stack exchange,提问作者InsaneCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:45:53