守护进程编写规则
(1)调用umask(0);
umask是个函数,用来限制(屏蔽)一些文件权限的。
(2)fork()一个子进程(脱离终端)出来,然后父进程退出( 把终端空出来,不让终端卡住);固定套路
fork()的目的是想成功调用setsid()来建立新会话,目的是
子进程有单独的sid;而且子进程也成为了一个新进程组的组长进程;同时,子进程不关联任何终端了;
#include <stdio.h> #include <stdlib.h> //malloc #include <unistd.h> #include <signal.h> #include <sys/stat.h> #include <fcntl.h> //创建守护进程 //创建成功则返回1,否则返回-1 int ngx_daemon() { int fd; switch (fork()) //fork()子进程 { case -1: //创建子进程失败,这里可以写日志...... return -1; case 0: //子进程,走到这里,直接break; break; default: //父进程,直接退出 exit(0); } //只有子进程流程才能走到这里 if (setsid() == -1) //脱离终端,终端关闭,将跟此子进程无关 { //记录错误日志...... return -1; } umask(0); //设置为0,不要让它来限制文件权限,以免引起混乱 fd = open("/dev/null", O_RDWR); //打开黑洞设备,以读写方式打开 if (fd == -1) { //记录错误日志...... return -1; } if (dup2(fd, STDIN_FILENO) == -1) //先关闭STDIN_FILENO[这是规矩,已经打开的描述符,动他之前,先close],类似于指针指向null,让/dev/null成为标准输入; { //记录错误日志...... return -1; } if (dup2(fd, STDOUT_FILENO) == -1) //先关闭STDIN_FILENO,类似于指针指向null,让/dev/null成为标准输出; { //记录错误日志...... return -1; } if (fd > STDERR_FILENO) //fd应该是3,这个应该成立 { if (close(fd) == -1) //释放资源这样这个文件描述符就可以被复用;不然这个数字【文件描述符】会被一直占着; { //记录错误日志...... return -1; } } return 1; } int main(int argc, char *const *argv) { if(ngx_daemon() != 1) { //创建守护进程失败,可以做失败后的处理比如写日志等等 return 1; } else { //创建守护进程成功,执行守护进程中要干的活 for(;;) { sleep(1); //休息1秒 printf("休息1秒,进程id=%d!\n",getpid()); //你就算打印也没用,现在标准输出指向黑洞(/dev/null),打印不出任何结果【不显示任何结果】 } } return 0; }
守护进程可以用命令启动,如果想开机启动,则需要借助 系统初始化脚本来启动。
守护进程不会收到的信号:内核发给你,另外的进程发给你的;
SIGHUP信号
守护进程不会收到来自内核的 SIGHUP 信号; 潜台词就是 如果守护进程收到了 SIGHUP信号,那么肯定是另外的进程发给你的;
很多守护进程把这个信号作为通知信号,表示配置文件已经发生改动,守护进程应该重新读入其配置文件;
SIGINT、SIGWINCH信号
守护进程不会收到来自内核的 SIGINT(ctrl+C),SIGWINCH(终端窗口大小改变) 信号;
守护进程和后台进程的区别
(1)守护进程和终端不挂钩;后台进程能往终端上输出东西(和终端挂钩);
(2)守护进程关闭终端时不受影响,守护进程会随着终端的退出而退出;