当前位置: 首页 > news >正文

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;
// }

 

http://www.lryc.cn/news/491250.html

相关文章:

  • 详细介绍HTTP与RPC:为什么有了HTTP,还需要RPC?
  • Paddle Inference部署推理(十二)
  • 外观模式 (Facade Pattern)
  • 人工智能-深度学习-Torch框架-手动构建回归流程
  • SpringBoot源码解析(五):准备应用环境
  • MySQL面试-1
  • nginx配置不缓存资源
  • PHP导出EXCEL含合计行,设置单元格格式
  • RabbitMQ 之 死信队列
  • 【创建型设计模式】单例模式
  • Charles抓包工具-笔记
  • Go语言使用 kafka-go 消费 Kafka 消息教程
  • 【论文笔记】Number it: Temporal Grounding Videos like Flipping Manga
  • C语言菜鸟入门·关键字·int的用法
  • 基于企业微信客户端设计一个文件下载与预览系统
  • 昇思MindSpore第七课---文本解码原理
  • C# 数据结构之【图】C#图
  • 传输控制协议(TCP)和用户数据报协议(UDP)
  • 【Python爬虫】Scrapy框架实战---百度首页热榜新闻
  • 采用python3.12 +django5.1 结合 RabbitMQ 和发送邮件功能,实现一个简单的告警系统 前后端分离 vue-element
  • Qt 实现网络数据报文大小端数据的收发
  • [译]Elasticsearch Sequence ID实现思路及用途
  • Java基于SpringBoot+Vue的藏区特产销售平台
  • 12-表的约束
  • 【人工智能】深度学习入门:用TensorFlow实现多层感知器(MLP)模型
  • 【Go】-go中的锁机制
  • c ++零基础可视化——vector
  • Centos 7 安装 Docker 最新版本
  • 构建高效在线教育:SpringBoot课程管理系统
  • 二进制与网络安全的关系