初探守护进程
1. 什么守护进程(也就是我们所说的Daemon进程)
在linux或者unix操作系统中在系统引导的时候会开启很多服务,这些服务就叫做守护进程。守护进程通常独立于控制终端并且周期性的执行某种任务或者等待处理某些发生的事件。守护进程常常在系统引导载入时启动,在系统关闭时终止。在Linux系统中的大部分服务都是通过守护进程来实现的。守护进程还能完成许多系统任务,例如,作业规划进程crond,打印进程lqd等等,这里的d就是Daemon的意思。为了增加灵活性,root可以选择系统开启的模式,这些模式叫做运行级别,每一种运行级别以一定的方式配置系统。守护进程是脱离于终端并且在后台运行的进程。守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的终端信息所打断。
2. 编写守护进程的过程
一般编写守护进程包含四个过程:
2.1创建子进程,让父进程退出
由于守护进程是脱离控制终端的,完成这一步后会在shell终端里边造成一种程序已经运行完毕的假象。从形式上做到了与终端的脱离。父进程退出后,子进程就变成了孤儿进程,而当造作系统发现一个孤儿进程后,就会自动由1号进程(也就是init进程)来收养这个孤儿进程。这样,原来的子进程就会变成init进程的子进程。
示例代码如下:
pid = frok();
if(pid>0)
{
exit(0);//父进程退出
}
2.2在子进程中创建新会话
这个是创建守护进程中最重要的一步:虽然实现简单,但是意义非凡。这里使用的系统函数是setsid()。我们来看两个概念:
2.2.1 进程组:是一个或者多个进程的集合。进程组由进程ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程必要的属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程ID不会因组长进程的退出而受影响。
2.2.2 会话组:会话组是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,自此期间该用户运行的所有的进程都属于这个会话期。
2.3setsid()函数的作用
这个函数会创建一个新的会话。那么为什么在创建守护进程的时候要使用这个函数。因为在创建守护进程的第一步中使用fork()函数,会将父进程的会话期、进程组和控制终端等。虽然父进程退出了,但是原先的会话期、进程组和控制终端等并没有改变,因此,还算不上真正意识上的独立,而setsid()函数能够使进程完全独立出来,从而脱离所有其他进程的控制。这是源于setsid()函数的三个作用:
(1) 样进程摆脱原会话的控制
(2) 让进程摆脱原进程组的控制
(3) 让进程摆脱原控制终端的控制
3. 改变当前目录为根目录
使用fork()函数创建的子进程继承了父进程的当前的工作目录。由于在进程运行过程中,当前目录所在文件系统(比如/mnt/usb/等)是不能卸载的,这对以后的使用或造成很多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,为了避免这种问题,做法就是将”/”作为守护进程的当前工作目录。当然如果有去他的特殊的特殊需求,有可以把当前工作目录变化为其他路径,如/tmp.。使用chfir()函数来改变当前工作目录。
4. 重新设置文件权限掩码
由于使用了fork()函数创建的子进程继承了父进程的文件权限掩码,这就给子进程使用文件带来了诸多麻烦。将文件权限掩码设置为0,可以大大增加该守护进程的灵活性。使用umask()函数来设置。
4.1说一些有关文件权限掩码的知识:
就是umask,umask是控制生成文件的缺省属性。
4.1.1一般来说,文件的默认权限是666,目录的权限是777。(umask为000的情况下)
4.1.2.如果umask设置为002,则新增文件的权限为664,目录的权限是775。
5. 关闭文件描述符
同文件权限掩码一样,使用frok()函数新建的子进程会从父进程哪里继承一些已经打开的文件。而这些被打开的文件永远不会被守护进程读或者写,但是他们却在不断的消耗系统的资源,所以我们将这些打开的文件关闭。在完成第二步之后,守护进程与所属的控制终端失去了联系。因此从终端输入的字符不可能到达守护进程,守护进程中使用的常规方法如printf()输出的字符也不会显示在终端上。所以我我们将文件描述符0、1、2(标准输入,标准输出,标准错误输出)关闭。
6. 总结:
这样我们就实现了一个简单的守护进程。下面就是该守护进程每隔10s向日志文件/tmp/dqemon.log写入一句话。
源码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int i,fd;
char* buf = "This is a Daemon\n";
pid = fork();
if(pid<0)
{
printf("Error fork\n");
exit(1);
}
else if(pid>0)//退出父进程
{
exit(0);
}
setsid();
chdir("/");
umask(0);
for(i=0;i<getdtablesize();i++)
{
close(i);
}
while(1)
{
if((fd = open("/tmp/daemon.log",O_CREAT | O_WRONLY | O_APPEND,0600))<0)
{
printf("open file error\n");
exit(1);
}
write(fd,buf,strlen(buf)+1);
close(fd);
sleep(5);
}
exit(0);
}