守护进程(daemon)是一类运行在后台的特殊进程(不受任何终端控制),通常执行某些特定的系统任务,linux系统中绝大多数进程都是在用户登录或者程序运行时创建,随着程序运行结束或者用户注销,这些进程也到达生命的终点,但是守护进程不受用户登录或注销的影响,它们一直运行着,直到系统关闭。
守护进程的特性
ps axj
-a 显示所有用户所拥胡的进程的状态
-x 显示没有控制终端的进程状态
-j 显示与作业相关的的信息:会话ID、进程组ID、控制终端以及终端进程组ID1.没有一个守护进程具有控制终端,其终端名设置为问号(?)
终端前台进程组ID设置为-1,内核守护进程以无控制终端方式启动
用户层守护进程缺少控制终端是守护进程调用setsid的结果
所有用户层守护进程都是进程组的组长进程以及会话的首进程,而且是这些进程组和会话中的唯一进程
大多数守护进程的父进程init进程
编写守护进程的规则
1)创建子进程,关闭父进程
守护进程需要脱离shell终端控制,就需要让父进程终止让shell认为该命令已经执行完毕,造成进程结束的假象,让所有工作在子进程中进行。子进程创建,父进程退出,子进程成为孤儿,被init收养,变成init的子进程。子进程继承原来的父进程ID,但它成为init的子进程有了新的进程ID,所以保证了它不是进程组的组长进程。
2) 调用umask将文件模式创建屏蔽字设置为0
由于使用fork函数新建的子进程继承了父进程的文件创建掩码,这就给该子进程使用文件带来了诸多的麻烦。例如:守护进程要创建一个可读,可写的文件,然而子进程因为继承原父进程的文件创建掩码,有能屏蔽掉这两种权限。因此,把文件创建掩码设置为0,可以大大增强该守护进程的灵活性。
3)在子进程中创建新会话
会话—会话(Session)是一个或多个进程组的集合。 一个会话可以有⼀个控制终端。这通常是登陆到其上的终端设备(在终端登陆情况下)或伪终端设备(在网络登陆情况下)。建立与控制终端连接的会话进程被称为
控制进程。一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组
创建新会话是创建守护进程中最重要的一步,在这里使用的是系统函数setsid。
setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid仃三个作用让进程摆脱原会话的控制、
让进程摆脱原进程组的控制
让进程摆脱原控制终端的控制
#include <unistd.h>
pid_t setsid(void);
该函数调用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1
注:调用这个函数之前,要保证当前进程不是进程组的组长。
4)改变工作目录
由于子进程继承的是父进程的工作目录,在进程运行的过程中,当前目录所在的文件系统不能被卸载,这会造成一些麻烦,因此,把当前工作目录换成其他的路径,如“/”或“/tmp”等。改变工作目录的常见函数是chdir。
5)关闭文件描述符
用fork新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读或写,但它们一样消耗系统资源,可能导致所在的文件系统无法卸载
流程图
例:
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <sys/wait.h>
void mydaemon(){
pid_t pid=fork();
int i,j;
int fd;
const char* arr ="I am daemon\n";
umask(0);//主要用于在创建新文件或目录时屏蔽掉新文件或目录不应有的访问允许权限 作用:设置允许当前进程创建文件目录最大可操作的权限
//struct sigaction sa;
//关闭父进程
if(pid<0){
perror("fork");
}else if(pid>0){
exit(0);
}
setsid();//新建一个会话
//sa.sa_handler = SIG_IGN;// 忽略SIGCHLD信号
//sigemptyset(&sa.sa_mask);
//sa.sa_flags = 0;
//if( sigaction(SIGCHLD, &sa, NULL ) < 0 ){ // 注册⼦进程退出忽略信号
// return;
//}
chdir("/");//设定为根目录
if(chdir("/")<0){
printf("chdir error\n");
return;
}
//它是用来返回这个进程的文件描述表的项数,也就是说这个进程打开的文件的数
//目getdtablesize()
j=getdtablesize();
for(i=0;i< j;++i){
close(i);
}
while(1){
if(fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND, 0600)<0){
printf("open error\n");
exit(1);
}
write(fd,arr,strlen(arr)+1);
close(fd);
sleep(5);
}
}