Linux创建精灵进程

创建Daemon进程的目的

控制终端会由于某些原因(如断开终端连接)会发送一些信号,然而这些信号会因为接收进程处理或者缺省处理而使得进程中断退出。

C/C++创建Daemon进程常用实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int Daemon()
{
pid_t pid = fork();
//parent exit
if (pid != 0)
{
exit(-1);
}
//first children become group leader
if (-1 == setsid())
{
exit(-1);
}
umask(0);
pid = fork();
if (0 != pid)
{
exit(0);
}
//second children
chdir("/");
for (int i = 0; i < 3; i++)
{
close(i);
}
int stdfd = open("/dev/null", O_RDWR);
dup2(stdfd, STDOUT_FILENO);
dup2(stdfd, STDERR_FILENO);
return 0;
}

流程:

1.第一次fork的作用是让shell认为本条命令已经终止,不再挂在终端输入上。还有一个作用是为后面的setsid()服务。因为,setsid()的调用者不能是进程组组长(group leader),此时父进程是进程组组长。

2.setsid()调用成功后,子进程会成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。此时新的进程就不会收到终端的信号了。

3. 第2次fork不是必须的。fork两次主要目的是:防止进程再次打开一个控制终端。因为打开一个控制终端的前台条件是该进程必须是会话组长。再fork一次,子进程ID != sid(sid是进程父进程的sid)。所以也无法打开新的控制终端。

创建Daemon进程的其他实现方式

通过忽略控制终端产生的信号来实现守护进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//后台进程读取/写入终端输入产生下面两个信号,或者控制终端不存在情况读取和写入会产生
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);

//按CTRL-C ,CTRL-\ CTRL-Z会向前台进程组发送下面这些信号
signal(SIGINT, SIG_IGN );
signal(SIGQUIT, SIG_IGN );
signal(SIGTSTP, SIG_IGN );

//终端断开,会给会话组长或孤儿进程组所有成员发送下面信号
signal(SIGHUP, SIG_IGN );

还有有些信号也可以由终端shell产生,需要关注
signal(SIGCONT, SIG_IGN );
signal(SIGSTOP, SIG_IGN );