编写规则:
1、调用umask将文件模式创建屏蔽字置为一个已知值(通常是0),因为屏蔽字会阻碍某些权限,影响保护进程的执行;
2、调用fork,然后父进程exit,保证当前进程不是进程组组长;
3、调用setsid,创建一个新的会话;
4、再次调用fork,退出父进程,保证守护进程不是会话首进程,这样调用open重定向标准输入、标准输出、标准错误时,就不会被分配终端;
5、将当前工作目录更改为根目录(并非必须的),因为如果守护进程工作的目录挂载在一个文件系统中,那么该文件系统无法卸载;
6、关闭不需要的文件描述符;
7、重定向标准输入、标准输出、标准错误至/dev/null。
接下来是《Unix环境高级编程》中的一个例子:
#include "apue.h" #include <syslog.h> #include <fcntl.h> #include <sys/resource.h> void daemonize(const char *cmd) { int i, fd0, fd1, fd2; pid_t pid; struct rlimit rl; struct sigaction sa; /* * Clear file creation mask. */ umask(0); /* * Get maximum number of file descriptors. */ if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { err_quit("%s: can't get file limit", cmd); } /* 这一步fork保证进程不是进程组组长进程 */ if ((pid = fork()) < 0) { err_quit("%s: can't fork", cmd); } else if (pid != 0) /* parent */ { exit(0); } /* 创建一个回话,会话只包含子进程,且子进程是会话首进程 */ setsid(); /* 会话首进程的退出会出发SIGHUP信号 默认此信号的操作会终止进程 */ sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) < 0) { err_quit("%s: can't ignore SIGHUP", cmd); } /* 再次创建子进程,退出父进程,保证守护进程不是会话首进程,这样open的时候就不会被分配终端 */ if ((pid = fork()) < 0) { err_quit("%s: can't fork", cmd); } else if (pid != 0) /* parent */ { exit(0); } /* * Change the current working directory to the root so * we won't prevent file systems from being unmounted. */ if (chdir("/") < 0) { err_quit("%s: can't change directory to /", cmd); } /* * Close all open file descriptors. */ if (rl.rlim_max == RLIM_INFINITY) { rl.rlim_max = 1024; } for (i = 0; i < rl.rlim_max; i++) { close(i); } /* 因为前面关闭了所有的文件描述符,此时open返回的必定是最小的0,后面两次dup返回的依次是1、2,也就完成了对标准输入、标准输出、标准错误重定向至/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",fd0, fd1, fd2); exit(1); } }