【计算机网络学习之路】日志和守护进程
文章目录
- 前言
- 一. 日志介绍
- 二. 简单日志
- 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)是一种用于定义一组有命名的常量值的数据类型。它们可以提供更具可读性和可维护性的代码。 枚举的作用是为一组相关的值提供一个易于理解和使用的命名空间。它们可以用于代表一系列可能的选项、状态或标志…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
