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()的守护进程模型里,我们一步步拆解整个执行流程:
初始启动:进程A执行
main()
程序启动后,进程A开始执行main(),第一个动作就是调用daemonize("date")。第一次
fork():创建进程B,进程A退出
在daemonize()里的第一次fork():- 进程A(父进程)拿到子进程B的ID,进入
else分支执行exit(0)直接退出。 - 进程B(子进程)拿到
pid=0,执行setsid()成为新会话的领导者,继续往下走。
- 进程A(父进程)拿到子进程B的ID,进入
第二次
fork():创建进程C,进程B退出
进程B执行到第二次fork():- 进程B(父进程)拿到子进程C的ID,进入
else if(pid !=0)分支执行exit(0)退出。 - 进程C(子进程)拿到
pid=0,继续执行daemonize()剩下的逻辑:切换工作目录到/、关闭所有文件描述符、重定向标准输入输出到/dev/null、初始化syslog。
- 进程B(父进程)拿到子进程C的ID,进入
进程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
相关产品推荐
相关产品推荐

