关于守护进程
- 为什么要设置守护进程?
- 守护进程———>就是脱离用户终端的后台进程
- 当我们关闭终端时,该进程也不会退出。举个例子:比如我们远程登录一个linux系统,通过终端运行服务器程序,此时我们关闭终端退出来的时候,我们希望该服务器程序依然运行。此时就需要使该服务器进程是守护进程。
- 他是Linux的一种长期运行的后台服务进程。我们常见的httpd,named,sshd等服务都是以守护进程Daemon方式运行的,通常服务名称以字母d结尾。
- 与普通进程相比的特点有:
- 无需终端控制(不需要与用户交互)
- 在后台运行
- 生命周期比较长,一般是随系统启动和关闭
- 守护进程的实现步骤:
- 1.让进程在后台执行。
- 方法是:调用fork产生一个子进程,让父进程退出,子进程就成了孤儿进程 ,使子进程成为后天进程。
- 2.在子进程中创建新的会话,脱离控制终端
- 刚开始,一个会话组id对应一个终端,会话组里包含前台进程组和后台进程组。当关闭终端时,会话组里的所有进程都关闭。所以子进程要想脱离终端时,使用setsid()就可创建新的会话组,并且担任该会话组的组长,独立出终端。
- 3.禁止进程重新打开控制终端
- 此时,进程已经成为一个无终端的会话组长,但他还可以重新申请打开一个终端,为了避免这样,可以使进程不再是会话组长来实现,在一次通过fork创建新的子进程,是调用fork的进程退出
- 4.关闭不再需要的文件描述符
- 创建的子进程从父进程继承打开的文件描述。如果不关闭,会有问题。先得到最高文件描述符值,然后用一个循环程序,关闭0到最高文件描述符值的所有文件描述符。
- 5.改变当前工作目录为根目录
- 子进程会继承父进程的工作目录,由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后使用会造成很多的麻烦。所以需要使用chdir改变子进程的工作目录为根目录。
- 4.重设文件权限掩码
文件权限掩码是屏蔽掉文件权限中的对应位。由于使用fork()函数新创建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带了很多的麻烦(比如父进程中的文件没有执行文件的权限,然而在子进程中希望执行相应的文件这个时候就会出问题)。因此在子进程中要把文件的权限掩码设置成为0,即在此时有最大的权限,这样可以大大增强该守护进程的灵活性。设置的方法是:umask(0) - 6.守护进程的退出,信号注册
通过kill命令,使守护进程退出,通常需要signal注册信号处理函数,进行退出处理。
- 代码示例:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/param.h>
#include<sys/stat.h>
#include<time.h>
#include<syslog.h>
int init_daemon(void)
{
int pid;
int i;
/*忽略终端I/O信号,STOP信号*/
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);
pid=fork();
if(pid>0)
exit(0);//结束父进程,使进程成为后台进程
else if(pid<0)
return -1;
/*建立一个新的进程组,使子进程成为这个进程的首进程,使该进程脱离所有终端*/
setsid();
/*再次新建一个子进程,退出父进程,保证该进程不是进程组长,同时让该进程无法再打开一个新的终端*/
pid=fork();
if(pid>0)
exit(0);
else if(pid<0)
return -1;
/*关闭所有从父进程继承的不再需要的文件描述符*/
for(i=0;i<NOFILE;close(i++))
;
/*改变工作目录,使进程不与任何文件联系*/
chdir("/");
/*将文件屏蔽字设置为0*/
umask(0);
/*忽略SIGCHLD信号*/
signal(SIGCHLD,SIG_IGN);
return 0;
}
int main(void)
{
time_t now;
init_daemon();
syslog(LOG_USER|LOG_INFO,"测试守护进程!\n");
while(1)
{
sleep(8);
time(&now);
syslog(LOG_USER|LOG_INFO,"系统时间:\t%s\t\t\n",ctime(&now));
}
}