什么是守护进程
守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性的执行某种任务或等待处理某些发送的事件。Linux上的大多数服务器就是用守护进程实现的。
我们可以看到在系统启动默认的守护进程,他们的父进程ID全是init;比如:我们比较熟悉的sshd,远程连接的守护进程;我在前面的写道的crond就是作业规划进程;
Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。其他进程启东市都是在用户登录或运行程序是创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销影响,他们一直运行着。这种进程就叫做守护进程(精灵进程)(Deamon)。
守护进程有个特征就是在程序运行名后面多了一个d;但是不是所有的尾字母是d的就是守护进程;
如何创建一个守护进程
在创建一个守护进程的最关键的步骤是调用setsid函数双肩一个新的会话,并成为会话组长;
#include<unistd.h>
pid_t setsid(void);
该函数调用成功,返回新创建的会话ID,出错返回-1;调用这个函数之前,当前进程不允许是进程组的组长,否则就出错;所以我们在调用setsid()函数之前,必须先fork()创建父子进程,父子进程在同一个进程组中,父进程是组长进程,所以子进程符合条件,将父进程退出即可满足要求;
成功调用该函数的结果是:
1、创建一个新的会话,当前进程成为会话组长,当前进程的id就是会话id;
2、创建一个新的进程组,当前进程成为进程组的组长,当前进程的ID就是进程组的ID;
3、如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然打开,仍然可以读写,但只是一个普通的打开文件额不是控制终端了。
创建守护进程的步骤
1、调用umask()将文件模式创建屏蔽字设置为0;
2、调用fork,父进程退出。//1、保证守护进程不是进程组长
3、调用setsid创建一个会话。//setsid会导致:1、调用进程成为新会话的首进程,2、调用进程成为一个进程组的组长进程,3、调用进程没有控制终端。
4、将当前工作目录更改我根目录。//系统中除了跟目录都可以被删除
5、关闭不在需要的文件描述符
6、忽略SIGCHLD信号;
自己创建守护进程:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
void mydaemon() //只fork()一次
{
umask(0); //用umask将文件模式创建为屏蔽字为0
if(fork() == 0) //fork子进程,关闭父进程
{}
else
{
exit(0);
}
setsid(); //让子进程成为一个守护进程
chdir("/"); //更改当前目录为根目录
close(0); //关闭默认的文件描述符表
close(1);
close(2);
signal(SIGCHLD, SIG_DFL); //忽略SIGCHLD信号
}
int main()
{
mydaemon();
// daemon(0, 0); //系统自己定义的守护进程,没有关闭它的默认的文件描述符表,两个参数(默认更改路径到'/', 不关闭文件描述符表)
while(1)
{}
return 0;
}
可以看到PPID为1,因为我们把父进程进程直接退出,它为孤儿进程被init接受,它没有终端,TPGID为-1;
为什么有人fork两次
我们在了解为什么fork两次,先看看第一次和此二次fork都起到什么作用:
(1)调用一次fork的作用:
第一次fork()的作用是让shell认为这条命令已经终止,不用挂在终端输入上,还有就是为了后面的setsid服务,应为调用setsid函数的进程不能是组长进程,当子进程调用完setsid函数之后,子进程是会话组长也是进程组组长,并且脱离了控制终端,此时,不管控制终端如何操作,新的进程都不会受到一些信号使得进程退出;
(2)第二次fork()的作用:
虽然当前关闭了和终端的联系,但是后期可能会误操作打开了终端。只有会话首进程能打开终端设备,也就是再fork一次,再把父进程退出,再次fork的子进程必须作为守护进程继续运行,保证了该守护进程不是对话期的首进程。
第二次fork不是必须的,是可选的:
void mydaemon()
{
umask(0);
if(fork() == 0)
{}
else
{
exit(0);
}
setsid();
if(fork() < 0)
{
return;
}
else if(fork() != 0)
{
exit(0);
}
chdir("/");
close(0);
close(1);
close(2);
signal(SIGCHLD, SIG_IGN);
}
现在就回有两个守护进程,一个时第一次fork的,一个时第二次fork的,可以看出他们在同一个进程组中,同一个会话组中。