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

news2024/11/24 23:48:22

文章目录

  • 一、TCP服务器日志
  • 二、守护进程预备知识
    • 2.1 守护进程概念
    • 2.2 前台任务和后台任务
    • 2.3 进程组与组长ID
    • 2.4 前台进程后台进程的切换
    • 2.5 自成会话
  • 三、实现守护进程
    • 3.1 自建会话setsid
    • 3.2 守护进程的条件
    • 3.3 代码实现

一、TCP服务器日志

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

#pragma once

#include <iostream>
#include <string>
#include <cstdarg>
#include <time.h>
#include <ctime>
#include <unistd.h>
#include <cstdio>

#define DEBUG   0// 调试
#define NORMAL  1// 正常
#define WARNING 2// 警告
#define ERROR   3// 错误
#define FATAL   4// 致命错误

const char* 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 : return nullptr;
    }
}

void logMessage(int level, const char* format, ...)
{
    // [日志等级][时间][pid][信息]
    char logprefix[1024];
    time_t now;
    time(&now);
    struct tm *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)-1 and set errno to indicate the error.

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

3.2 守护进程的条件

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

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

如果希望执行某个命令,但又不希望在屏幕上显示出输出的结果,那么可以将输出重定向到/dev/null
/dev/null是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读取不到。
但是/dev/null文件非常的有用,将命令的输出重定向到它,会起到“静止输出”的效果。

3.3 代码实现

#pragma once

#include <unistd.h>
#include <signal.h>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "daemon.hpp"

void Daemon()
{
    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"

int main(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(new TCPServer(port));
    ptr->InitServer();
    Daemon();
    ptr->start();
    return 0;
}

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

// log.hpp
#pragma once

#include <iostream>
#include <string>
#include <cstdarg>
#include <time.h>
#include <ctime>
#include <unistd.h>
#include <cstdio>

#define DEBUG   0// 调试
#define NORMAL  1// 正常
#define WARNING 2// 警告
#define ERROR   3// 错误
#define FATAL   4// 致命错误

#define LOG_NOR "log.txt"
#define LOG_ERR "log.error"

const char* 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 : return nullptr;
    }
}

void logMessage(int level, const char* format, ...)
{
    // [日志等级][时间][pid][信息]
    char logprefix[1024];
    time_t now;
    time(&now);
    struct tm *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);
    }
}

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



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/579183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAP-MM-计算方案字段解析

01、 “步骤”&#xff1a;标识此条件类型在计算方案中的顺序编号&#xff0c;此编号会影响到后续业务中条件类型的排序&#xff0c;不同条件类型之间的编号最好间隔大一些&#xff0c;这样设置便于以后对计算方案进行扩展&#xff1b; 02、 “计数器”&#xff1…

(哈希表 ) 349. 两个数组的交集 ——【Leetcode每日一题】

❓349. 两个数组的交集 难度&#xff1a;简单 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,2,1], nums2 [2,2] 输出&#xff1a;[…

Hbase学习笔记

1 HBase介绍 (1) HBase是什么 HBase是一个开源的非关系型分布式、实时数据库(Nosql)&#xff0c;运行于HDFS文件系统之上&#xff0c;因此key容错地存储海量稀疏的数据。 海量稀疏就是说不能保证每一个key它的列都有value。 关系型数据库&#xff1a;mysql、oracle 非关系…

Midjourney AI绘画咒语与生成的作品(实例)

据说Midjourney出来后&#xff0c;有一大批设计师抱着电脑&#xff0c;哭晕了~~ 有兴趣的朋友&#xff0c;可以看一下我前两天发的&#xff0c;地址如下&#xff1a; Midjourney AI绘画中文教程详解&#xff08;完整版&#xff09;模型、命令、参数与各种高级用法https://blog…

马斯洛的的五层需求完美吗 不

马斯洛五层需求难道就很完美吗&#xff1f;不&#xff01; 我很佩服的复旦大学哲学王德峰教授批判 对马斯洛的自我实现&#xff0c;他认为 中国人没有西方意义上的自我实现 比如儒家成人达己、圆满、道问学和尊德性&#xff0c;比如孔子的学生颜回 比如佛家的渡人、觉悟。比如弘…

使用sklearn进行机器学习案例(1)

文章目录 案例一. 加州房价预测案例二. MNIST手写数字识别案例三. 波士顿房价预测 案例一. 加州房价预测 线性回归通过对训练集进行训练&#xff0c;拟合出一个线性方程&#xff0c;使得预测值与实际值之间的平均误差最小化。这个过程可以使用梯度下降法等优化算法来实现。即通…

使用curl命令传输数据

文章目录 一、curl命令二、举例和注意事项Reference 一、curl命令 curl是传输数据的命令行工具&#xff0c;可以通过命令行发送HTTP请求和接收HTTP响应。它的名字是“client for URLs”&#xff0c;意为URL的客户端&#xff0c;表示该工具主要用于处理URL相关的任务。curl可以…

Mysql数据库对表的基本操作

一.表基本操作 1.当前数据库内创建表 2.查看表 3.删除表 4.修改表结构 5.复制表&#xff08;结构&#xff09; 二.表约束创建 1.约束的作用 2.约束的类型 3.演示 一.表基本操作 1.当前数据库内创建表 CREATE TABLE 表名( 列名 列数据类型&#xff0c; 列名 列…

如何在华为OD机试中获得满分?Java实现【寻找关键钥匙】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

使用compose desktop构建桌面应用

使用compose desktop构建桌面应用时遇到的一些坑 官方指导移步&#xff1a;多平台开发说明&#xff0c;我这里只写一些自己遇到问题 我在mac机器开发&#xff0c;然后使用githubAction构建的win平台的包&#xff0c;现记录一下遇到的问题 构建文件build.gradle编写不同平台的程…

SAM-MM-配额协议字段解析

1、有效起始日&#xff1a;配额协议的生效日期&#xff0c;系统默认为当前创建日期&#xff0c;灰色&#xff0c;不可更改&#xff1b; 2、有效至&#xff1a;配额协议的失效日期&#xff0c;要手动填一个日期&#xff1b; 3、最小数量拆分&#xff1a;如果要按照配额比例&am…

.NET面向AI编程——SK框架(SemanticKernel)的简易入门实践

前言&#xff1a;随着ChatGPT开始在各个领域遍地开花&#xff0c;有关的应用也开始层出不穷。随着微软开源SK框架&#xff0c;无疑是给.NET开发者带来面向AI应用的新机遇。以下内容&#xff0c;通过创建一个简单的prompt开始&#xff0c;开发一个使用SK框架进行开发的入门教程&…

xss跨站之waf绕过及安全修复(28)

手工探针xss绕过waf规则 打开靶场 写入跨站测试语句发现拦截 这里就做一个最经典的方法&#xff0c;做一个关键字拆分来确立拦截的是什么东西。 去掉最后字符串&#xff0c;访问还是拦截&#xff0c;再去掉alert&#xff08;1&#xff09;&#xff0c;访问还是拦截&#xff…

八大排序-直接插入排序、希尔排序、直接选择排序、冒泡排序、堆排序、快速排序、归并排序、基数排序(上)

目录 前言 直接插入排序&#xff08;Insertion Sort&#xff09; 一、概念及其介绍 二、过程图示 三、代码 四、复杂度 希尔排序&#xff08;Shell Sort&#xff09; 一、概念 二、实现思路 三、图示过程 四、代码 4.1代码 4.2运行结果 4.3解释 五、复杂度 堆排…

华为诺亚实验室VanillaNet学习笔记

华为诺亚实验室VanillaNet学习笔记 VanillaNet: the Power of Minimalism in Deep Learning Abstract 基础模型的核心理念是“多而不同”&#xff0c;计算机视觉和自然语言处理领域的惊人成功就是例证。然而&#xff0c;优化的挑战和变压器模型固有的复杂性要求范式向简单性…

Linux:finalshell虚拟机与真实机的上传和下载;rz、sz

使用命令rz上传的速度会比拖拽上传的速度慢

Day3:Windows网络编程-UDP

对比TCP与UDP的通信区别 UDP Server没有listen()和accept() TCP Server #include <iostream> #include <WinSock2.h> // 包含网络库 #pragma comment(lib,"ws2_32.lib") using namespace std;int main() {// 1. 初始化套接字 初始化套接字库cout <&l…

Qt编写视频监控系统76-Onvif跨网段组播搜索和单播搜索的实现

一、前言 在视频监控行业一般会用国际onvif工具来测试设备是否支持onvif协议&#xff0c;工具的名字叫ONVIF Device Manager&#xff08;还有个工具叫ONVIF Device Test Tool&#xff0c;专用于程序员测试各种数据交互&#xff09;&#xff0c;可以自行搜索下载&#xff0c;此…

linux网桥简单理解和持久化配置

文章目录 前言一、Linux 网桥是什么&#xff1f;二、网桥主要作用三、网桥配置命令及安装(CentOS系统)1 网桥配置命令2.持久化网桥配置 前言 linux bridge是网络虚拟化中非常重要的一种设备&#xff0c;今天就来学习下linux bridge的相关知识 一、Linux 网桥是什么&#xff1…

Allegro操作规范

光绘输出操作规范 1.1添加钻孔表 添加钻孔表的具体步骤为: 1.通过屏幕右边的Visibility选项的Views列表,将Drill层打开 2.将Visibility选项中的PIN和Via选项都选中,见下图所示: 1.2添加钻孔文件 参数设好之后关闭NC Drill/Parameters窗口,输出数控机床钻孔文件的命…