对于守护进程,在APUE的13.3节中介绍了其编程规则,共6点。
- 调用umask()设置用户创建文件的默认权限(一般是umask(0));
- 调用fork,然后使其父进程退出exit;
- 调用setsid来创建一个新的会话
- 将当前工作目录切换到根目录
- 关闭所有不需要的文件描述符
- 重定位stdin,stdout 和 stderr 到 /dev/null
对于daemonize.c程序,其完全按照13.3节中介绍的编程规则进行编写
/* 设置创建文件的默认权限为可读、可写 */
umask(0);
/* 调用fork, 然后使父进程退出, 以此来保证子进程不是组长进程 */
/* 调用setsid(), 使得子进程成为新会话的首进程, 并且没有终端 */
if ((pid = fork()) < 0)
err_quit("%s: can't fork", cmd);
else if (pid != 0) /* parent */
exit(0);
setsid();
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");
/* 使用第二个子进程作为守护进程, 这样可以确保守护进程不是会话首进程 */
if ((pid = fork()) < 0)
err_quit("%s: can't fork", cmd);
else if (pid != 0) /* parent */
exit(0);
/* 将当前工作目录切换为根目录, 这样可以避免原工作目录所在的文件系统不能被拆卸 */
if (chdir("/") < 0)
err_quit("%s: can't change directory to /");
/* 关闭所有的文件描述符(从0到最大的文件描述符值) */
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for (i = 0; i < rl.rlim_max; i++)
close(i);
/* 重定位标准输入(stdin), 标准输出(stdout), 标准出错(stderr) 到 /dev/null */
/* 在stdin,stdout和stderr都关闭的情况下, 此处的open函数会返回最小的文件描述符(也就是stdin) */
fd0 = open("/dev/null", O_RDWR);
/* dup函数会返回当前可用文件描述符中最小的数值, 这里返回的依次是 stdout和stderr */
fd1 = dup(0);
fd2 = dup(0);
其中对于 open("/dev/null", O_RDWR)
的用法可以参考APUE中3.3节关于open函数返回值得描述,这里就是用来获取标准输入对应的文件描述符0的。