守护进程运行在后台,不与任何控制终端相关联.守护进程以root用户运行或者其他特殊的用户,并处理一些系统级的任务, 习惯上守护进程的名字以d结尾,但不是必须的.
对守护进程的两个基本要求: 它必须是init进程的子进程; 不与任何控制终端相关联.
一般来讲, 进程可以通过以下步骤成为守护进程:
1. 调用fork(), 创建新的进程, 它会是将来的守护进程.
2. 在守护进程的父进程中调用exit(). 这保证祖父进程(守护进程的祖父进程)确认父进程已经结束. 还保证父进程不再继续运行, 守护进程不是组长进程. 最后一点是顺利完成一下步骤的前提.
3. 调用setsid(), 使得守护进程有一个新的进程组和新的会话, 两者都把它作为首进程, 这也保证它不会与控制终端相关联.
4. 用chdir()将当前工作目录改为根目录. 因为前面调用fork()创建了新进程, 它所继承来的当前工作目录可能在文件系统的任何地方. 而守护进程通常在系统启动时运行, 同时不希望一些随机目录保持打开状态, 也就阻止了管理员卸载守护进程工作目录所在的那个文件系统.
5. 关闭所有的文件描述符. 不需要继承任何打开的文件描述符, 对于无法确认的文件描述符, 让它们继续处于打开状态.
6. 打开0, 1和2号文件描述符(标准输入, 标准输出和标准错误), 把它们重定向到/dev/null.
根据这些规则, 下面程序可以成为守护进程:
1 #include<sys/types.h>
2 #include<sys/stat.h>
3 #include<stdlib.h>
4 #include<stdio.h>
5 #include<fcntl.h>
6 #include<unistd.h>
7 #include<linux/fs.h>
8
9 int main(void)
10 {
11 pid_t pid;
12 int i;
13 /* create new process */
14 pid = fork();
15 if(pid == -1) return -1;
16 else if(pid != 0) exit(EXIT_SUCCESS);
17 /* create new session and process group */
18 if(setsid() == -1) return -1;
19 /* set the working directory to the root directory*/
20 if(chdir("/") == -1) return -1;
21 /* close all open files -- NR_OPEN is overkill, but works */
22 for(i = 0; i < NR_OPEN; i++) close(i);
23 /* redirect fd's 0, 1, 2 to /dev/null */
24 open("/dev/null", O_RDWR); /* stdin */
25 dup(0); /* stdout */
26 dup(0); /* stderr */
27 /* do it daemon thing.*/
28
29 return 0;
30 }
许多Unix系统在它们的C函数库中提供了daemon()函数来完成这些工作:
#include <unistd.h>
int daemon(int nochdir, int noclose);