守护进程概述
守护进程,也就是通常所说的Daemon 进程,是Linux 中的后台服务进程。它是一个生
存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事
件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统有很多守护进程,
大多数服务都是通过守护进程实现的,如本书在第二章中讲到的系统服务都是守护进程。同
时,守护进程还能完成许多系统任务,例如,作业规划进程crond、打印进程lqd等(这里的
结尾字母d就是Daemon的意思)。
由于在Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运
行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,
相应的进程都会自动关闭。但是守护进程却能够突破这种限制,它从被执行开始运转,直到
整个系统关闭时才会退出。如果想让某个进程不因为用户或终端或其他的变化而受到影响,
那么就必须把这个进程变成一个守护进程。可见,守护进程是非常重要的。
编写守护进程
编写守护进程看似复杂,但实际上也是遵循一个特定的流程。只要将此流程掌握了,就
能很方便地编写出用户自己的守护进程。下面就分4个步骤来讲解怎样创建一个简单的守护
进程。在讲解的同时,会配合介绍与创建守护进程相关的几个系统函数,希望读者能很好地
掌握。
1.创建子进程,父进程退出
这是编写守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就
会在Shell 终端里造成一程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而
用户在Shell终端里则可以执行其他的命令,从而在形式上做到了与控制终端的脱离。
到这里,有心的读者可能会问,父进程创建了子进程,而父进程又退出之后,此时该子
进程不就没有父进程了吗?守护进程中确实会出现这么一个有趣的现象,由于父进程已经先
于子进程退出,会造成子进程没有父进程,从而变成一个孤儿进程。在Linux 中,每当系统
发现一个孤儿进程,就会自动由1 号进程(也就是init 进程)收养它,这样,原先的子进程
就会变成init进程的子进程了。2.在子进程中创建新会话
这个步骤是创建守护进程中最重要的一步,虽然它的实现非常简单,但它的意义却非常
重大。在这里使用的是系统函数setsid,在具体介绍setsid之前,读者首先要了解两个概念:
进程组和会话期。
· 进程组
进程组是一个或多个进程的集合。进程组由进程组ID 来惟一标识。除了进程号(PID)
之外,进程组ID 也一个进程的必备属性。
每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程ID 不会
因组长进程的退出而受到影响。
· 会话期
会话组是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,
在此期间该用户运行的所有进程都属于这个会话期。
接下来就可以具体介绍setsid的相关内容:
(1)setsid函数作用
setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid有下面的3个
作用。
· 让进程摆脱原会话的控制。
· 让进程摆脱原进程组的控制。
· 让进程摆脱原控制终端的控制。
那么,在创建守护进程时为什么要调用setsid 函数呢?读者可以回忆一下创建守护进程
的第一步,在那里调用了fork函数来创建子进程再将父进程退出。由于在调用fork函数时,
子进程全盘拷贝了父进程的进会话期、进程组、控制终端等,虽然父进程退出了,但原先的
会话期、进程组、控制终端等并没有改变,因此,还不是真正意义上独立开来,而setsid 函
数能够使进程完全独立出来,从而脱离所有其他进程的控制。
(2)setsid函数格式
所需头文件#include
#include
函数原型pid_t setsid(void)
函数返回值
成功:该进程组ID
出错:-1
3.改变当前目录为根目录
这一步也是必要的步骤。使用fork创建的子进程继承了父进程的当前工作目录。由于在
进程运行过程中,当前目录所在的文件系统(比如“/mnt/usb”等)是不能卸载的,这对以后
的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是
让“/”作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,
也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir。
4.重设文件权限掩码
文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有一个文件权限掩码是050,它
就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程
的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置
为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通
常的使用方法为umask(0)。
5.关闭文件描述符
同文件权限掩码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的
文件。这些被打开的文件可能永远不会被守护进程读或写,但它们一样消耗系统资源,而且
可能导致所在的文件系统无法卸下。
在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的
字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上
显示出来。所以,文件描述符为0、1 和2的3 个文件(常说的输入、输出和报错这3 个文件)
已经失去了存在的价值,也应被关闭。通常按如下方式关闭文件描述符:
for(i=0;iclose(i);
该实例首先建立了一个守护进程,然后让该守护进程每隔10s在/tmp/dameon.log中写入
一句话。
/*dameon.c创建守护进程实例*/
#include
#include
#include
#include
#include
#include
#include
#define MAXFILE 65535
int main()
{
pid_t pc;
int i,fd,len;
char *buf="This is a Dameon\n";
len =strlen(buf);
pc=fork(); //第一步
if(pc<0){
printf("error fork\n");
exit(1);
}else if(pc>0)
exit(0);
/*第二步*/
setsid();
/*第三步*/
chdir("/");
/*第四步*/
umask(0);
for(i=0;i/*第五步*/
close(i);
/*这时创建完守护进程,以下开始正式进入守护进程工作*/
while(1){
if((fd=open("/tmp/dameon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0){
perror("open");
exit(1);
}
write(fd, buf, len+1);
close(fd);
sleep(10);
}
[root@(none) 1]# ps -ef|grep daemon
root 16011 1 ./daemon
root 16014 3318 grep daemon
阅读(926) | 评论(1) | 转发(0) |