什么是守护进程:
守护进程是运行在系统后台的一种特殊程序,独立于控制终端并且周期性的执行某种任务或者等待处理某些发生的事件。
守护进程也称为精灵进程,有以下特点:
- 自成进程组,自成会话,不受一般用户注销影响
- 在命名上一般以d结尾
- 其本身是一个孤儿进程
- 生命周期长,要7*24小时在线
- 一般的网络服务器必须以守护进程的形式存在
守护进程的创建:
创建守护进程的关键就是创建一个新的Session,并成为为Session Leader。
#include<unistd.h>
pid_t setsid(void);
注意:
setsid()函数会创建一个Session
如果调用setsid()的进程不是一个进程组的组长,那么函数会创建一个新的Session,此进程变成一个新进程组的组长进程,并且此进程会变成Session的首进程。
这个进程没有控制终端,如果在之前这个进程有控制终端,那么它会与控制终端的联系会被解除。如果该进程是一个组进程的组长,那么函数会出错返回。
守护进程的创建过程:
调用umask将文件模式创建屏蔽字设为0
该进程可能会从父进程继承文件创建模式,可能会改变守护进程所创建的文件的存取位,为了防止这样的情况发生,将文件模式创建屏蔽字设为0。
调用fork,父进程退出(exit)
如果守护进程的启动是以一条shell命令启动,那么父进程的终止可以让shell认为这条命令执行结束。
保证子进程不是一个进程组的组长进程。
调用setsid创建一个Session
当setsid调用成功时:
子进程会成为新会话的首进程;
子进程会成为一个进程组的组长进程;
子进程与原来的会话和进程组脱离;
子进程脱离控制终端。
将当前工作目录改为根目录
进程在活动时,其工作目录所在的文件系统不能卸下,一般需要将工作目录改变到根目录。比如说,在/mnt/mmc启动守护进程,但/mnt/mmc已经没有别的用途,你想将它卸载,此时不能成功。
关闭不在需要的文件描述符
进程会从其父进程那里继承到打开的文件描述符,如果不关闭可能会造成系统资源的浪费,造成其他的错误,所以应该对其进行关闭。
忽略SIGCHLD信号
该进程生成子进程的话,忽略SIGCHLD信号可以让其不等待子进程,让操作系统去回收子进程的资源,提高性能。
注意:
在创建守护进程时有人会fork两次,这样做的原因是什么呢?
第2次fork不是必须的。也看到很多开源服务没有fork第二次。fork第二次主要目的是。防止进程再次打开一个控制终端。因为打开一个控制终端的前台条件是该进程必须是会话首进程。再fork一次,子进程不是会话首进程。所以也无法打开新的控制终端。
创建一个守护进程的实例代码:
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
void mydaemon()
{
umask(0);//设置文件屏蔽字
pid_t pid;
if((pid = fork()) < 0)//创建子进程
{
printf("fork error\n");
return ;
}
else if(pid > 0)
{
exit(0);//结束父进程
}
setsid();//调用setsid函数
//子进程成为新会话首进程,子进程成为新进程组组长进程
if((pid = fork()) < 0)//再次创建子进程,此时的子进程不会在打开tty设备
{
printf("fork error\n");
return;
}
else if(pid != 0)
{
exit(1);//结束父进程
}
//将当前工作目录更改为根目录
if(chdir("/") < 0)
{
printf("child dir error\n");
return ;
}
//关闭文件描述符
close(0);
close(1);
close(2);
//忽略SIGCHLD信号
signal(SIGCHLD, SIG_IGN);
}
int main()
{
mydaemon();
while(1)
{
sleep(1);
}
return 0;
}
使用命令可以查看到当前的守护进程正在运行
利用库函数daemon()创建守护进程
#include<unistd.h>
int daemon(int nochdir, int noclose);
参数说明:
nochdir:=0,将当前目录更改为根目录
noclose:=0,将标准输入、输出和错误重定向到“/dev/null”
“/dev/null”:把/dev/null 看作"黑洞". 它非常等价于一个只写文件. 所有写入它的内容都会永远丢失, 而尝试从它那儿读取内容则什么也读不到。
利用daemon库函数实现一个守护进程:
#include<stdio.h>
#include<unistd.h>
int main()
{
daemon(0,0);
while(1);
}
查看这个守护进程:
我们可以进入/proc/pid目录查看进程号为pid的进程信息
可以发现其工作目录确实为根目录,我们可以打开其文件描述符表查看其文件描述符:
我们可以发现其确实指向了/dev/null