守护进程(Daemon进程,Daemon守护神,Demon魔鬼,一个字母之差,幸好百度一下)
一.啥是守护进程?
守护进程:linux后台服务进程,通常独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。daemon进程通常在系统引导时装入启动,在系统关闭时终止(所以linux系统是多线程的?)
注:在linux中,每个系统与用户进行交流的界面称为终端(terminal),每一个在此终端开始运行的进程都会依附于这个终端,这个终端就称为这些程序的控制终端,当控制终端被关闭时,相应的进程就会自动关闭。but 我们的 守护进程 能够突破这种限制,它从被执行开始运转,直到整个系统关闭时才会退出。所以如果想让某个进程不因为用户或者其他变化而受到影响,我们就必须把这个进程变成一个守护进程。
二.如何创建守护进程?
按以下四个步骤创建一个简单的守护进程:
第一步.创建子进程并让父进程退出(也就是产生一个孤儿进程)
由于程是脱离控制终端的,因此,完成第一步后就会在shell终端里造成一程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在shell终端则可以执行其他命令,从而形式上做到了与控制终端脱离。
第二步.在子进程中创建新会话(很重要)
setsid函数用于创建一个新的会话并担任该回话组的组长。
回忆一下,在第一步中,父进程退出留下的子进程全盘拷贝了父进程的会话期、进程组、控制终端等,因此,这个子进程还不是真正的独立,所以
调用setsid的作用是:①让进程摆脱原会话的控制;
②让进程摆脱原进程组的控制;
③让进程摆脱原控制终端的控制;
注:进程组:是一个进程或多个进程的集合。进程组由进程组ID唯一标识。除进程号(PID)外,进程组ID也是一个进程的必备属性。进程组有一个组长进程,组长进程的进程号与进程组ID相等,且进程组的ID不会因为组长进程的退出受影响。
会话组:是一个或多个进程组的集合。
第三步.改当前目录为根目录
子进程继承了父进程的工作目录。由于在进程运行过程中,当前目录所在的文件系统(如/mnt/usb)是不能卸载的,这对以后的使用会造成诸多不便。因此,通常是让“/"或者‘’/tmp"作为守护进程的当前工作目录。改变工作目录的常见函数:chdir
第四步.重设文件权限掩码,关闭文件描述符
把文件权限掩码设置为0,可以大大增强守护进程的灵活性。通常使用方法为:umask(0)。
子进程会从父进程那里继承一些已经打开的文件,然而守护进程可能永远不会对这些文件读或写,但它们一样消耗系统资源,且可能导致所在文件系统无法卸下。
比如:在第二步中守护进程已经与控制终端失去联系,也就是说终端不可能向守护进程输入字符,同样守护进程也不能printf 于终端上显示,所以文件描述符0、1、2的三个文件(输入、输出、报错三个文件)应该被关闭。
三.守护进程实例:
建立守护进程,并每隔5s在/tmp/daemon.log中写一句话。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<fcntl.h> #include<sys/types.h> #include<unistd.h> #include<sys/wait.h> #define MAXFILE 65535 int main() { pid_t pc; int i,fd,len; char * buf="this is a daemon " len=strlen(buf); /*第一步*/ pc=fork(); if(pc<0) { printf("fork error "); exit(1); } /*父进程退出*/ else if (pc>0) exit(0); /*第二步*/ setsid(); /*第三步*/ chdir("/"); /*第四步*/ umask(0); for(i=0;i<MAXFILE;i++) close(i); /*完成守护进程的创建,以下开始守护进程写操作*/ while(1) { if((fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0) { perror("open"); exit(1); } write(fd,buf,len+1); close(fd); sleep(5); } }