前言:
1.了解守护进程
2.守护进程的特点
3.如何创建守护进程
4.创建守护进程时fork一次与fork两次的区别
初识守护进程
守护进程也称为精灵进程(Daemon),是运行在后台的一种特殊进程(孤儿进程)。
守护进程独立于控制终端并且周期性地执行某些任务或等待处理某些发生的事件。
一般的进程都会在用户登录时或运行程序时创建,在用户注销或程序运行结束时终止。但是守护进程不受用户登录或注销的影响。它们会一直在运行。
守护进程的特点:
1.独立于控制终端
2.父进程为1(init),所以守护进程是一个孤儿进程
3.守护进程自成进程组
4.守护进程自成一个会话并成为该会话的话首进程(会话首进程拥有打开控制终端的能力),或者成为一个会话中的某个进程组。
5.守护进程不受用户登录和注销的影响。
创建守护进程
最重要的是要调用一个setsid函数,调用该函数成功的前提条件是调用该函数的进程不是一个进程组的组长,所以就需要调用fork函数来创建一个子进程,让该子进程来调用setsid,此时的父进程即是该进程的组长,因为守护进程是一个孤儿进程,所以父进程需要提前退出。
setsid调用成功后,会有如下的变化:
1.创建一个新的会话,当前进程就成为该会话的会话首进程,该会话的ID就是当前进程的ID。
2.创建一个新的进程组,当前进程成为该进程组的组长,该进程的ID就是该进程组的ID。
3.该子进程和控制终端去关联。
创建守护进程的步骤:
1.调用umask将屏蔽字清0;
2.调用fork创建子进程,然后将父进程退出(①保证子进程成为一个孤儿进程 ②保证子进程不是父进程的组长 )。
3.创建一个新会话。(调用setsid)
调用进程成为会话的话首进程;
调用进程成为进程组的组长进程。
4.调用进程和控制终端去关联
5.将当前的工作目录更改为根目录(以防绝对路径的工作目录下中间的某个目录被删除,就会导致该目录失效)
6.关闭不在需要的文件描述符(0。1。2);
7.忽略SIDCHLD信号。
创建一个守护进程:
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<signal.h>
void mydaemon()
{
umask(0);//将umask清0,防止后面新创建的文件的权限收到该umask的影响
if(fork() > 0)
{
//father
exit(1);//父进程退出,保证子进程成为一个后台进程且不是该进程组的组长
}
//child
setsid();//创建一个新的会话
chdir("/");//将工作目录更改为根目录,防止受到被删除目录的影响
close(0);
close(1);
close(2);//与终端设备去关联
signal(SIGCHLD,SIG_IGN);//忽略掉SIGCHILD信号
}
int main()
{
mydaemon();
while(1);
return 0;
}
查看结果:
从上图可以看出:
创建的两个mydaemon都是守护进程,而且由于他们两个的PPID. PID. SID都相同,所以这两个守护进程都是自成会话,并成为会话的会话首进程,而且从PPID(父进程)为1可以看出,这两个子进程都是孤儿进程,从TTY都是?可以看出都和终端没关联,最后,从TPGID可以看出,他们就是守护进程。
创建守护进程的另一种方法:
参数:
当 nochdir为零时,当前目录变为根目录,否则不变;
当 noclose为零时,标准输入、标准输出和错误输出重导向为/dev/null,也就是不输出任何信 息,否则照样输出。
返回值:
deamon()调用了fork(),如果fork成功,那么父进程就调用_exit(2)退出,所以看到的错误信息 全部是子进程产生的。如果成功函数返回0,否则返回-1并设置errno。
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main()
{
//mydaemon();
daemon(0,0);
while(1);
return 0;
}
创建守护进程时fork一次与两次有何不同:
创建守护进程时fork一次:
创建一个守护进程就需要调用setsid函数,而这个函数要调用成功的前提条件是调用该函数的进程不能是一个进程组的组长,也就是说不能是一个进程组中的第一个进程,那么我们就很容易想到,可以fork一个子进程,此时的父进程肯定会使该进程组的组长,这样就保证了子进程不是进程组的组长,那么子进程就可以调用setsid函数,不要忘记exit掉父进程哦,这样子进程才可以成为孤儿进程,被init进程领养,,失去和控制终端的关联。但是该孤儿进程自成会话,而且是会话的会话首进程,会话首进程又拥有打开控制终端的能力。
第二次fork的原因:
fork两次,其实是让子进程再去调用fork创建子进程的子进程,该子进程就与上面所创建的第一个子进程在一个进程组内,该子进程一定不会是会话的会话首进程,也就避免了拥有打开控制终端能力这种情况的发生。
fork两次:
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<signal.h>
void mydaemon()
{
umask(0);//将umask清0,防止后面新创建的文件的权限收到该umask的影响
if(fork() > 0)
{
//father
exit(1);//父进程退出,保证子进程成为一个后台进程且不是该进程组的组长
}
//child
setsid();//创建一个新的会话
//fork第二次是为了防止第二次创建的守护进程具有打开控制终端的能力
if(fork() > 0)
{
exit(2);//父进程退出
}
chdir("/");//将工作目录更改为根目录,防止受到被删除目录的影响
close(0);
close(1);
close(2);//与终端设备去关联
signal(SIGCHLD,SIG_IGN);//忽略掉SIGCHILD信号
}
int main()
{
mydaemon();
// daemon(0,0);
while(1);
return 0;
}
观察结果: