【计算机网络学习之路】日志和守护进程

文章目录

  • 前言
  • 一. 日志介绍
  • 二. 简单日志
    • 1. 左字符串
    • 2. 右字符串
  • 三. 守护进程
    • 1. ps -axj命令
    • 2. 会话
      • 扩展命令
    • 3. 创建守护进程
  • 结束语

前言

本系列文章是计算机网络学习的笔记,欢迎大佬们阅读,纠错,分享相关知识。希望可以与你共同进步。

本篇博客介绍简单,较为基础的日志。

日志和守护进程都是辅助服务器的,一个是服务器的运行信息,一个是服务器的运行方式

一. 日志介绍

日志是记录事件,运行结果的工具
日志文件是重要的系统文件,其中记录了很多重要的系统运行的事件。包括用户的登录信息,系统的启动信息,系统的安全信息,各种服务相关信息
日志对于安全来说也很重要,它记录了每天系统发生的各种事情,通过日志来检查错误发送的原因,或受到攻击时攻击者留下的痕迹

日志管理服务

日志级别分为:

debug有调试信息的,日志通信最多
info一般信息日志,最常用
notic最具有重要性的普通条件的信息
warning警告级别
err错误级别,组织某个功能或者模块不能正常工作的信息
crit严重级别,阻止整个系统或者整个软件不能正常工作的信息
alert需要立刻修改的信息
emerg内核崩溃等重要信息
fatal致命错误
none什么都不记录

注意: 从上到下,级别从低到高,记录信息越来越少

二. 简单日志

本篇博客的日志是以函数的形式完成的,调用方式如下:

logMessage(Warning,"read error,%d,errno:%d",strerror(errno),errno);

参数有三个:日志级别,格式控制,可变参数

对应如下:

void logMessage(int level,const char*format,...){}

注意:format类型需要时const char*,因为大部分是以常量字符串的形式传参

我们期望最后的日志信息是这样的:[日志级别] [时间] [进程号] 消息内容(format)

可以将日志信息分成两部分,左字符串和右字符串,前三个为一组,消息内容使用vsnprintf

1. 左字符串

#pragma once#include<iostream>
#include<string>
#include<ctime>
#include<unistd.h>
#include<sys/types.h>
#include<stdarg.h>enum
{Debug = 0,Info,Warning,Error,Fatal,Uknown
};
//获取日志等级字符串
static std::string getLevelString(int level)
{switch (level){case Debug:return "Debug";case Info:return "Info";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "Uknown";}
}
//获取时间字符串
static std::string gettime()
{time_t cur=time(nullptr);struct tm* tmp=localtime(&cur);char buffer[128];snprintf(buffer,sizeof(buffer),"%d-%d-%d %d:%d:%d",tmp->tm_year+1,tmp->tm_mon+1,tmp->tm_mday+1,tmp->tm_hour,tmp->tm_min,tmp->tm_sec);return buffer;
}
//日志信息
void logMessage(int level,char*format,...)
{char logLeft[1024];std::string level_string=getLevelString(level);//日志级别std::string time_string=gettime();//时间std::string pid_string=std::to_string(getpid());//进程号//[日志级别] [时间] [进程号]snprintf(logLeft,sizeof(logLeft),"[%s] [%s] [%s] ",level_string.c_str(),time_string.c_str(),pid_string.c_str());
}

2. 右字符串

接下来介绍va系列——解析可变参数
在这里插入图片描述

在C语言学习中,函数的调用会创建栈帧,而函数传参会进行压栈,可变参数也是如此,所以可变参数的前一个参数的后一位就是可变参数的起始地址

  • va_list:类似指针,可以指向可变参数的起始和结束,可以遍历可变参数
  • va_start:将va_list定位到last后面
  • va_arg:将从va_list开始的数据,按照type类型进行提取返回
  • va_end:清空va_list
  • va_copy:将src拷贝给dest

而vsnprintf可以帮我们遍历可变参数,不需要我们自己控制

vsnprintf()

vsnprintf是一个标准的 C 函数,用于格式化字符串并将生成的字符存储在缓冲区中。它与函数类似,但有一个关键区别:函数不是直接采用可变长度的参数列表,而是采用参数,该参数是已使用宏初始化的参数列表。

以下是该函数的工作原理:

在这里插入图片描述

  • str:写入的缓冲区
  • size:缓冲区大小
  • format:格式控制
  • ap:va_list 指向可变参数的指针

使用如下:

const std::string filename="./log/tcpserver.log";void logMessage(int level,char*format,...)
{char logLeft[1024];std::string level_string=getLevelString(level);std::string time_string=gettime();std::string pid_string=std::to_string(getpid());snprintf(logLeft,sizeof(logLeft),"[%s] [%s] [%s] ",level_string.c_str(),time_string.c_str(),pid_string.c_str());char logRight[1024];va_list p;//类似指针va_start(p,format);//将p定位到可变参数首地址vsnprintf(logRight,sizeof(logRight),format,p);//按format格式将可变参数写入logRightva_end(p);//将va_list清理汇总两个字符串,打印//printf("%s%s\n",logLeft,logRight);//也可以输入到日志文件中进行持久化FILE *fp = fopen(filename.c_str(), "a");if(fp == nullptr)return;fprintf(fp,"%s%s\n", logLeft, logRight);fflush(fp); //可写也可以不写fclose(fp);
}

完整代码如下:
log.hpp

#pragma once#include<iostream>
#include<string>
#include<ctime>
#include<unistd.h>
#include<sys/types.h>
#include<stdarg.h>const std::string filename = "./log/tcpserver.log";enum
{Debug = 0,Info,Warning,Error,Fatal,Uknown
};static std::string getLevelString(int level)
{switch (level){case Debug:return "Debug";case Info:return "Info";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "Uknown";}
}static std::string gettime()
{time_t cur=time(nullptr);struct tm* tmp=localtime(&cur);char buffer[128];snprintf(buffer,sizeof(buffer),"%d-%d-%d %d:%d:%d",tmp->tm_year+1,tmp->tm_mon+1,tmp->tm_mday+1,tmp->tm_hour,tmp->tm_min,tmp->tm_sec);return buffer;
}//日志组成:日志等级 时间 PID 消息内容//日志等级 格式控制 可变参数
void logMessage(int level,char*format,...)
{char logLeft[1024];std::string level_string=getLevelString(level);std::string time_string=gettime();std::string pid_string=std::to_string(getpid());snprintf(logLeft,sizeof(logLeft),"[%s] [%s] [%s] ",level_string.c_str(),time_string.c_str(),pid_string.c_str());char logRight[1024];va_list p;//类似指针va_start(p,format);//将p定位到可变参数首地址vsnprintf(logRight,sizeof(logRight),format,p);//按format格式将可变参数写入logRightva_end(p);//将va_list清理打印日志信息//printf("%s%s\n",logLeft,logRight);// 保存到文件中FILE *fp = fopen(filename.c_str(), "a");if(fp == nullptr)return;fprintf(fp,"%s%s\n", logLeft, logRight);fflush(fp); //可写也可以不写fclose(fp);
}

三. 守护进程

服务器一方面需要24小时不间断运行,另一方面还需要不被其他程序所影响,更准确点,是避免被其他程序的任何终端所产生信息所打断。这就要求服务器要守护进程化

守护进程

守护进程,也就是通常说的 Daemon进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存周期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件

守护进程本质是孤儿进程,脱离终端。避免被任何终端所产生的信息所打断,其在执行过程的信息也不在任何终端上显示,一般是是使用日志文件持久化信息。
由于在Linux中,每一个系统与用户进行交流的界面被称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程就会自动关闭

Linux的终端文件路径一般在 /dev中
在这里插入图片描述

1. ps -axj命令

使用ps -axj命令查看进程

在这里插入图片描述

  • PPID:父进程进程ID
  • PID:进程ID
  • PGID:进程组ID
  • SID:会话ID
  • TTY:控制终端;?表示没有控制终端
  • TPGID:终端进程组ID
  • STAT:状态

    R:进程正在运行或在运行队列中等待
    S:进程处于休眠状态,等待某个条件的形成或接收到信号。S+代表前台运行s表示会话领导Ss表示既在休眠,又是会话领导
    D:进程不可中断状态,收到信号不唤醒和不可运行,进程必须等待直到有中断发生,通常是IO
    Z:进程已终止,但进程描述符存在,直到父进程调用pidwait()回收后释放。僵尸进程
    T:进程已停止,进程收到SIGSTOP,SIGSTP,SIGTIN,SIGTOU信号后停止运行
    X:已经死掉的进程

  • UID: 执行者身份
  • COMMAND:程序名/运行该程序的指令

2. 会话

在ps -axj中可以看到有进程组ID和会话ID。二者分别是什么呢

在这里插入图片描述

可以看到,一条命令的三个sleep形成了三个进程,进程号依次递增
PGID(进程组号)相同,和第一个进程ID相同;SID(会话ID)相同,终端文件相同,TPGID(终端进程组)=PGID

再起三个sleep任务

在这里插入图片描述

可以看到PGID不同,SID相同

  • 会话 >= 进程组 >= 进程
  • 会话关联一个终端文件
  • 进程组的组长,都是多个进程中的第一个

扩展命令

接下来介绍一些命令:

ctrl+z:将当前前台进程调到后台,并停止
在这里插入图片描述

jobs:查看当前会话的后台任务,会话的概念稍后讲解
jobs只能查看本会话的后台任务,无法查看其他会话的后台任务
在这里插入图片描述

fg+任务号:将后台程序调到前台运行
在这里插入图片描述

bg+任务号:让后台停止的任务开始运行

在这里插入图片描述


接下来回归会话的讲解

当我们通过Xshell连接云服务器时,我们登录成功,会为本次登录创建一个会话。每一次登录成功,都会创建会话。每个会话会关联一个终端文件。当我们退出时,其实只是将该会话资源回收
其中创建的进程组和内部进程,都是在当前会话中。因为一个会话只有一个控制终端,所以如果后台任务提到前台,老的前台任务就无法运行
一个会话只能有一个前台任务在运行

在这里插入图片描述

而退出会销毁会话,所以如果运行服务器的会话关闭,那么服务器也会停止运行。
所以,一般的网络服务器,为了不受到用户的登录注销等其他影响,网络服务器都需要以守护进程的方式进行

3. 创建守护进程

守护进程本质是自成会话的孤儿进程
而一个进程要想成为守护进程,需要满足以下几个要求:

  1. 不能是进程组组长。因为要调用函数独立出去,如果进程组组长,则会影响进程组的其他进程
  2. 需要忽略异常信号
  3. 读写错误输入输出需要特殊处理
  4. 进程的工作路径可能要改

新建会话的函数:setsid()
在这里插入图片描述
返回值:成功返回新的进程ID,失败返回-1并设置错误码

哪个进程调用这个函数,哪个进程的的资源就会被转移到这个自成会话的进程

代码如下:
daemon.hpp

#pragma once #include<cerrno>
#include<cstdlib>
#include<cstring>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>#include"log.hpp"void Daemon()
{//1.忽略异常信号signal(SIGPIPE,SIG_IGN);signal(SIGCHLD,SIG_IGN);//2.守护进程不能是进程组组长if(fork()>0)exit(0);//父进程退出//子进程不会是进程组组长//3.新建会话,自己成为会话的话首进程pid_t ret=setsid();if((int)ret==-1){logMessage(Fatal,"deamon error,%s,errno:%d",strerror(errno),errno);exit(1);}//4.可选,更改守护进程的工作路径//chdir("/");//5.处理后续的读写错误——文件描述符0,1,2int fd=open("/dev/null",O_RDWR);if(fd<0){logMessage(Fatal,"open null error,%s,errno:%d",strerror(errno),errno);exit(2);}//将0,1,2的内容输入到null文件dup2(fd,0);dup2(fd,1);dup2(fd,2);close(fd);
}

/dev/null是一个文件,像是一个黑洞一样,扔进去的数据不会显示,也从里面读不到数据
在这里插入图片描述
正好可以将读写错误——0,1,2文件的输入输出数据丢进这个黑洞文件中。

但如此,日志系统就不能以打印显示日志信息,而需要创建一个日志文件,将日志信息写入文件中进行持久化。如此也不会影响其他进程

结束语

本篇博客到此结束,感谢看到此处。
欢迎大家纠错和补充
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

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

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

相关文章

canvas扩展001:利用fabric绘制图形,可以平移,旋转,放缩

canvas实例应用100 专栏提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。 canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重要的帮助。 文章目录 示例…

串口通信基础知识介绍

一、串行通讯与并行通讯 在通信和计算机科学中&#xff0c;串行通信(Serial Communication)是一个通用概念&#xff0c;泛指所有的串行的通信协议&#xff0c;如RS232、RS422、RS485、USB、I2C、SPI等。 串行通讯是指仅用一根接收线和一根发送线就能将数据以位进行传输的一种…

JAVA时间常用操作工具类

小刘整理了JAVA中对时间的常用操作&#xff0c;封装了几种方法&#xff0c;简单方便&#xff0c;开箱即用。时间转字符串格式&#xff0c;字符串转时间&#xff0c;以及过去和未来的日期。除此之外&#xff0c;还新增了时间戳之差计算时分秒天的具体方案。 public static void …

如何使用nginx部署静态资源

Nginx可以作为静态web服务器来部署静态资源&#xff0c;这个静态资源是指在服务端真实存在&#xff0c;并且能够直接展示的一些文件数据&#xff0c;比如常见的静态资源有html页面、css文件、js文件、图片、视频、音频等资源相对于Tomcat服务器来说&#xff0c;Nginx处理静态资…

DGL在异构图上的GraphConv模块

回顾同构图GraphConv模块 首先回顾一下同构图中实现GraphConv的主要思路&#xff08;以GraphSAGE为例&#xff09;&#xff1a; 在初始化模块首先是获取源节点和目标节点的输入维度&#xff0c;同时获取输出的特征维度。根据SAGE论文提出的三种聚合操作&#xff0c;需要获取所…

蓝桥杯第四场双周赛(1~6)

1、水题 2、模拟题&#xff0c;写个函数即可 #define pb push_back #define x first #define y second #define int long long #define endl \n const LL maxn 4e057; const LL N 5e0510; const LL mod 1e097; const int inf 0x3f3f; const LL llinf 5e18;typedef pair…

vue3+ts 兄弟组件之间传值

父级&#xff1a; <template><div><!-- <A on-click"getFlag"></A><B :flag"Flag"></B> --><A></A><B></B></div> </template><script setup lang"ts"> i…

01、copilot+pycharm

之——free for student 目录 之——free for student 杂谈 正文 1.for student 2.pycharm 3.使用 杂谈 copilot是github推出的AI程序员&#xff0c;将chatgpt搬到了私人终端且无token限制&#xff0c;下面是使用方法。 GitHub Copilot 是由 GitHub 与 OpenAI 合作开发的…

2023年3月电子学会青少年软件编程 Python编程等级考试一级真题解析(判断题)

2023年3月Python编程等级考试一级真题解析 判断题(共10题,每题2分,共20分) 26、在Python编程中,print的功能是将print()小括号的内容输出到控制台,比如:在Python Shell中输入print(北京,你好)指令,小括号内容可以输出到控制台 答案:错 考点分析:考查python中print…

【【Linux编程介绍之关键配置和常用用法】】

Linux编程介绍之关键配置和常用用法 Hello World ! 我们所说的编写代码包括两部分&#xff1a;代码编写和编译&#xff0c;在Windows下可以使用Visual Studio来完成这两部&#xff0c;可以在 Visual Studio 下编写代码然后直接点击编译就可以了。但是在 Linux 下这两部分是分开…

2024年第十六届山东省职业院校技能大赛中职组 “网络安全”赛项竞赛正式卷任务书

2024年第十六届山东省职业院校技能大赛中职组 “网络安全”赛项竞赛正式卷任务书 2024年第十六届山东省职业院校技能大赛中职组 “网络安全”赛项竞赛正式卷A模块基础设施设置/安全加固&#xff08;200分&#xff09;A-1&#xff1a;登录安全加固&#xff08;Windows, Linux&am…

【Mybatis】预编译/即时sql 数据库连接池

回顾 Mybatis是一个持久层框架.有两种方式(这两种方式可以共存) 1.注解 2.xml 一.传递参数 以使用#{} 来接受参数为例 (以上两种方式一样适用的) 1)传递单个参数 #{} 可以为任意名称 2)多个参数 默认的参数名称就是接口方法声明的形参 3)参数为对象 默认给每个对象的每个属性都…

Linux内核中的overlay文件系统

一、简介 Docker 内核实现容器的功能用了linux 内核中的三个特性 Namespace、Cgroup、UnionFs&#xff0c;今天我们来说一下UnionFs。 linux UnionFs 实现的是overlay 文件系统 OverlayFs 文件系统分为三层&#xff0c; lower 是只读层 Upper 是可读写 Merged 是 lower 和U…

OD机考真题搜集:叠积木1

题目 有一堆长方体积木,它们的高度和宽度都相同,但长度不一。 小橙想把这堆积木叠成一面墙,墙的每层可以放一个积木,或将两个积木拼接起来,要求每层的长度相同。若必须用完这些积木,叠成的墙最多为多少层?如下是叠成的一面墙的图示,积木仅按宽和高所在的面进行拼接。 …

【数据结构】树与二叉树(廿六):树删除指定结点及其子树(算法DS)

文章目录 5.3.1 树的存储结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法1. 获取大儿子、大兄弟结点2. 搜索给定结点的父亲3. 搜索指定数据域的结点4. 删除结点及其左右子树a. 逻辑删除与物理删除b. 算法DSTc. 算法解析d. 代码实现递归释放树算法DS e. 算法测试 5. 代码整合…

PPT 遇到问题总结(修改页码统计)

PPT常见问题 1. 修改页码自动计数 1. 修改页码自动计数 点击 视图——>幻灯片母版——>下翻找到计数页直接修改——>关闭母版视图

vue+springboot读取git的markdown文件并展示

前言 最近&#xff0c;在研究一个如何将我们git项目的MARKDOWN文档获取到&#xff0c;并且可以展示到界面通过检索查到&#xff0c;于是经过几天的摸索&#xff0c;成功的研究了出来 本次前端vue使用的是Markdown-it Markdown-it 是一个用于解析和渲染 Markdown 标记语言的 …

Cache学习(3):Cache地址映射(直接映射缓存组相连缓存全相连缓存)

1 Cache的与存储地址的映射 以一个Cache Size 为 128 Bytes 并且Cache Line是 16 Bytes的Cache为例。首先把这个Cache想象成一个数组&#xff0c;数组总共8个元素&#xff0c;每个元素大小是 16 Bytes&#xff0c;如下图&#xff1a; 现在考虑一个问题&#xff0c;CPU从0x0654…

城市生命线丨桥梁结构健康监测系统的作用

在城市建设当中&#xff0c;有非常多的城市基本建设&#xff0c;建设当中&#xff0c;桥梁作为不可忽视的一环&#xff0c;也需要有很多桥梁建设的智能监测系统&#xff0c;在这个桥梁结构健康监测系统中&#xff0c;桥梁的各个数值都能被监测得到。 WITBEE万宾使用城市生命线智…

高并发内存池

1.什么是内存池 内存池动态内存分配与管理技术&#xff0c;对于程序员来说&#xff0c;通常情况下&#xff0c;动态申请内存需要使用new,delete,malloc,free这些API来申请&#xff0c;这样导致的后果是&#xff0c;当程序长时间运行之后&#xff0c;由于程序运行时所申请的内存…