守护进程是在后台运行且不与任何控制终端关联的进程。
守护进程没有控制终端通常源于它们由系统初始化脚本启动。然而守护进程也可能从某个终端由用户在shell提示符下键入命令行启动,这样的守护进程必须亲自脱离与控制终端的关联,从而避免与作业控制、终端会话管理、终端产生信号等发生任何不期望的交互,也可以避免在后台运行的守护进程非预期地输出到终端。
编程实例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/resource.h>
void daemon()
{
pid_t pid;
struct rlimit rl;
struct sigaction sa;
umask(0);
if ((pid = fork()) < 0)
exit(-1);
else if (pid > 0)
exit(0);
setsid();
printf("pid:%d sid:%d pgid:%d\n", getpid(), getsid(0), getpgid(0));
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
exit(-1);
if ((pid = fork()) < 0)
exit(-1);
else if (pid > 0)
exit(0);
printf("pid:%d sid:%d pgid:%d\n", getpid(), getsid(0), getpgid(0));
if (chdir("/") < 0)
exit(-1);
int fd_size = getdtablesize();
printf("fd_size:%d\n", fd_size);
for (int fd = 0; fd < fd_size ; fd++)
close(fd);
int fd0 = open("/dev/null", O_RDWR);
int fd1 = dup(0);
int fd2 = dup(0);
if (fd0 != 0 || fd1 != 1 || fd2 != 2)
exit(-1);
rl.rlim_cur = 65535;
rl.rlim_max = 65535;
setrlimit(RLIMIT_NOFILE, &rl);
}
int main()
{
daemon();
return 0;
}
1.调用umask将文件模式创建屏蔽字设置为0。由继承得来的文件模式创建屏蔽字可能会拒绝设置某些权限。
2.第一次调用fork()后,当父进程终止时,shell认为该命令已执行完毕。这样子进程就自动在后台运行,子进程继承了父进程的进程组id。
3.子进程调用setsid()后,子进程成为会话组长(sid==pid),也是进程组组长(pgid==pid),并且脱离了原来的控制终端。
4.第二次调用fork()后,父进程终止,子进程id不等于父进程的sid。由于打开一个控制终端的前提条件是该进程必须是会话组长,所以该进程再也无法打开新的控制终端。
5.忽略SIGHUP信号,第二次fork后,父进程终止时,其会话中的所有进程都会受到SIGHUP信号,所以需要忽略。
6.调用chdir()改变当前工作目录,防止占用可卸载的文件系统。
7.关闭所有文件描述符,再将stdin、stdout、stderr重定向到/dev/null。
8.最后还可以重新设置系统资源。