1. 什么是守护进程
守护进程是一种在后台运行不受终端控制,并且周期性的执行某种任务或等待处理某些发生的事件的进程。
2.生存周期
从守护进程开始执行直到手动杀死进程或者系统关闭。
3.脱离终端的原因
(1)开启守护进程后终端去执行其他任务,守护进程的错误信息不应再出现在终端上,即不应再影响终端。
(2)守护进程运行后不应再被任何终端上的信息所影响。
4.启动守护进程的方法
(1)系统初始化脚本启动(在/etc或者/etc/rc下)
(2)许多网络服务器是由inetd超级服务器启动的。(inetd也是守护进程,它是由系统初始化脚本启动的),inetd监听网络请求,当请求到来时启动实际的服务器。
(3)cron守护进程按规则定期执行一些程序,这些程序也是以守护进程的方式运行的。
(4)可以用at命令指定在将来的某一时刻执行程序。
(5)守护程序也可以从终端启动,通常这种方式只用于守护进程的测试,或者是重起因某种原因而停止的进程。
(6)在终端上用nohup 启动的进程。用这种方法可以把所有的程序都变为守护进程。(功能强大的一条命令)
5.输出守护进程消息
守护进程没有控制终端,当发生问题时它要用syslog函数发送给syslogd守护进程,并根据配置信息作出相应的处理。
6.创建守护进程
(1)调用fork()函数产生后台子进程。然后终止父进程,留下子进程继续运行。如果进程是以shell命令方式从前台启动的,当父进程终止时,shell就认为命令已经完成,可以自动使子进程在后台运行。同时,子进程属于父进程的进程组,从而保证了子进程不是所属组的组长进程,因此子进程可以调用setid()函数产生新的会话期。
(2)调用setsid()函数产生新的会话期并失去控制终端。产生新的会话期后,进程成为新的会话期的组长进程以及新进程组的组长进程,同时进程将失去控制终端,这一步是最重要的一步。
进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程组ID不会因组长进程的退出而受到影响。
会话期:会话期是一个或者多个进程组的集合。通常一个会话开始于用户的登录,终止于用户的退出,在此期间该用户运行的所有进程都属于这个会话期。
(3)忽略SIGHUP信号并在此调用fork()函数产生新子进程。第二次调用fork()函数的目的是确保守护进程将来即使打开一个终端设备,也不会自动获取控制终端(确保它不会是一个会话的头)。忽略SIGHUP信号是因为会话期组长进程终止时会向会话期中的其他进程发送SIGHUP信号造成其他进程终止,所以忽略此信号。
(4)改变工作目录。通常将目录改变到根目录下,这样它启动的目录也可以被卸掉。因为在进程运行中,当前目录所在的文件系统是不能卸载的。
(5)关闭已打开的文件描述符,并打开一个空设备,把它复制到标准输出、标准错误上。这是由于守护进程的程序可能包括一些库或者重用一些其他程序代码,库中可能存在向标准输出或者标准错误输出的函数调用。因为关闭了所有的描述符,这些函数将返回错误而可能造成程序终止,如果将标准输出和标准错误映射到空设备上,将不会产生任何影响。
(6)用openlog()函数建立与syslogd的连接。
7.示例
创建守护进程的典型代码如下:
#define MAXFD 64
void daemon_init(const char *pname, int facility)
{
int i;
pid_t pid;
if((pid = fork()) != 0) //创建第一个子进程;
exit(0); //父进程终止
//子程序继续
setsid(); //成为会话头
signal(SIGHUP, SIG_IGN);//忽略SIGHUP信号
if((pid = fork()) != 0)
exit(0); //第二个子进程终止
//第二个子进程继续
chdir("/"); //改变工作目录
umask(0); //清楚文件模式创建掩码
for(i = 0; i < MAXFD; i++)//关闭所有打开的套接字
close(i);
openlog(pname, LOG_PID, facility);//用syslogd处理错误
}