什么是守护进程?
守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。
Linux 的大多数服务器就是用守护进程实现的。比如,Internet 服务器 inetd,Web 服务器 httpd 等。
#include "apue.h"
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h>
void daemonize(const char *cmd){
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
umask(0);//因为我们从shell创建的daemon子进程,所以daemon子进程会继承shell的umask,如果不清除的话,会导致daemon进程创建文件时屏蔽某些权限。
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
err_quit("%s: can't get file limit", cmd);
if ((pid = fork()) < 0)//fork后让父进程退出,子进程获得新的pid,肯定不为进程组组长,这是setsid前提。
err_quit("%s: can't fork", cmd);
else if (pid != 0)
exit(0);
setsid();//调用setsid来创建新的进程会话。这使得daemon进程成为会话首进程,脱离和terminal的关联。
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
err_quit("%s: can't ignore SIGHUP", cmd);
if ((pid = fork()) < 0)//最好在这里再次fork。这样使得daemon进程不再是会话首进程,那么永远没有机会获得控制终端。如果这里不fork的话,会话首进程依然可能打开控制终端。(好像不用fork二次了。。。。)
err_quit("%s: can't fork", cmd);
else if (pid != 0)
exit(0);
if (chdir("/") < 0)//将当前工作目录切换到根目录。父进程继承过来的当前目录可能mount在一个文件系统上,如果不切换到根目录,那么这个文件系统不允许unmount。
err_quit("%s: can't change directory to /", cmd);
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for (i = 0; i < rl.rlim_max; i++)
close(i);//在子进程中关闭从父进程中继承过来的那些不需要的文件描述符。可以通过_SC_OPEN_MAX来判断最高文件描述符(不是很必须).
fd0 = open("/dev/null", O_RDWR);//打开/dev/null复制到0,1,2,因为dameon进程已经和terminal脱离了,所以需要重新定向标准输入,标准输出和标准错误(不是很必须).
fd1 = dup(0);
fd2 = dup(0);
openlog(cmd, LOG_CONS, LOG_DAEMON);
if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
syslog(LOG_ERR, "unexpected file descriptors %d %d %d",fd0, fd1, fd2);
exit(1);
}
}