【计算机网络学习之路】日志和守护进程
文章目录
- 前言
- 一. 日志介绍
- 二. 简单日志
- 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_listva_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. 创建守护进程
守护进程本质是自成会话的孤儿进程
而一个进程要想成为守护进程,需要满足以下几个要求:
- 不能是进程组组长。因为要调用函数独立出去,如果进程组组长,则会影响进程组的其他进程
- 需要忽略异常信号
- 读写错误输入输出需要特殊处理
- 进程的工作路径可能要改
新建会话的函数: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文件的输入输出数据丢进这个黑洞文件中。
但如此,日志系统就不能以打印显示日志信息,而需要创建一个日志文件,将日志信息写入文件中进行持久化。如此也不会影响其他进程
结束语
本篇博客到此结束,感谢看到此处。
欢迎大家纠错和补充
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
相关文章:

【计算机网络学习之路】日志和守护进程
文章目录 前言一. 日志介绍二. 简单日志1. 左字符串2. 右字符串 三. 守护进程1. ps -axj命令2. 会话扩展命令 3. 创建守护进程 结束语 前言 本系列文章是计算机网络学习的笔记,欢迎大佬们阅读,纠错,分享相关知识。希望可以与你共同进步。 本…...

foobar2000 突然无法正常输出DSD信号
之前一直在用foobar2000加外置dac听音乐,有一天突然发现听dsd的时候,dac面板显示输出的是PCM格式信号,而不是DSD信号,这让我觉得很奇怪,反复折腾了几次,卸载安装驱动什么的,依然如此,…...

鸿蒙HarmonyOS 编辑器 下载 安装
好 各位 之前的文章 注册并实名认证华为开发者账号 我们基实名注册了华为的开发者账号 我们可以访问官网 https://developer.harmonyos.com/cn/develop/deveco-studio 在这里 直接就有我们编辑器的下载按钮 我们直接点击立即下载 这里 我们根据自己的系统选择要下载的系统 例…...

机器学习第13天:模型性能评估指标
☁️主页 Nowl 🔥专栏《机器学习实战》 《机器学习》 📑君子坐而论道,少年起而行之 文章目录 交叉验证 保留交叉验证 k-折交叉验证 留一交叉验证 混淆矩阵 精度与召回率 介绍 精度 召回率 区别 使用代码 偏差与方差 介绍 区…...
Elasticsearch基础优化
分片策略 分片和副本得设计为ES提供支付分布式和故障转移得特性,但不意味着分片和副本是可以无限分配, 而且索引得分片完成分配后由于索引得路由机制,不能重新修改分片数(副本数可以动态修改) 一个分片得底层为一个l…...

【Amazon】通过直接连接的方式导入 KubeSphere集群至KubeSphere主容器平台
文章目录 一、设置主集群方式一:使用 Web 控制台方式二:使用 Kubectl命令 二、在主集群中设置代理服务地址方式一:使用 Web 控制台方式二:使用 Kubectl命令 三、登录控制台验证四、准备成员集群方式一:使用 Web 控制台…...

三数之和问题
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。 示例 1&…...

【JavaEE】多线程 (2) --线程安全
目录 1. 观察线程不安全 2. 线程安全的概念 3. 线程不安全的原因 4. 解决之前的线程不安全问题 5. synchronized 关键字 - 监视器锁 monitor lock 5.1 synchronized 的特性 5.2 synchronized 使⽤⽰例 1. 观察线程不安全 package thread; public class ThreadDemo19 {p…...
关于点胶机那些事
总结一下点胶机技术要点: 1:不论多复杂的点胶机,简单点,可以简化为:1:运控 2:点胶,3:检测 运控的目的就是负责把针头移到面板对应的胶路上,点胶即就是排胶&…...

Python | CAP - 累积精度曲线分析案例
CAP通常被称为“累积精度曲线”,用于分类模型的性能评估。它有助于我们理解和总结分类模型的鲁棒性。为了直观地显示这一点,我们在图中绘制了三条不同的曲线: 一个随机的曲线(random)通过使用随机森林分类器获得的曲线…...
ubuntu22.04安装swagboot遇到的问题
一、基本情况 系统:u 22.04 python: 3.10 二、问题描述 swagboot官方提供的安装路径言简意赅:python3 -m pip install --user snagboot 当然安装python3和pip是基本常识,这里就不再赘述。 可是在安装的时候出现如下提示说 Failed buildin…...
python每日一题——8无重复字符的最长子串
题目 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串…...

【数据中台】开源项目(2)-Dbus数据总线
1 背景 企业中大量业务数据保存在各个业务系统数据库中,过去通常的同步数据的方法有很多种,比如: 各个数据使用方在业务低峰期各种抽取所需数据(缺点是存在重复抽取而且数据不一致) 由统一的数仓平台通过sqoop到各个…...

职场快速赢得信任
俗话说的好,有人的地方就有江湖。 国内不管是外企、私企、国企,职场环境都是变换莫测。 这里主要分享下怎么在职场中快速赢取信任。 1、找到让自己全面发展的方法 要知道,职场中话题是与他人交流的纽带,为了找到共同的话题&am…...

【SpringBoot3+Vue3】五【完】【实战篇】-前端(配合后端)
目录 一、环境准备 1、创建Vue工程 2、安装依赖 2.1 安装项目所需要的vue依赖 2.2 安装element-plus依赖 2.2.1 安装 2.2.2 项目导入element-plus 2.3 安装axios依赖 2.4 安装sass依赖 3、目录调整 3.1 删除部分默认目录下文件 3.1.1 src/components下自动生成的…...

[LaTex]arXiv投稿攻略——jpg/png转pdf
一、将图片复制进ppt,右键单击图片选择设置图片格式,获取图片高度和宽度 二、选择“设计-幻灯片大小-自定义幻灯片大小” 三、设置幻灯片大小为图片大小 四、 选择“最大化” 五、 检查幻灯片大小是否与图像大小一致 六、导出为PDF...

使用Pytorch从零开始构建GRU
门控循环单元 (GRU) 是 LSTM 的更新版本。让我们揭开这个网络的面纱并探索这两个兄弟姐妹之间的差异。 您听说过 GRU 吗?门控循环单元(GRU)是更流行的长短期记忆(LSTM)网络的弟弟,也是循环神经网络&#x…...

【尚跑】2023宝鸡马拉松安全完赛,顺利PB达成
1、赛事背景 千年宝地,一马当先!10月15日7时30分,吉利银河2023宝鸡马拉松在宝鸡市行政中心广场鸣枪开跑。 不可忽视的是,这次赛事的卓越之处不仅在于规模和参与人数,还在于其精心的策划和细致入微的组织。为了确保每位…...
Mac nginx安装,通过源码安装教程
第一部分 安装参考网址: https://blog.csdn.net/a1004084857/article/details/128512612; 以上步骤执行完,进入找到sbin目录,查看下面是不是有nginx可执行文件,如果有在当前sbin下执行./nginx,就会发现NGINX已启动 第…...
TypeScript中的枚举是什么?
在TypeScript中,枚举(Enum)是一种用于定义一组有命名的常量值的数据类型。它们可以提供更具可读性和可维护性的代码。 枚举的作用是为一组相关的值提供一个易于理解和使用的命名空间。它们可以用于代表一系列可能的选项、状态或标志…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...