守护进程的特性
- 守护进程完全脱离于终端
- 守护进程必须与它运行前的环境完全脱离开来,这些环境包括未关闭的文件描述符、控制终端、会话和进程组、工作目录和文件创建掩模等。这些环境通常是从执行它的父进程(特别是shell)中继承下来的
创建守护进程
知道了守护进程的特性,我们只需要使创建的进程满足守护进程的特性即可。注意进程组组长无法调用setsid(),只有非进程组长才能调用setsid()。
- 创建子进程,父进程退出(子进程成为孤儿进程,终端可以执行其他任务,但子进程还是属于该终端)
- 在子进程中创建新会话:使用setsid(),子进程成为了新的进程组长,会话组长(这样摆脱了原来的进程组,会话,终端)
- 由于子进程是新的会话组长,可以创建新的控制终端。为了避免这个,我们再fork()一下,使父进程退出。
- 将继承自父进程的文件屏蔽字清零,umask(0)。
- 关闭不需要的文件描述符。
- 改变工作目录
代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int cdaemon()
{
int fd;
switch(fork())
{
case -1:
return -1;
case 0:
break;
default:
exit(0);
}
if(setsid() == -1)
{
return -1;
}
switch(fork())
{
case -1:
return -1;
case 0:
break;
default:
exit(0);
}
umask(0);
fd = open("/dev/null", O_RDWR);
if(fd == -1)
{
return -1;
}
if(dup2(fd, STDIN_FILENO) == -1)
{
return -1;
}
if(dup2(fd, STDOUT_FILENO) == -1)
{
return -1;
}
if(fd <= STDERR_FILENO)
{
return -1;
}
if(close(fd) == -1)
{
return -1;
}
chdir("/");
return 1;
}
int main()
{
if(cdaemon() != 1)
{
printf("create daemon failure!\n");
exit(0);
}
for(;;)
{
sleep(1);
printf("do smoething here\n");
}
return 0;
}