linux之守护进程
什么是守护进程?
这是我们自己的编写的一个TCP服务器
==当我们关闭终端的掉服务器终端的时候==
可以看到服务器的进程没了!这有问题吗?——==这很有问题!因为服务器竟然受到用户的登陆和退出的影响!==服务器的运行方式应该是==不会受到用户的登陆和注销的影响!==服务器应该要一直运行下去!==除非我们自己要手动的kill掉这个服务器!==
==这种不受用户登陆和注销影响的进程我们就称之为守护进程!==
是会话,作业与进程组的概念——如何让进程后台运行/后台变前台/如何查看作业/暂停任务/让暂停的任务重新启动/
==在此之前我们先要了解一个概念——对话!==
会话
==这个对话这样说起来也是很抽象的!那么它究竟是什么呢?==
作业
守护进程化的原理与daemon函数实现
这个daemon函数就是操作系统为我们提供的,进程守护进程化的函数!
daemon函数的实现
//daemon.hpp
#pragma once
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
#include<cassert>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define DEV "/dev/null"
void daemonSelf(const char* CurrPath = nullptr)
{
//1.让调用该函数的进程,屏蔽掉异常的信号!
//什么叫做异常的信号呢?——比如说客户端给服务端发送一个消息!
//服务端请求完这个消息正准备响应会去,这时候客户端崩溃了!那么这时候相当于给一个已经关闭的文件描述符进行写入!
//那么就像是管道读写一样!如果读端关闭!写端存在!那么操作系统就会发送SIGPIPE信号杀死写端!这里也是同样如此!
//所以我们要忽略掉一些系统信号!
signal(SIGPIPE,SIG_IGN);//忽略SIGPIPE,防止错误写入!
//2.我们要调用setsid!但是setsid是不能直接调用的!操作系统不允许!直接调用会报错!
//为什么呢?——为的就是防止组长调用setsid!
//所以我们要如何让自己不是组长!
//一般在一个进程里面谁早谁就是组长!
if(fork()>0) exit(0);//父进程直接退出!子进程变成孤儿进程
//走到这里的肯定是子进程!——子进程就一定不是组组长!
//守护进程,又叫做精灵进程——是孤儿进程的一种!
pid_t n = setsid();
assert(n != -1);
//3.守护进程是脱离终端的!所以我们要关闭或重定向以前默认打开的文件!
//因为是脱离终端的是不需要关心键盘或者显示器上的任何时事件所以012号的描述符应该关闭或者重定向!
//此时只有用网络端口才能访问到这个进程!
//我们最好对012的文件描述符进行重定向!而不是关闭!
//万一我们有些日志没有处理干净是往显示器上打印的怎么办?对一个已经关闭的文件描述符写入,会立马报错导致进程挂掉了!
//在linux下我们可以向/dev/null这个文件进行重定向!
//这个文件像是一个黑洞,默认处理数据的方式就是凡是写入的都抛弃
//我们读取的时候,即不阻塞,但是也什么都没有!
int fd = open(DEV,O_RDWR);
if(fd >= 0)
{
dup2(fd,0);
dup2(fd,1);
dup2(fd,2);
close(fd);//以后就不需要用了
}
else
{
close(0);
close(1);
close(2);
}
//4.可选,进程的执行路径是否更改!——即修改当前进程的当前路径
if(CurrPath) chdir(CurrPath);
}
#include"tcpServer.hpp"
#include<memory>
#include"daemon.hpp"
using namespace server;
using namespace std;
static void usage(std::string proc)
{
std::cout << "\nUsage:\n\t" << proc << " local_port\n\t\n";
}
int main(int argc,char* argv[])
{
if(argc != 2)
{
usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = atoi(argv[1]);//将字符串转换为整数
unique_ptr<TcpServer> tsvr(new TcpServer());
tsvr->initServer();
daemonSelf();//调用守护进程的逻辑就是先daemon,守护进程化
//然后执行服务器的核心逻辑!
tsvr->start();
return 0;
}
==这就是这个服务器进程就会,自成进程组组长,自成会话!——前提这个进程不是组长,它才能成为组长==
==当我们关闭我们的启动服务器的终端!==
==我们可以发现仍然是可以运行的!——我们这就完成了服务器的守护进程化!==