编写规则:
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);
}
}