守护进程
守护进程是一种特殊的孤儿进程,父进程是一号init进程,运行在后台,与终端和登陆会话脱离关系,不受影响。
守护进程通常系统引导的时候启动,并且一直运行直到系统关闭。另一些只在需要的时候才启动,完成任务后就自动结束。
例如这些TPGID为-1的都是守护进程
通常我们的服务器程序需要以守护进程的方式来进行运行,使其能够后台化。
守护进程的创建
在Linux为我们提供了daemon()帮助我们创建守护进程
#include <unistd.h>
int daemon(int nochdir, int noclose);
nochdir参数用于指定是否改变当前当前的工作目录,如果传递参数0,则说明将工作目录切换到根目录"/"下,否则继续使用当前的工作目录。
noclose参数用于指定是否关闭标准输入、标准输出、标准错误。如果参数为0,则将三者全部重定向到/dev/null文件中,否则继续使用原来的设备。
那么,它是如何实现的呢?
守护进程的实现
要想使得进程能够脱离终端、运作在后台,就必须保证守护进程与其创建之前的环境分隔开,包括他从父进程继承来的权限掩码,工作目录,未关闭的文件描述符,进程组,会话,控制终端等,所以我们要做的就是摆脱这些束缚,使我们能够独立于原先的环境。
所以需要完成以下步骤
- 创建子进程,关闭父进程,使当前进程成为孤儿进程
- 创建新的会话,成为会话首进程
- 屏蔽SIGHUP信号(防止因为脱离终端时被结束进程)
- 再次创建子进程,关闭父进程,使子进程不是会话首进程,无控制终端的权限
- 设置权限掩码
- 切换工作目录到根目录下
- 关闭标准输入、标准输出、标准错误设备
- 关闭其他已经打开的文件描述符
- 将标准输入、标准输出、标准错误重定向至/dev/null文件中
代码实现如下
#ifndef __DAEMON_H__
#define __DAEMON_H__
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/stat.h>
bool daemonsize()
{
//创建子进程,关闭父进程,使子进程成为孤儿进程,由一号init进程收养后运作在后台
pid_t pid = fork();
//子进程创建失败
if(pid < 0)
{
return false;
}
//关闭父进程
else if(pid > 0)
{
exit(0);
}
//创建新的会话,该会话只包含子进程,并且子进程为会话首进程
pid_t sid = setsid();
if(sid < 0)
{
return false;
}
//忽略SIGHUP信号,否则在下面关闭父进程的时候会因为脱离终端而收到该信号导致进程退出
signal(SIGHUP, SIG_IGN);
//再次创建子进程,关闭父进程。使得当前进程不再是会话首进程,无控制终端的权限
if(pid < 0)
{
return false;
}
//关闭父进程
else if(pid > 0)
{
exit(0);
}
//设置权限掩码
umask(0);
//将工作目录切换到根目录下
if(chdir("/") < 0)
{
return false;
}
/* 关闭掉多余的文件描述符 */
//关闭标准输入,标准输出,标准错误
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
//将标准输入,标准输出,标准错误重定向到/dev/null文件中
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
return true;
}
#endif /* __DAEMON_H__ */