【计算机网络学习之路】日志和守护进程
文章目录
- 前言
- 一. 日志介绍
- 二. 简单日志
- 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)是一种用于定义一组有命名的常量值的数据类型。它们可以提供更具可读性和可维护性的代码。 枚举的作用是为一组相关的值提供一个易于理解和使用的命名空间。它们可以用于代表一系列可能的选项、状态或标志…...
Python字典进阶:从‘学生成绩统计’到‘自动选课分析’,教你写出更地道的代码
Python字典进阶:从‘学生成绩统计’到‘自动选课分析’,教你写出更地道的代码 在Python的世界里,字典(dict)就像是一个神奇的魔法口袋,它能以键值对的形式存储各种数据,让信息的存取变得异常高效…...
YOLO-Master 与 YOLO 开始朴
AI Agent 时代的沙箱需求 从 Copilot 到 Agent:执行能力的质变 在生成式 AI 的早期阶段,应用主要以“Copilot”形式存在,AI 仅作为辅助生成建议。然而,随着 AutoGPT、BabyAGI 以及 OpenAI Code Interpreter(现为 Advan…...
RK3588嵌入式Linux开发实战:uboot镜像合成与rkbin文件整合指南
1. RK3588开发必备:理解uboot镜像合成的核心意义 刚接触RK3588开发板时,很多工程师都会困惑:为什么编译好的uboot不能直接烧录?这个问题我最初也踩过坑。实际上,Rockchip平台的启动流程比传统嵌入式系统更复杂…...
使用Alpine配置WSL ssh门户狙
1. 哑铃图是什么? 哑铃图(Dumbbell Plot),有时也称为DNA图或杠铃图,是一种用于比较两个相关数据点的可视化图表。 它源于人们对更有效数据比较方式的持续探索。 在传统的时间序列比较中,我们通常使用两条折…...
单亲宝爸带6岁“小魔王”累到崩溃,幸好有蕙兰瑜伽……
每天被儿子折腾到筋疲力尽,直到我遇见了蕙兰瑜伽“爸爸,我们来打仗吧!”儿子举着玩具剑,眼睛里闪着兴奋的光。“宝贝,让爸爸休息五分钟……”我瘫在沙发上,连抬手的力气都没有。这是我和6岁儿子的日常。我是…...
MATLAB中矩阵转置
该MATLAB代码演示了图像处理和矩阵操作的基本功能。首先清除工作环境并读取图像文件,然后将图像矩阵转置存储为十六进制文本文件。代码展示了矩阵转置操作(A和C)及不同维度的表示方法,其中创建了640512的零矩阵C及其转置矩阵D。关…...
Spring IOC 源码学习 事务相关的 BeanDefinition 解析过程 (XML)反
从0构建WAV文件:读懂计算机文件的本质 虽然接触计算机有一段时间了,但是我的视野一直局限于一个较小的范围之内,往往只能看到于算法竞赛相关的内容,计算机各种文件在我看来十分复杂,认为构建他们并能达到目的是一件困难…...
[具身智能-345]:MCP Client工作原理
如果说 MCP Server 是“手脚”和“感官”,那么 MCP Client 就是连接“大脑”(LLM)与这些手脚的“神经系统”和“守门员”。它绝不仅仅是一个简单的 API 调用封装,而是一个具备状态管理、安全校验和协议转换能力的智能代理AI Agent…...
从KITTI到自定义:OpenPCDet在Windows 10下的实战数据适配与避坑指南
1. OpenPCDet框架与KITTI数据集基础 OpenPCDet作为当前点云3D目标检测领域的热门框架,其模块化设计和简洁的代码结构让很多研究者选择它作为开发基础。我第一次接触这个框架时,最吸引我的是它的数据-模型分离设计——这种设计让切换数据集和模型变得非常…...
AI技术变革下的SEO关键词优化新模式探索
在AI技术的推动下,SEO关键词优化正在经历深刻的变革。AI不仅改变了关键词选择的方式,还极大提升了布局的精准度。通过分析用户行为和市场趋势,AI能够自动推荐最有效的关键词,并实时调整优化策略。这一特点使得企业能够快速响应竞争…...
