編寫規則:
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);
}
}