Linux:进程间通信之进程池和日志
一、进程池的设计
因为每一次我们要进行进程间通信都需要fork,和操作系统做交互是存在很大成本的,所以我们是不是可以提前fork出几个进程,然后当我们想要使用的时候直接去给他们安排任务,这样就减少了系统调用的次数从而提高了内存申请速度!!
父进程和多个子进程建立管道之后, 父进程只需要(1)选择任务 (2)选择进程
1、 先描述管道
2、加载任务
3、初始化进程池
管道继承下去打开的写端都是3号fd 为了让子进程没有管道的概念,我们直接用dup来将子进程的标准输入改成管道文件,这样可以让子进程没有管道的概念,我们也就可以直接无脑从0号fd读取
4、子进程完成任务
5、父进程控制子进程
6、菜单
7、结束进程
为什么正着回收不行呢?——>因为子进程会把父进程指向前面管道的写端继承下去
所以2号继承会有指向1号进程写端的fd 3号继承会有1号和2号进程写端的fd…… 所以到10号的时候就会有9个进程写端的fd 所以第一个子进程被回收的时候,会因为一些子进程的写端没有关闭而造成阻塞!! ·
解决方案1:倒着回收
解决方案2:两个循环(先把所有写端关闭了再一起回收)
解决方案3:确保子进程只有一个写端(就是在创建的时候就把指向前面几个进程的写端全部关掉)
8 、负载均衡
随机数种子轮转 或者是 让进程轮流工作
整体代码:
#pragma once#include <iostream>
#include <vector>typedef void (*task_t)();void task1()
{std::cout << "lol 刷新日志" << std::endl;
}
void task2()
{std::cout << "lol 更新野区,刷新出来野怪" << std::endl;
}
void task3()
{std::cout << "lol 检测软件是否更新,如果需要,就提示用户" << std::endl;
}
void task4()
{std::cout << "lol 用户释放技能,更新用的血量和蓝量" << std::endl;
}void LoadTask(std::vector<task_t> *tasks)
{tasks->push_back(task1);tasks->push_back(task2);tasks->push_back(task3);tasks->push_back(task4);
}
#include "Task.hpp"
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>const int processnum = 10;
std::vector<task_t> tasks;// 先描述
class channel
{
public:channel(int cmdfd, int slaverid, const std::string &processname):_cmdfd(cmdfd), _slaverid(slaverid), _processname(processname){}
public:int _cmdfd; // 发送任务的文件描述符pid_t _slaverid; // 子进程的PIDstd::string _processname; // 子进程的名字 -- 方便我们打印日志// int _cmdcnt;
};void slaver()
{// read(0)while(true){int cmdcode = 0;int n = read(0, &cmdcode, sizeof(int)); // 如果父进程不给子进程发送数据呢??阻塞等待!if(n == sizeof(int)){//执行cmdcode对应的任务列表std::cout <<"slaver say@ get a command: "<< getpid() << " : cmdcode: " << cmdcode << std::endl;if(cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();}if(n == 0) break;}
}
// 输入:const &
// 输出:*
// 输入输出:&
void InitProcessPool(std::vector<channel> *channels)
{// version 2: 确保每一个子进程都只有一个写端std::vector<int> oldfds;for(int i = 0; i < processnum; i++){int pipefd[2]; // 临时空间int n = pipe(pipefd);assert(!n); // 演示就可以(void)n;pid_t id = fork();if(id == 0) // child{std::cout << "child: " << getpid() << " close history fd: ";for(auto fd : oldfds) {std::cout << fd << " ";close(fd);}std::cout << "\n";close(pipefd[1]);dup2(pipefd[0], 0);close(pipefd[0]);slaver();std::cout << "process : " << getpid() << " quit" << std::endl;// slaver(pipefd[0]);exit(0);}// fatherclose(pipefd[0]);// 添加channel字段了std::string name = "process-" + std::to_string(i);channels->push_back(channel(pipefd[1], id, name));oldfds.push_back(pipefd[1]);sleep(1);}
}void Debug(const std::vector<channel> &channels)
{// testfor(const auto &c :channels){std::cout << c._cmdfd << " " << c._slaverid << " " << c._processname << std::endl;}
}void Menu()
{std::cout << "################################################" << std::endl;std::cout << "# 1. 刷新日志 2. 刷新出来野怪 #" << std::endl;std::cout << "# 3. 检测软件是否更新 4. 更新用的血量和蓝量 #" << std::endl;std::cout << "# 0. 退出 #" << std::endl;std::cout << "#################################################" << std::endl;
}void ctrlSlaver(const std::vector<channel> &channels)
{int which = 0;// int cnt = 5;while(true){int select = 0;Menu();std::cout << "Please Enter@ ";std::cin >> select;if(select <= 0 || select >= 5) break;// select > 0&& select < 5// 1. 选择任务// int cmdcode = rand()%tasks.size();int cmdcode = select - 1;// 2. 选择进程// int processpos = rand()%channels.size();std::cout << "father say: " << " cmdcode: " <<cmdcode << " already sendto " << channels[which]._slaverid << " process name: " << channels[which]._processname << std::endl;// 3. 发送任务write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode));which++;which %= channels.size();// cnt--;// sleep(1);}
}void QuitProcess(const std::vector<channel> &channels)
{for(const auto &c : channels){close(c._cmdfd);waitpid(c._slaverid, nullptr, 0);}// version1 // int last = channels.size()-1;// for(int i = last; i >= 0; i--)// {// close(channels[i]._cmdfd);// waitpid(channels[i]._slaverid, nullptr, 0);// }// for(const auto &c : channels) close(c._cmdfd);// // sleep(5);// for(const auto &c : channels) waitpid(c._slaverid, nullptr, 0);// // sleep(5);
}
int main()
{LoadTask(&tasks);srand(time(nullptr)^getpid()^1023); // 种一个随机数种子// 在组织std::vector<channel> channels;// 1. 初始化 --- bug?? -- 找一下这个问题在哪里?然后提出一些解决方案!InitProcessPool(&channels);// Debug(channels);// 2. 开始控制子进程ctrlSlaver(channels);// 3. 清理收尾QuitProcess(channels);return 0;
}
二、日志的设计
1、什么是日志
日志的时间、日志的等级、日志的内容、文章的名称和行号
帮助我们能够看到一些代码运行过程中的重要信息。
2、设置等级和写日志的方式
因为我们的日志信息可能会需要各种类型 比如%d %s…… 所以必须用到可变参数。且格式化为字符串。
且日志一般情况下是写到文件里的 ,也有可能要按照等级去分文件,因此
3、可变参数列表的解析举例
4、日志信息的前半部分(时间)
time是获取一个时间戳
gettimeofday获取当天的时间(tv是输出型参数)
settimeofday设置当天的时间 (tv是输入型参数)
localtime 把time类型的返回值传过来 转化成tm结构返回 (有具体的时间信息)
leftbuffer是左半部分的时间信息(可以用格式化,snprintf), rightbuffer是右半部分的日志信息(可变参数 vsnprintf)。
可以用运算符重载美化一下,这样调用的时候就更方便
5、想办法把文件打包
#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}// void logmessage(int level, const char *format, ...)// {// time_t t = time(nullptr);// struct tm *ctime = localtime(&t);// char leftbuffer[SIZE];// snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),// ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,// ctime->tm_hour, ctime->tm_min, ctime->tm_sec);// // va_list s;// // va_start(s, format);// char rightbuffer[SIZE];// vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);// // va_end(s);// // 格式:默认部分+自定义部分// char logtxt[SIZE * 2];// snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);// // printf("%s", logtxt); // 暂时打印// printLog(level, logtxt);// }void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path;
};// int sum(int n, ...)
// {
// va_list s; // char*
// va_start(s, n);// int sum = 0;
// while(n)
// {
// sum += va_arg(s, int); // printf("hello %d, hello %s, hello %c, hello %d,", 1, "hello", 'c', 123);
// n--;
// }// va_end(s); //s = NULL
// return sum;
// }
相关文章:

Linux:进程间通信之进程池和日志
一、进程池的设计 因为每一次我们要进行进程间通信都需要fork,和操作系统做交互是存在很大成本的,所以我们是不是可以提前fork出几个进程,然后当我们想要使用的时候直接去给他们安排任务,这样就减少了系统调用的次数从而提高了内存…...

详细介绍HTTP与RPC:为什么有了HTTP,还需要RPC?
目录 一、HTTP 二、RPC 介绍 工作原理 核心功能 如何服务寻址 如何进行序列化和反序列化 如何网络传输 基于 TCP 协议的 RPC 调用 基于 HTTP 协议的 RPC 调用 实现方式 优点和缺点 使用场景 常见框架 示例 三、问题 问题一:是先有HTTP还是先有RPC&…...

Paddle Inference部署推理(十二)
十二:Paddle Inference推理 (python)API详解 15. PredictorPool 类 PredictorPool 对 Predictor 进行了简单的封装,通过传入 config 和 thread 的数目来完成初始化,在每个线程中,根据自己的线程 id 直接从…...

外观模式 (Facade Pattern)
外观模式 (Facade Pattern) 外观模式是一种 结构型设计模式,通过为子系统中的一组接口提供一个统一的高层接口,简化了子系统的使用,让复杂系统更易于访问。 原理 核心思想: 提供一个 统一的接口 来访问子系统中的多个接口&#…...

人工智能-深度学习-Torch框架-手动构建回归流程
from sklearn.datasets import make_regression import math import random import torch from sklearn.datasets import make_regression: 导入make_regression函数,用于生成回归数据集。 import math: 导入math模块,用于进行数学计算,例如…...

SpringBoot源码解析(五):准备应用环境
SpringBoot源码系列文章 SpringBoot源码解析(一):SpringApplication构造方法 SpringBoot源码解析(二):引导上下文DefaultBootstrapContext SpringBoot源码解析(三):启动开始阶段 SpringBoot源码解析(四):解析应用参数args Sp…...

MySQL面试-1
InnoDB中ACID的实现 先说一下原子性是怎么实现的。 事务要么失败,要么成功,不能做一半。聪明的InnoDB,在干活儿之前,先将要做的事情记录到一个叫undo log的日志文件中,如果失败了或者主动rollback,就可以通…...

nginx配置不缓存资源
方法1 location / {index index.html index.htm;add_header Cache-Control no-cache,no-store;try_files $uri $uri/ /index.html;#include mime.types;if ($request_filename ~* .*\.(htm|html)$) {add_header Cache-Control "private, no-store, no-cache, must-revali…...

PHP导出EXCEL含合计行,设置单元格格式
PHP导出EXCEL含合计行,设置单元格格式,水平居中 垂直居中 public function exportSalary(Request $request){//水平居中 垂直居中$styleArray [alignment > [horizontal > Alignment::HORIZONTAL_CENTER,vertical > Alignment::VERTICAL_CE…...

RabbitMQ 之 死信队列
一、死信的概念 先从概念解释上搞清楚这个定义,死信,顾名思义就是无法被消费的消息,字面意思可以这样理 解,一般来说,producer 将消息投递到 broker 或者直接到 queue 里了,consumer 从 queue 取出消息进行…...

【创建型设计模式】单例模式
【创建型设计模式】单例模式 这篇博客接下来几篇都将阐述设计模式相关内容。 接下来的顺序大概是:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。 一、什么是单例模式 单例模式是一种创建型设计模式,它保证一个类仅有一个实例&#…...

Charles抓包工具-笔记
摘要 概念: Charles是一款基于 HTTP 协议的代理服务器,通过成为电脑或者浏览器的代理,然后截取请求和请求结果来达到分析抓包的目的。 功能: Charles 是一个功能全面的抓包工具,适用于各种网络调试和优化场景。 它…...

Go语言使用 kafka-go 消费 Kafka 消息教程
Go语言使用 kafka-go 消费 Kafka 消息教程 在这篇教程中,我们将介绍如何使用 kafka-go 库来消费 Kafka 消息,并重点讲解 FetchMessage 和 ReadMessage 的区别,以及它们各自适用的场景。通过这篇教程,你将了解如何有效地使用 kafk…...

【论文笔记】Number it: Temporal Grounding Videos like Flipping Manga
🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。 基本信息 标题: Number it: Temporal Grou…...

C语言菜鸟入门·关键字·int的用法
目录 1. int关键字 1.1 取值范围 1.2 符号类型 1.3 运算 1.3.1 加法运算() 1.3.2 减法运算(-) 1.3.3 乘法运算(*) 1.3.4 除法运算(/) 1.3.5 取余运算(%) 1.3.6 自增()与自减(--) 1.3.7 位运算 2. 更多关键字 1. int关键字 int 是一个关键字࿰…...

基于企业微信客户端设计一个文件下载与预览系统
在企业内部沟通与协作中,文件分享和管理是不可或缺的一部分。企业微信(WeCom)作为一款广泛应用于企业的沟通工具,提供了丰富的API接口和功能,帮助企业进行高效的团队协作。然而,随着文件交换和协作的日益增…...

昇思MindSpore第七课---文本解码原理
1. 文本解码原理 文本解码是将模型的输出(通常是概率分布或词汇索引)转换为可读的自然语言文本的过程。在生成文本时,常见的解码方法包括贪心解码、束搜索(BeamSearch)、随机采样等。 2 实践 2.1 配置环境 安装mindn…...

C# 数据结构之【图】C#图
1. 图的概念 图是一种重要的数据结构,用于表示节点(顶点)之间的关系。图由一组顶点和连接这些顶点的边组成。图可以是有向的(边有方向)或无向的(边没有方向),可以是加权的ÿ…...

传输控制协议(TCP)和用户数据报协议(UDP)
一、传输控制协议(TCP) 传输控制协议(Transmission Control Protocol,TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的 RFC 793 定义。 它通过三次握手建立连接,确保数…...

【Python爬虫】Scrapy框架实战---百度首页热榜新闻
如何利用Scrapy框架实战提取百度首页热榜新闻的排名、标题和链接 一、安装Scrapy库 二、创建项目(以BaiduSpider为例) scrapy startproject BaiduSpider生成每个文件的功能: 二、 创建爬虫脚本(爬虫名:newsÿ…...

采用python3.12 +django5.1 结合 RabbitMQ 和发送邮件功能,实现一个简单的告警系统 前后端分离 vue-element
一、开发环境搭建和配置 #mac环境 brew install python3.12 python3.12 --version python3.12 -m pip install --upgrade pip python3.12 -m pip install Django5.1 python3.12 -m django --version #用于检索系统信息和进程管理 python3.12 -m pip install psutil #集成 pika…...

Qt 实现网络数据报文大小端数据的收发
1.大小端数据简介 大小端(Endianness)是计算机体系结构的一个术语,它描述了多字节数据在内存中的存储顺序。以下是大小端的定义和它们的特点: 大端(Big-Endian) 在大端模式中,一个字的最高有效…...

[译]Elasticsearch Sequence ID实现思路及用途
原文地址:https://www.elastic.co/blog/elasticsearch-sequence-ids-6-0 如果 几年前,在Elastic,我们问自己一个"如果"问题,我们知道这将带来有趣的见解: "如果我们在Elasticsearch中对索引操作进行全面排序会怎样…...

Java基于SpringBoot+Vue的藏区特产销售平台
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...

12-表的约束
知识背景 表的约束,就是在表中的数据上加上约束,也被称为数据完整性约束。数据完整性约束的目的是为了不被规定的、不符合规范的数据进入数据库 在录入数据库或数据发生变化时,DBMS(数据库管理系统)会按照一定的约束条件对数据进行监测&…...

【人工智能】深度学习入门:用TensorFlow实现多层感知器(MLP)模型
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 多层感知器(MLP)是一种基础的神经网络结构,广泛应用于分类和回归任务。作为深度学习的重要组成部分,理解并实现MLP是学习更复杂神经网络模型的基础。本文将介绍多层感知器的核心概念、数学原理,并使用…...

【Go】-go中的锁机制
目录 一、锁的基础知识 1. 互斥量/互斥锁 2. CAS(compare and swap) 3. 自旋锁 4. 读写锁 5. 乐观锁 & 悲观锁 6. 死锁 二、go中锁机制 1. Mutex-互斥锁 2. RWMutex-读写锁 2.1 RWMutex流程概览 2.2 写锁饥饿问题 2.3. golang的读写锁源…...

c ++零基础可视化——vector
c 零基础可视化——vector 初始化 vector<int> v0(5); // 0 0 0 0 0 vector<int> v1(5, 1); // 1 1 1 1 1 vector<int> v2{1, 2, 3} // 1 2 3 vector<int> v3(v1); // 1 1 1 1 1 vector<vector<int>> v4(2, vect…...

Centos 7 安装 Docker 最新版本
文章目录 一、卸载旧版本二、安装最新版本docker三、问题解决3.1 启动docker报错3.2 启动容器报错 一、卸载旧版本 #如果之前安装过旧版本的Docker,可以使用下面命令卸载 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest …...

构建高效在线教育:SpringBoot课程管理系统
1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理在线课程管理系统的相关信息成为必然。开发…...