- 示例出处
- 守护进程概念
- 创建步骤
- 存在即合理
- nginx中的daemon
- 缝缝补补又一套
这个守护进程的示例是我从nginx的源码当中剥离出来的。
nginx的源码是比muduo要复杂些哈,muduo跟我以前写过的服务端项目有很多共通之处,就相当于是剥离了业务代码的网络层框架,所以看起来也比较亲切。这个nginx就感觉稍微有点陌生哈。
所以我决定一块一块能用的我先剥出来。
守护进程概念
守护进程是一个在后台运行并且不受任何终端控制的进程。
守护进程没有控制终端,因此当某些情况发生时,不管是一般的报告性信息,还是需由管理员处理的紧急信息,都需要以某种方式输出。
创建步骤1、创建“孤儿进程”,形式上与终端脱离;
2、让这个“孤儿进程”成为新会话的组长,防止进程被原会话中其他进程干扰;
3、改变工作目录并重设文件创建掩码;
4、关闭文件描述符,因为没必要开着了。
以上2/3/4都是在消除父进程的印记。
还看到有些人说,应该先屏蔽些信号,省的出师未捷身先死哈。想屏蔽就屏蔽呗,也不差写那四五行代码。
存在即合理1)终端要干别的事儿了,后边凉快的地方呆着去。
2)避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。逍遥吧!
接下来我们看看nginx里面的守护进程实现哈,当然,我们要带着辩证的角度来看,要是看到它省略了几句啥,咱可以自己补上嘛,有试无害嘛。
nginx中的daemonngx_int_t ngx_daemon(ngx_log_t *log)
{
int fd;
//要成为守护进程,首先要成为孤儿,进孤儿院
switch (fork()) {
case -1:
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
return NGX_ERROR;
case 0:
break;
default:
exit(0);
}
if (setsid() == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");
return NGX_ERROR;
}
umask(0);
fd = open("/dev/null", O_RDWR); //难怪在好多地方有看到这么个写法,当时就不知道是干嘛的
if (fd == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"open("/dev/null") failed");
return NGX_ERROR;
}
if (dup2(fd, STDIN_FILENO) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
return NGX_ERROR;
}
if (dup2(fd, STDOUT_FILENO) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
return NGX_ERROR;
}
#if 0
if (dup2(fd, STDERR_FILENO) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");
return NGX_ERROR;
}
#endif
if (fd > STDERR_FILENO) {
if (close(fd) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
return NGX_ERROR;
}
}
return NGX_OK;
}
是吧,人家的实现里面有些细节我们前面还是没有考虑到的,不过我们前面考虑到的一些细节人家也是没有采用的,不知道是不是没有必要还是咋滴,我还是将两者结合一下补一份哈,有需要的看情况自取。
缝缝补补又一套
#include#include #include #include #include #include #include #include #include #include int init_daemon(void) { int i; // 1)屏蔽一些控制终端操作的信号 //这些nginx在创建进程的时候设定了哈 signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGTSTP,SIG_IGN); signal(SIGHUP ,SIG_IGN); // 2)创建孤儿进程 switch (fork()) { case -1: return -1; case 0: break; default: exit(0); } // 3)脱离控制终端、登录会话和进程组 setsid(); // 4)禁止进程重新打开控制终端 switch (fork()) { case -1: return -1; case 0: break; default: // 结束第一子进程,第二子进程继续(第二子进程不再是会话组长) exit(0); } // 5)关闭打开的文件描述符 for(i=0; i< NOFILE; ++i){ close(i); } // 6)改变当前工作目录 //这个nginx也没有做 chdir("/"); // 7)重设文件创建掩模 umask(0); // 8)重定向标准流 fd = open("/dev/null", O_RDWR); //难怪在好多地方有看到这么个写法,当时就不知道是干嘛的 if (fd == -1) { return -1; } if (dup2(fd, STDIN_FILENO) == -1) { return -1; } if (dup2(fd, STDOUT_FILENO) == -1) { return -1; } #if 0 if (dup2(fd, STDERR_FILENO) == -1) { return -1; } #endif //把fd关了,nginx可真省,难怪前面那些都不要了 //还是那句话,个人看个人情况自取 if (fd > STDERR_FILENO) { if (close(fd) == -1) { return -1; } } // 9)处理 SIGCHLD 信号 signal(SIGCHLD,SIG_IGN); return 0; } int main(int argc, char *argv[]) { init_daemon(); while(1); return 0; }



