1、守护进程概述
守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。
守护进程常常在系统启动时开始运行,在系统关闭时终止Linux系统有很多守护进程,大多数服务都是用守护进程实现的。
守护进程常常在系统启动时开始运行,在系统关闭时终止Linux系统有很多守护进程,大多数服务都是用守护进程实现的。
在Linux中,每一个系统与用户进行交流的界面称为终端。从该终端开始运行的进程都会依附于这个终端,这个终端称为这些进程的控制终端。当控制终端被关闭时,相应的进程都会被自动关闭。
守护进程能够突破这种限制,它从开始运行,直到整个系统关闭才会退出。如果想让某个进程不会因为用户或终端的变化而受到影响,就必须把这个进程变成一个守护进程。
守护进程能够突破这种限制,它从开始运行,直到整个系统关闭才会退出。如果想让某个进程不会因为用户或终端的变化而受到影响,就必须把这个进程变成一个守护进程。
2、守护进程编写步骤
守护进程的编写可以分为以下几个步骤:
1.创建子进程,父进程退出
2.在子进程中创建新会话
3.改变当前目录为根目录
4.重设文件权限掩码
5.关闭文件描述符
我们分步骤进行讲解
2.1 创建子进程,父进程退出
这是创建守护进程的第一步。由于守护进程是脱离控制终端的,因此完成第一步后,子进程就变成了后台进程
之后的所有后续工作都在子进程中完成,而用户在shell终端里则可以执行其他的命令,从而在形式上做到了与控制终端的脱离由于父进程已经先于子进程退出,会造成子进程没有父进程,从而变成一个孤儿进程。在Linux中,每当系统发现一个孤儿进程,就会自动由1号进程收养。原先的子进程就会变成init进程的子进程。
2.2 在子进程中创建新的会话
在这里我们先了解两个概念:进程组和会话期。
(1)进程组
进程组是一个或多个进程的集合。进程组由进程组
ID
来唯一标识。
每个进程组都有一个组长进程,进程组
ID
就是
组长进程的进程号。
(2)会话期
会话组是一个或多个进程组的集合。通常,一个会话开始于用户登录,结束于用户退出。或者开始于终端打开,结
束于终端关闭。会话期的第一个进程称为会话组长。在此期间该用户运行的所有进程都属于这个会话期。
用下面这幅图来加深一下理解
在这个步骤中,我们使用setsid()函数来完成此项工作。setsid()函数的作用是创建一个新的会话,并使得当前进
程成为新会话组的
组长。调用setsid()有下面3个作用
1、让进程摆脱原会话的控制;
2、让进程摆脱原进程组的控制;
3、 让进程摆脱原控制终端的控制
使用setsid()函数的意义在哪里?由于调用fork函数时,子进程全盘拷贝了父进程的进会话期、进程组、控制终端等,虽然之后父进程退出了,但原先的会话期、进程组、控制终端等并没有改变,因此,还没有真正意义上独立开来。而setsid()正是让子进程完全独立,从而脱离其他进程的控制。
函数原型 pid_t setsid(void)
函数返回值 成功,返回进程组ID;失败,返回-1
2.3 改变当前目录
通常的做法是让“/”或”/tmp”作为守护进程的当前工作目录 。
在进程运行过程中,当前目录所在的文件系统是不
能卸载的。
chdir函数可以改变进程当前工作目录。
2.4 重设文件权限掩码
文件权限掩码是指文件权限中被屏蔽掉的对应位。把文件权限掩码设置为0,可以增加该守护进程的灵活性。设
置文件权限掩码的函数是umask通常的使用方法为umask(0) 。
2.5 关闭文件描述符
新建的子进程会从父进程那里继承所有已经打开的文件。在创建完新的会话后,守护进程已经脱离任何控制
终端,应当关闭用不到的文件。
这些被打开的文件可能永远不会被守护进程读或写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸载
从终端输入的字符不可能达到守护进程,守护进程中用常规的方法(如printf)输出的字符也不可能在终端上显示出来。所以,
文件描述符为0、1和2的三个文件(对应标准输入、标准输出和标准错误这三个流)已经失去了存在的意义,也应被关闭。
关闭文件描述符的程序如下
fdtablesize = getdtablesize();
for (fd = 0; fd < fdtablesize; fd++)
{
close(fd);
}
守护进程创建例程:不断的向日志写入数据
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc, const char *argv[])
{
pid_t pid;
int i,fd;
char *buf = "this is a daemon process\n";
pid = fork();
if(pid<0)
{
printf("fork err\n");
}
else if(pid>0)
{
/*first:parent process exit*/
exit(0);
}
/*second:setsid*/
setsid();
/*third:change dir*/
chdir("/tmp");
/*fourth:unmask*/
umask(0);
/*fifth:close fd*/
for(i=0;i<getdtablesize();i++)
{
close(i);
}
while(1)
{
if((fd = open("daemon.log",O_CREAT|O_WRONLY|O_APPEND),0600)<0)
{
printf("open err\n");
exit(1);
}
write(fd,buf,strlen(buf));
close(fd);
sleep(2);
}
exit(0);
return 0;
}
每隔2s向daemon.log中写入数据。