我们接下来说一下关于守护进程。
守护进程也叫做精灵进程,是运行在后台的一种特殊的进程,它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。生存周期长,它们常常在系统引导装入的时候启动,仅仅在系统关闭的时候才终止。因为它们没有控制终端,所以说它们是在后台运行的。很多服务器的应用都和守护进程相关。
守护进程的特征:
通常来说,我们需要知道,对于当我们开机以后首先操作系统首先通过0号进程去进行操作,然后,这个时候内核会在引导的时候装入启动用户的命令。内核进程是非常特殊的,内核进程以root权限运行,无控制终端,无命令行。
1号进程是守护进程,init进程,它是一个系统的守护进程,它主要去负责系统启动的各个运行层次的系统的服务。这些服务都是通常在它们自己拥有的守护进程的帮助下实现的。
系统当中通常的守护进程有:
- cron守护进程,作业规划进程
- flush守护进程,在可用内存达到设置的最小阈值冲洗至磁盘
- kswapd守护进程,内存换页守护进程。
- jdb守护进程,帮助实现ext4文件系统的日志功能
守护进程的特点:
守护进程启动后没有控制终端,不能直接和用户交互。
守护进程不受用户登录注销的影响。只受开机关机的影响。
守护进程存在的原因是因为控制终端由于某些原因会发送一些信号的原因。
守护进程的规则
- 首先调用umask修改屏蔽字为0。
- 调用fork,然后退出父进程,这样就可以让新创建的子进程成为一个孤儿进程,被1号进程领养,这样就保证了子进程独立成一个会话。
- 调用setsid创建一个新的会话。这样当前的子进程就成为了新会话的首进程,成为了新的进程组的组长进程,并且无控制终端。
- 将当前的工作目录改为根目录
- 关闭不需要的文件描述符,关闭从父进程哪里继承来的任何的文件描述符。
- 开始执行守护进程
守护进程实现
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<signal.h>
void mydaemon()
{
umask(0);
if(fork()>0)
{
exit(2);
}
setsid();
signal(SIGCHLD,SIG_IGN);
chdir("/");
close(0);
close(1);
close(2);
}
int main()
{
mydaemon();
while(1)
{
}
return 0;
}
从中我们看到,所创建的PID(进程号) PGID(进程组号) SID(会话号)三个号是一样的,TPGID(控制tty进程组ID)是-1。
守护进程的两次fork()
在有些的我们守护进程版本下,有些人建议两次进行fork,终止父进程,继续使用子进程当中的守护进程,这就保证了该守护进程不是会话首进程。
fork()两次的原因:
第一次fork是因为调用setsid函数的进程不能是进程组组长,fork出来的父进程一般就是进程组的组长,所以父进程是无法调用setsid创建会话的。然后结束了父进程,这样,也就使得子进程成为了一个独立的会话,当父进程结束以后,子进程就成为了一个孤儿进程,所以被1号进程领养,所以也就脱离了控制终端。
第二次fork的作用是为子进程fork()出一个子进程,第二次fork的原因是为了防止进程再次打开一个控制终端,因为当前的进程是一个会话的会话组长,所以是可以去打开控制终端的前台的,如果再fork()一次,子进程ID!=sid。所以也就无法打开新的控制终端。子进程的子进程做相关的守护进程的操作。所以这个子进程的子进程现在是当前会话的非控制进程,所以它是无法进行创建控制进程。这样也就没了什么影响。
两次fork的代码:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<signal.h>
void mydaemon()
{
//设置掩码
umask(0);
//父进程进行退出
if(fork()>0)
{
exit(2);
}
//创建新的会话
setsid();
//捕捉SIGCHLD信号,操作为忽略
signal(SIGCHLD,SIG_IGN);
//进行第二次fork(),为了防止新的紫禁城再次进行调用控制终端
if(fork()>0)
{
exit(2);
}
//改变文件目录
chdir("/");
//关闭文件描述符
close(0);
close(1);
close(2);
}
int main()
{
mydaemon();
while(1)
{
}
return 0;
}
新创建的子进程的子进程,所以PID是不一样的,依然是被1号进程领养的进程,自成会话,避免了再次控制终端。