#include <sys/param.h>
#include <signal.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
void daemonize() {
int pid = -1, fd = -1;
// 1.轉變為后臺進程
if ((pid = fork()) == -1) exit(1);
if (pid != 0) exit(0); // 父進程(前臺進程)退出
// 2.離開原先的進程組,會話
if (setsid() == -1) exit(1); // 開啟一個新會話
// 3.禁止再次打開控制終端
if ((pid = fork()) == -1) exit(1);
if (pid != 0) exit(0); // 父進程(會話領頭進程)退出
// 4.關閉打開的文件描述符,避免浪費系統資源
for (int i = 0; i < NOFILE; i++)
close(i);
// 5.改變當前的工作目錄,避免卸載不了文件系統
if (chdir("/") == -1) exit(1);
// 6.重設文件掩碼,防止某些屬性被父進程屏蔽,也有設置為0027的(守護進程創建的臨時文件不希望被其他用戶查看)
umask(0);
// 7.重定向標準輸入,輸出,錯誤流,因為守護進程沒有控制終端
// 如果只是把0、1、2都close了,那么守護進程里新創建的文件fd會用到0、1、2,如果用戶的代碼里有用到printf/cout之類的,那就會把數據打到新創建的文件中,這樣就會產生混淆
if ((fd = open("/dev/null", O_RDWR)) == -1) exit(1); // 打開一個指向/dev/null的文件描述符
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
// 8.本守護進程的子進程若不需要返回信息,那么交給init進程回收,避免產生僵尸進程,否則子進程退出后將成為僵尸進程
if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) exit(1);
}
對setsid闡述較透徹:http://www.cnblogs.com/xuxm2007/archive/2011/07/29/2121280.html
對終端的事說了些:http://blog.51cto.com/10541559/1771212
對syslog的事說了些:https://www.linuxidc.com/Linux/2015-01/111933.htm
此外,man 3 daemon這個庫函數只實現了部分階段(例如沒有屏蔽SIGCHLD以及對mask做任何處理):https://github.com/lattera/glibc/blob/master/misc/daemon.c