守护进程
- 引入及概念
- 流程
- 1、fork()创建子进程,父进程exit()退出;
- 2、在子进程调用setsid()创建新会话;
- 3、在子进程中调用chdir()让根目录“/”成为子进程的工作目录;
- 4、在子进程中调用umask()重设文件权限掩码为0;
- 5、在子进程中close()不需要的文件描述符;
- 实现
引入及概念
守护进程的引入其一大原因还是普通进程不可靠,防止进程死掉,普通进程是绑定终端的,其终端退出就死掉了。
守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在 执行过程中的信息也不在任何终端上显示。由于在 linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依 附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时相应的进程都会自动关闭。
流程
1、fork()创建子进程,父进程exit()退出;
这是创建守护进程的第一步。由于守护进程是脱离控制终端的,完成这一步后就会在Shell终端里造成程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离,在后台工作。
由于父进程先于子进程退出,子进程就变为孤儿进程,并由 init 进程作为其父进程收养。
2、在子进程调用setsid()创建新会话;
在调用了 fork() 函数后,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变。这还不是真正意义上的独立开来,而 setsid() 函数能够使进程完全独立出来。
setsid()创建一个新会话,调用进程担任新会话的首进程,其作用有:
使当前进程脱离原会话的控制
使当前进程脱离原进程组的控制
使当前进程脱离原控制终端的控制
这样,当前进程才能实现真正意义上完全独立出来,摆脱其他进程的控制。
3、在子进程中调用chdir()让根目录“/”成为子进程的工作目录;
这一步也是必要的步骤。使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让"/"作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir。(避免原父进程当前目录带来的一些麻烦)
4、在子进程中调用umask()重设文件权限掩码为0;
文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限(就是说可读可执行权限均变为7)。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此把文件权限掩码重设为0即清除掩码(权限为777),这样可以大大增强该守护进程的灵活性。通常的使用方法为umask(0)。(相当于把权限开发)
5、在子进程中close()不需要的文件描述符;
同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。其实在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。(关闭失去价值的输入、输出、报错等对应的文件描述符)
for (i=0; i < MAXFILE; i++)
close(i);
实现
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXFILE 3
int main()
{
pid_t pid;
int fd,len,i,num;
char *buf="the dameon is running\n";
len=strlen(buf)+1;
/*1.fork()创建子进程,父进程exit()退出;*/
pid=fork();
if(pid<0)
{
printf("fork fail\n");
exit(1);
}
if(pid>0)
exit(0);
/*2.在子进程调用setsid()创建新会话;*/
setsid();
/*3.在子进程中调用chdir()让根目录“/”成为子进程的工作目录;*/
chdir("/");
/*4.在子进程中调用umask()重设文件权限掩码为0;*/
umask(0);
/*5.在子进程中close()不需要的文件描述符;*/
for(i=0;i<MAXFILE;i++)
close(i);
/*6.守护进程处理信息*/
while(1)
{
fd=open("var/log/dameon.log",O_CREAT|O_WRONLY|O_APPEND,0666);
write(fd,buf,len);
close(fd);
sleep(10);
}
}