怎样编写一个守护进程(daemon)

编写步骤

  1. 调用fork(),父进程退出,保留子进程

    使进程在形式上脱离控制终端,shell 终端里可以执行其他命令了。

  2. 调用setsid()创建新session

    上一步中子进程虽然已经在后台运行而不会随着终端的退出而退出,但其 session
    仍然是 shell 所在的 session,process group ID 也没有变,这样 daemon 会受
    到原 process group 的影响,比如向原 group 发送信号。setsid()会新建一个
    会话,并设置 process group ID。调用者会成为新的 session 和 process group
    的 leader。

    第一步调用fork()的另一个原因是 process group leader 不能调用
    setsid()这个函数。新建一个子进程并退出父进程可保证子进程不是group
    leader。

  3. 再次调用fork(),保留子进程

    因为setsid()使进程成为了新的的 session leader,这使得它可以重新打开一个
    控制终端,此次fork()的作用是使子进程不再是 session leader,所以不会因无
    意或故意地打开终端设备而获得控制终端。

  4. 调用chdir()改变工作目录

    改变工作目录以防止一直占用某个目录而导致不能卸载文件系统。

  5. 调用umask(0)重新设置掩码

    因为子进程继承了父进程的文件权限掩码,会给子进程使用文件带来麻烦,将其设
    置为0可以减少不必要的麻烦。

  6. 调用close()关闭文件描述符

    子进程继承了父进程的文件描述符,通常并不需要,因此将其关闭。可使用
    getdtablesize()获得文件描述符表的大小。

  7. 调用signal()处理SIGCHLD信号

    防止守护进程的子进程成为僵尸进程,可以将SIGCHLD的信号处理函数设置为
    SIG_IGN

一个简单例子

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

void do_the_work(void) {}

int main(void)
{
pid_t pc;
int i, j, fd, len;
int dtablesize;

/* Step 1 */
pc = fork();
if (pc < 0) {
perror("fork error");
exit(1);
} else if (pc > 0) {
exit(0);
}

/* Step 2 */
setsid();

/* Step 3 */
pc = fork();
if (pc < 0) {
perror("fork error");
exit(2);
} else if (pc > 0) {
exit(0);
}

/* Step 4 */
chdir("/");

/* Step 5 */
umask(0);

/* Step 6 */
dtablesize = getdtablesize();
for (i = 0; i < dtablesize; i++)
close(i);

/* Step 7 */
signal(SIGCHLD, SIG_IGN);

/* Done. Do the works */
while (1) {
sleep(3);
do_the_work();
}

return 0;
}

References

LINUX下守护进程的C语言实现
UNIX环境高级编程(APUE)之单实例守护进程
How a session leader can take the control of tty in Linux?
How can I recover the controlling tty after a setsid
reptyr: Changing a process’s controlling terminal

0%