0


【linux】守护进程(精灵进程)

文章目录

一、TCP服务器日志

上一章我们写了一个TCP网络服务器【网络编程】demo版TCP网络服务器实现。
为了方便观察到每一步我们可以封装一个日志:

#pragmaonce#include<iostream>#include<string>#include<cstdarg>#include<time.h>#include<ctime>#include<unistd.h>#include<cstdio>#defineDEBUG0// 调试#defineNORMAL1// 正常#defineWARNING2// 警告#defineERROR3// 错误#defineFATAL4// 致命错误constchar*to_string_level(int level){switch(level){case DEBUG:return"DEBUG";case NORMAL:return"NORMAL";case WARNING:return"WARNING";case ERROR:return"ERROR";case FATAL:return"FATAL";default:returnnullptr;}}voidlogMessage(int level,constchar* format,...){// [日志等级][时间][pid][信息]char logprefix[1024];
    time_t now;time(&now);structtm*ptm =localtime(&now);char timebuf[1024];snprintf(timebuf,sizeof timebuf,"%d年%d月%d日 %d:%d:%d", ptm->tm_year +1900, ptm->tm_mon +1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);snprintf(logprefix,sizeof logprefix,"[%s][%s][pid: %d]",to_string_level(level), timebuf,getpid());char logline[1024];
    va_list arg;va_start(arg, format);vsnprintf(logline,sizeof logline, format, arg);
    std::cout << logprefix << logline << std::endl;}

运行结果:
在这里插入图片描述

二、守护进程预备知识

2.1 守护进程概念

但是我们发现这个服务器存在问题,但是我们如果把云服务器关掉的话服务器就自动退出了

服务器肯定不是这样运行的,服务器应该是启动后不再受用户的登录和注销的影响。除非我们不想用这个服务器了,把它kill掉。
我们把这种进程就叫做守护进程

2.2 前台任务和后台任务

当Xhell连接云服务器时,Linux服务器会提供一个

bash

(命令行解释),我们可以启动一系列前台和后台任务。我们把所有的前台后台任务叫做会话。
在这里插入图片描述
这里的bash就是前台任务,一个会话允许存在一个前台任务和多个(或0个)后台任务。

在命令的后边加上

&

符号就可以把这个进程运行在后台。
在这里插入图片描述
在这里插入图片描述
jobs命令用于显示Linux中的任务列表及任务状态,包括后台运行的任务。
在这里插入图片描述

我们把红色框框的部分称作作业,最左边的序号就是作业号

2.3 进程组与组长ID

创建一批后台进程:
在这里插入图片描述
可以看到PGID相同的就属于同一个进程组。而PID和PGID相同的就是组长进程

同一个组的成员共同完成一个作业。

这里的SID就是会话ID。所有的进程属于同一个会话。其实就是bash的ID。

2.4 前台进程后台进程的切换

**使用

fg + 作业号

就可以把一个作业放在前台。如果再

[Ctrl] + z

暂停就会又回到后台**
**

bg

命令用于将作业放到后台运行,使前台可以执行其他任务。该命令的运行效果与在运行命令后面添加符号 & 的效果是相同的,都是将其放到系统后台执行。**

使用这些命令就可以让进程进行前后台切换。

综上可得知前台只能允许一个进程运行,当我们把一个作业切换到前台,此时的前台进程就会被自动切换到后台。

而如果我们关闭Xshell,全部的进程都会被清理掉(会受到用户登录和注销的影响)。
如果不想受到影响就不能放到前台或者后台,而是要自成会话。

2.5 自成会话

在这里插入图片描述
当一个进程自成会话了,就不会受到终端设备(Xshell)的影响了。

三、实现守护进程

3.1 自建会话setsid

#include<unistd.h>

pid_t setsid(void);

RETURN VALUE
Upon  successful  completion,setsid()  shall  return  the value of the new process group ID of the calling process.  
Otherwise, it shall return(pid_t)-1and set errno to indicate the error.

这个函数的作用就是新建一个会话,如果只有一个进程,那么组长就是自己。
这里有一个要求,调用该函数的进程不能是组长

3.2 守护进程的条件

1️⃣ 要让进程忽略掉异常信号,因为客户端关闭的时候我们正在写就会发送异常导致服务端退出。
2️⃣ 必须让进程不是组长。
3️⃣ 守护进程必须脱离终端,所以要关闭或者重定向以前进程默认打开的文件(0, 1, 2)。

这里不建议直接关闭文件描述符,因为有可能会导致写入不存在的文件(显示器)导致报错。我们可以重定向到

/dev/null

如果希望执行某个命令,但又不希望在屏幕上显示出输出的结果,那么可以将输出重定向到

/dev/null

/dev/null

是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读取不到。
但是

/dev/null

文件非常的有用,将命令的输出重定向到它,会起到“静止输出”的效果。

3.3 代码实现

#pragmaonce#include<unistd.h>#include<signal.h>#include<cassert>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include"daemon.hpp"voidDaemon(){signal(SIGPIPE, SIG_IGN);// 保证不是组长if(fork()>0)exit(1);// 子进程
    pid_t n =setsid();assert(n !=-1);int fd =open("/dev/null", O_RDWR);if(fd <0){// 无奈之举close(0);close(1);close(2);}else{dup2(fd,0);dup2(fd,1);dup2(fd,2);}}// TCPServeer.cc#include"TCPServer.hpp"#include<memory>#include"daemon.hpp"intmain(int argc,char*argv[]){if(argc !=2){
        std::cout <<"incorrect number of parameters"<< std::endl;exit(1);}uint16_t port =atoi(argv[1]);
    std::unique_ptr<TCPServer>ptr(newTCPServer(port));
    ptr->InitServer();Daemon();
    ptr->start();return0;}

在这里插入图片描述
而为了获取服务器的日志信息,我们可以重定向到文件中。

// log.hpp#pragmaonce#include<iostream>#include<string>#include<cstdarg>#include<time.h>#include<ctime>#include<unistd.h>#include<cstdio>#defineDEBUG0// 调试#defineNORMAL1// 正常#defineWARNING2// 警告#defineERROR3// 错误#defineFATAL4// 致命错误#defineLOG_NOR"log.txt"#defineLOG_ERR"log.error"constchar*to_string_level(int level){switch(level){case DEBUG:return"DEBUG";case NORMAL:return"NORMAL";case WARNING:return"WARNING";case ERROR:return"ERROR";case FATAL:return"FATAL";default:returnnullptr;}}voidlogMessage(int level,constchar* format,...){// [日志等级][时间][pid][信息]char logprefix[1024];
    time_t now;time(&now);structtm*ptm =localtime(&now);char timebuf[1024];snprintf(timebuf,sizeof timebuf,"%d年%d月%d日 %d:%d:%d", ptm->tm_year +1900, ptm->tm_mon +1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);snprintf(logprefix,sizeof logprefix,"[%s][%s][pid: %d]",to_string_level(level), timebuf,getpid());char logline[1024];
    va_list arg;va_start(arg, format);vsnprintf(logline,sizeof logline, format, arg);// std::cout << logprefix << logline << std::endl;
    FILE *nor =fopen(LOG_NOR,"a");
    FILE *err =fopen(LOG_ERR,"a");if(nor && err){if(level == DEBUG || level == NORMAL || level == WARNING){fprintf(nor,"%s%s\n", logprefix, logline);}else{fprintf(err,"%s%s\n", logprefix, logline);}fclose(nor);fclose(err);}}

客户端:
在这里插入图片描述
服务端:
在这里插入图片描述



标签: linux 服务器 运维

本文转载自: https://blog.csdn.net/qq_66314292/article/details/130904791
版权归原作者 命由己造~ 所有, 如有侵权,请联系我们删除。

“【linux】守护进程(精灵进程)”的评论:

还没有评论