1.函数执行过程
server.c----->main------->rc = server_main(srv, argc, argv);---->
#ifdef HAVE_FORK
/* network is up, let's daemonize ourself */
if (0 == srv->srvconf.dont_daemonize && 0 == graceful_restart) {
parent_pipe_fd = daemonize();
}
#endif
2、如何转化为守护进程daemonize()代码
#ifdef HAVE_FORK
static int daemonize(void) {
int pipefd[2];
pid_t pid;
/* 下面用于屏蔽一些有关控制终端操作的信号,防止守护进程没有正常运作之前控制终端受到干扰退出或挂起 */
#ifdef SIGTTOU
signal(SIGTTOU, SIG_IGN);//忽略后台进程写控制终端信号
#endif
#ifdef SIGTTIN
signal(SIGTTIN, SIG_IGN);//忽略后台进程读控制终端信号
#endif
#ifdef SIGTSTP
signal(SIGTSTP, SIG_IGN);//忽略终端挂起
#endif
if (pipe(pipefd) < 0) exit(-1);
/* 下面开始从普通进程转换为守护进程 * 目标1:后台运行。 * 做法:脱离控制终端->调用fork之后终止父进程,子进程被init收养,此步达到后台运行的目标。 */
if (0 > (pid = fork())) exit(-1);
if (0 < pid) {
char buf;
ssize_t bytes;
close(pipefd[1]);
/* parent waits for grandchild to be ready */
do {
bytes = read(pipefd[0], &buf, sizeof(buf));
} while (bytes < 0 && EINTR == errno);
close(pipefd[0]);
if (bytes <= 0) {
/* closed fd (without writing) == failure in grandchild */
fputs("daemonized server failed to start; check error log for details\n", stderr);
exit(-1);
}
exit(0);
}
close(pipefd[0]);
/*,目标2:脱离控制终端,登陆会话和进程组。 * 做法:使用setsid创建新会话,成为新会话的首进程,则与原来的 * 登陆会话和进程组自动脱离,从而脱离控制终端。 * (上一步的fork保证了子进程不可能是一个会话的首进程,这是调用setsid的必要条件) */
if (-1 == setsid()) exit(0);
/* 上面已经完成了大部分工作,但是有的系统上,当会话首进程打开 * 一个尚未与任何会话相关联的终端设备时,该设备自动作为控制 * 终端分配给该会话。 * 为避免该情况,我们再次fork进程,于是新进程不再是会话首进程。 * 会话首进程退出时可能会给所有会话内的进程发送SIGHUP,而该 * 信号默认是结束进程,故需要忽略该信号来防止孙子进程意外结束。 */
signal(SIGHUP, SIG_IGN);
if (0 != fork()) exit(0);
/* 最后目标:改变工作目录到根目录。 * 原因:进程活动时,其工作目录所在的文件系统不能卸下。 */
if (0 != chdir("/")) exit(0);
fdevent_setfd_cloexec(pipefd[1]);
return pipefd[1];
}
#endif
3、如何转化为守护进程daemonize()流程图
参考文档:Lighttpd源码分析_高群凯
下载地址:https://download.csdn.net/download/caofengtao1314/10576306
参考网站:https://blog.csdn.net/jiange_zh/article/details/50483099