Linux:利用匿名管道构建进程池

文章目录

  • 进程池
  • 实现进程池
    • 创建信道和进程
    • 发送任务
    • 释放资源
  • 进程池代码
  • 总结

本篇的主题是借助前面所学的基础管道实现一个进程池,那么在实现进程池前先了解进程池是什么,进程池有什么意义,进而对于进程池有一个基本的把握

进程池

给定一个进程,为这个进程创建多个管道,并且对于每一个管道都设置一个子进程,当父进程发送消息,子进程就能获取到消息的内容,而父进程的存在价值就是控制子进程,并且给子进程布置服务等等,而这个内容就叫做进程池

在谈到进程池前,要先提及的是池化技术,池化技术是很重要的技术,在STL容器中也存在诸如空间配置器这样的内容,这种内容诞生的原因就是因为,在调用系统中的资源是需要成本的,例如有空间资源,时间资源等等,而如果不断的申请这些资源就会耗费多余的资源,于是诞生了池化技术,提前申请一块大的空间,由申请人自己来管理,这个就是基本的池化技术

在STL的空间配置器中就有这样的内容,利用空间配置器可以提前申请一部分内容,并且基于这个空间进行自我管理,随时进行申请,就能减少从系统中申请资源带来的额外的成本,而进程池也是基于这样的原理,系统调用也是有成本的,就连最基本的函数跳转都有成本,所以才出现了内联函数来帮助提高效率,更别说对于这样大型的系统调用的接口,必然是有成本的,因此借助池化技术来完成工作时有很大的必要的

实现进程池

对于本篇实现的这个进程池来说,功能也是比较简单的:

  1. 创建信道和进程
  2. 发送任务
  3. 回收资源

创建信道和进程

要实现进程池,就要先清楚进程池整体的结构是什么,对于进程池来说,首先要有进程,有管道,这都进程池创建的必要组成部分,那么在创建进程池的阶段就要实现这些内容,所以要先创建并定义管道

// 定义要创建的子进程个数
const int num = 5;int main()
{for (int i = 0; i < num; i++){// 创建管道int pipefd[2];int n = pipe(pipefd);assert(n == 0);// 创建子进程pid_t id = fork();if (id == 0){// 子进程负责读close(pipefd[1]);// do something...cout << "this is child" << endl;exit(0);}// 父进程负责写close(pipefd[0]);cout << "this is father" << endl;}return 0;
}

但是这样写固然是有问题的,问题点在于,父进程创建的子进程的数据会丢失,不利于进行管理,那么在管理这样的数据之前,首先要对这些内容进行描述,所以要创建对应的结构体进行描述,要管理的这些数据包括有,父进程管理的子进程的pid是多少,父进程和这个子进程传输的文件描述符下标是多少,最好还能用名字来管理,基于这些信息就能创建对应的描述结构体

class channel
{
public:channel(int fd, pid_t id) : ctrlfd(fd), workerid(id){name = "channel-" + to_string(number++);}public:int ctrlfd;pid_t workerid;string name;
};

因此基于这个内容,就创建好了描述的内容,而可以创建一张表,用来描述存储信息的情况,这样父进程对于子进程的管理就转换成了对于顺序表的管理,这样就能对于数据做一个很好的管理工作,再对创建过程做出一个具体的封装,就能封装出下面的代码:

void Create(vector<channel> *c)
{for (int i = 0; i < num; i++){// 创建管道int pipefd[2];int n = pipe(pipefd);assert(n == 0);// 创建子进程pid_t id = fork();if (id == 0){// 子进程负责读close(pipefd[1]);// do something...cout << "this is child" << endl;exit(0);}// 父进程负责写close(pipefd[0]);c->push_back(channel(pipefd[1], id));}
}int main()
{vector<channel> channels;Create(&channels);return 0;
}

到此,封装的第一步就完成了,对象已经被创建完毕了

发送任务

对于任务这个版块,本篇的实现方式采用的是模拟调用的方式,给定三种调用的方式,然后利用随机调用的方式对这三种方式进行调用,用来模拟父进程给子进程派发任务这样的一个过程,具体的实现如下:

#pragma once
#include <iostream>
#include <functional>
#include <vector>
#include <ctime>
#include <unistd.h>
using namespace std;typedef function<void()> task_t;void Download()
{cout << "this is download" << endl;
}void PrintLog()
{cout << "this is PrintLog" << endl;
}void PushVideoStream()
{cout << "this is pushvideo" << endl;
}class Init
{
public:// 任务码const static int g_download_code = 0;const static int g_printlog_code = 1;const static int g_push_videostream_code = 2;// 任务集合vector<task_t> tasks;public:Init(){tasks.push_back(Download);tasks.push_back(PrintLog);tasks.push_back(PushVideoStream);srand(time(nullptr));}bool CheckSafe(int code){if (code >= 0 && code < tasks.size())return true;elsereturn false;}void RunTask(int code){return tasks[code]();}int SelectTask(){return rand() % tasks.size();}string ToDesc(int code){switch (code){case g_download_code:return "Download";case g_printlog_code:return "PrintLog";case g_push_videostream_code:return "PushVideoStream";default:return "Unknow";}}
};Init init;

将任务整合到函数体实现的对应位置,如下所示:

// 发送任务
void SendCommand(const vector<channel> &c, int count)
{int pos = 0;while (count--){// 1. 选择任务int command = init.SelectTask();// 2. 选择信道(进程)const auto &channel = c[pos++];pos %= c.size();// 3. 发送任务write(channel.ctrlfd, &command, sizeof(command));sleep(1);}cout << "SendCommand done" << endl;
}

释放资源

对于释放资源来讲,主体思路主要是将管道对应的读端和写端都关闭,这样就可以使得管道得以释放了,于是有初步的实现设计

void ReleaseChannels(vector<channel> c)
{for (const auto &channel : c){pid_t rid = waitpid(channel.workerid, nullptr, 0);if (rid == channel.workerid){std::cout << "wait child: " << channel.workerid << " success" << std::endl;}}
}

从表象上看,这样的释放过程是没有问题的,但是从深层次的角度来讲,代码中其实隐藏着一些问题,用下图来表示

下图所示的是在理想状态下,在操作系统内部应该形成的状态体系结构,但是实际上,这样的图是有问题的,在父进程创建子进程的时候,子进程会继承的还有父进程的文件描述符表,正是依据这个原理才能设计出管道这样的结构,但是在第二个子进程继承父进程时,也会继承一份指向,所以管道1的引用计数实际上是比想象的要多一个指针的
在这里插入图片描述
下图所示的是操作系统内部真正的示意图

在这里插入图片描述
基于这样的原因,在进行设计的时候,可以记录之前已经打开的文件描述符,在后续创建子进程的时候刻意的将这些文件描述符都关掉,这样就可以解决问题

进程池代码

// task.hpp
#pragma once
#include <iostream>
#include <functional>
#include <vector>
#include <ctime>
#include <unistd.h>
using namespace std;typedef function<void()> task_t;void Download()
{cout << "this is download" << endl;
}void PrintLog()
{cout << "this is PrintLog" << endl;
}void PushVideoStream()
{cout << "this is pushvideo" << endl;
}class Init
{
public:// 任务码const static int g_download_code = 0;const static int g_printlog_code = 1;const static int g_push_videostream_code = 2;// 任务集合vector<task_t> tasks;public:Init(){tasks.push_back(Download);tasks.push_back(PrintLog);tasks.push_back(PushVideoStream);srand(time(nullptr));}bool CheckSafe(int code){if (code >= 0 && code < tasks.size())return true;elsereturn false;}void RunTask(int code){return tasks[code]();}int SelectTask(){return rand() % tasks.size();}string ToDesc(int code){switch (code){case g_download_code:return "Download";case g_printlog_code:return "PrintLog";case g_push_videostream_code:return "PushVideoStream";default:return "Unknow";}}
};Init init;
// processpool.cc
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.hpp"
using namespace std;// 定义要创建的子进程个数
const int num = 5;
static int number = 1;class channel
{
public:channel(int fd, pid_t id) : ctrlfd(fd), workerid(id){name = "channel-" + to_string(number++);}public:int ctrlfd;pid_t workerid;string name;
};void Work()
{while (true){int code = 0;ssize_t n = read(0, &code, sizeof(code));if (n == sizeof(code)){if (!init.CheckSafe(code))continue;init.RunTask(code);}else if (n == 0){break;}else{// do nothing}}cout << "child quit" << endl;
}void Create(vector<channel> *c)
{vector<int> old;for (int i = 0; i < num; i++){// 1. 定义并创建管道int pipefd[2];int n = pipe(pipefd);assert(n == 0);(void)n;// 2. 创建进程pid_t id = fork();assert(id != -1);// 3. 构建单向通信信道if (id == 0) // child{if (!old.empty()){for (auto fd : old){close(fd);}}close(pipefd[1]);dup2(pipefd[0], 0);Work();exit(0); // 会自动关闭自己打开的所有的fd}// fatherclose(pipefd[0]);c->push_back(channel(pipefd[1], id));old.push_back(pipefd[1]);// childid, pipefd[1]}
}// 发送任务
void SendCommand(const vector<channel> &c, int count)
{int pos = 0;while (count--){// 1. 选择任务int command = init.SelectTask();// 2. 选择信道(进程)const auto &channel = c[pos++];pos %= c.size();// 3. 发送任务write(channel.ctrlfd, &command, sizeof(command));sleep(1);}cout << "SendCommand done" << endl;
}// 回收资源
void ReleaseChannels(std::vector<channel> c)
{int num = c.size() - 1;for (; num >= 0; num--){close(c[num].ctrlfd);waitpid(c[num].workerid, nullptr, 0);}
}int main()
{vector<channel> channels;// 1. 创建进程Create(&channels);// 2. 发送任务SendCommand(channels, 10);// 3. 回收资源ReleaseChannels(channels);return 0;
}

运行结果如下所示

在这里插入图片描述

总结

对于进程池来说,就是预先创建一批进程,然后让master进程直接向目标进程排放任务,这个进程被预先创建好之后,有任务就处理,没有任务就进行阻塞

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/650649.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习笔记-李沐动手学深度学习(四)(12-13,权重衰退、L2正则化、Dropout)

总结 【trick】过拟合及正则化项参数的理解 实际数据都有噪音&#xff0c;一般有噪音后&#xff0c;模型实际学习到的权重w就会比 理论上w的最优解&#xff08;即没有噪音时&#xff09;大。&#xff08;QA中讲的&#xff09; 【好问题】 &#xff08;1&#xff09;不使用正…

svg 属性详解:填充与边框

svg 属性详解&#xff1a;填充与边框 1 颜色和透明度2 填充规则 fill-rule3 边框样式3.1 stroke-width3.2 stroke-linecap3.3 stroke-linejoin3.4 stroke-dasharray 1 颜色和透明度 图像都有颜色&#xff0c;svg 中可以使用属性 fill 和 stroke 来修改图形的颜色。fill 属性设置…

九州金榜|家庭教育中孩子厌学原因及解决办法

作为家长我们希望自己的孩子热爱学习&#xff0c;并取得优异成绩。但是&#xff0c;在现实中&#xff0c;孩子往往会出现厌学情绪&#xff0c;作为家长为此感到非常困扰。如何帮助孩子克服厌学情绪&#xff0c;九州金榜家庭教育将会带大家找出背后的原因&#xff0c;并寻找有效…

Tortoise-tts Better speech synthesis through scaling——TTS论文阅读

笔记地址&#xff1a;https://flowus.cn/share/a79f6286-b48f-42be-8425-2b5d0880c648 【FlowUs 息流】tortoise 论文地址&#xff1a; Better speech synthesis through scaling Abstract: 自回归变换器和DDPM&#xff1a;自回归变换器&#xff08;autoregressive transfo…

SpringSecurity(15)——OAuth2密码模式

工作流程 将用户和密码传过去&#xff0c;直接获取access_token&#xff0c;用户同意授权动作是在第三方应用上完成&#xff0c;而不是在认证服务器&#xff0c;第三方应用申请令牌时&#xff0c;直接带用户名和密码去向认证服务器申请令牌。这种方式认证服务器无法判断用户是…

网站服务器中毒或是被入侵该怎么办?

随着互联网的普及和发展&#xff0c;网站服务器已经成为了企业和个人存储数据、展示信息的重要平台。然而&#xff0c;网络安全问题也日益突出&#xff0c;其中网站服务器中毒或被入侵的事件时有发生。一旦发生这种情况&#xff0c;不仅会导致网站无法正常运行&#xff0c;还可…

阿里云负载均衡对接

1 、开通负载均衡产品 2 、ALB / NLB / CLB ALB&#xff1a; 应用型负载均衡 &#xff0c; 给定对应服务域名与当前实例DNS绑定之后即可使用 支持&#xff1a; HTTP/HTTPS/QUIC等应用层流量协议 NLB&#xff1a; 网络型负载均衡 支持&#xff1a; TCP / UDP / TCPSSL C…

浏览器——HTTP缓存机制与webpack打包优化

文章目录 概要强缓存定义开启 关闭强缓存协商缓存工作机制通过Last-Modified If-Modified-Since通过ETag If-None-Match 不使用缓存前端利用缓存机制&#xff0c;修改打包方案webpack 打包webpack 打包名称优化webpack 默认的hash 值webapck其他hash 类型配置webpack打包 web…

STM32连接阿里云物联网平台

文章目录 引言一、STM32连接阿里云物联网平台思路二、ESP8266烧录固件三、使用AT指令连接阿里云物联网平台四、STM32环形串口缓冲区驱动程序五、STM32连接阿里云驱动程序 引言 连续写了两篇关于阿里云连接的文章&#xff0c;都是使用Arduino ESP8266 & Arduino ESP32的方式…

什么是网络安全?网络安全概况

网络安全涉及保护我们的计算机网络、设备和数据免受未经授权的访问或破坏。 这个领域包括多种技术、过程和控制措施&#xff0c;旨在保护网络、设备和数据免受攻击、损害或未授权访问。网络安全涉及多个方面&#xff0c;包括但不限于信息安全、应用程序安全、操作系统安全等 …

Chain-of-Thought Prompting Elicits Reasoning in Large Language Models导读

通过生成一系列中间推理步骤&#xff08;即“思维链”&#xff09;显著提高大型语言模型进行复杂推理的能力 这篇论文探讨了如何通过生成一系列中间推理步骤&#xff08;即“思维链”&#xff09;显著提高大型语言模型进行复杂推理的能力。研究人员使用一种简单的方法——思维…

【PyTest】玩转HTML报告:修改、汉化和优化

前言 Pytest框架可以使用两种测试报告&#xff0c;其中一种就是使用pytest-html插件生成的测试报告&#xff0c;但是报告中有一些信息没有什么用途或者显示的不太好看&#xff0c;还有一些我们想要在报告中展示的信息却没有&#xff0c;最近又有人问我pytest-html生成的报告&a…

重磅!讯飞星火V3.5马上发布!AI写作、AI编程、AI绘画等功能全面提升!

讯飞星火大模型相信很多友友已经不陌生了&#xff0c;可以说是国内GPT相关领域的龙头标杆&#xff0c;而对于1月30日即将在讯飞星火发布会发出的V3.5新版本来说&#xff0c;讯飞星火V3.5与之前版本相比&#xff0c;性能提升方面相当明显&#xff0c;在提示语义理解、内容生成、…

代码随想录算法训练营29期|day32 任务以及具体安排

第八章 贪心算法 part02 122.买卖股票的最佳时机II // 贪心思路 class Solution {public int maxProfit(int[] prices) {int result 0;for (int i 1; i < prices.length; i) {result Math.max(prices[i] - prices[i - 1], 0);}return result;} } 思路&#xff1a;将股票问…

shell脚本登录dlut-lingshui并设置开机连网和断网重连

本文提供了一个用于无图形界面linux系统自动连接dlut-lingshui校园网的shell脚本&#xff0c;并提供了设置开机联网以及断网重连的详细操作步骤。本文的操作在ubuntu 22.04系统上验证有效&#xff0c;在其他版本的linux系统上操作时遇到问题可以自行百度。 1. 获取校园网认证界…

单片机学习笔记---独立按键控制LED显示二进制

这节我们来实现独立按键的第三个功能&#xff0c;独立按键控制LED显示二进制 新创建一个工程文件&#xff0c;然后上来我们就要把基本框架写好&#xff0c;这是基本的习惯 老规矩&#xff0c;然后把Delay 1ms的代码复制过来 复制过来后改造一下&#xff1a; 把1ms删掉&#x…

数据结构和算法笔记5:堆和优先队列

今天来讲一下堆&#xff0c;在网上看到一个很好的文章&#xff0c;不过它实现堆是用Golang写的&#xff0c;我这里打算用C实现一下&#xff1a; Golang: Heap data structure 1. 基本概念 满二叉树&#xff08;二叉树每层节点都是满的&#xff09;&#xff1a; 完全二叉树&a…

微信小程序(十二)在线图标与字体的获取与引入

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.从IconFont获取图标与文字的样式链接 2.将在线图标配置进页面中&#xff08;源码&#xff09; 3.将字体配置进页面文字中&#xff08;源码&#xff09; 4.css样式的多文件导入 获取链接 1.获取图标链接 登入…

ABAP 状态栏排除某些按钮

ABAP 状态栏排除某些按钮 GUI State状态栏 在状态栏这里有这些按钮&#xff0c;现在在导出界面要排除掉这些按钮&#xff1a; 将要排除的按钮追加到gt_code内表&#xff1a; gt_fcode功能码内表的定义 DATA:gt_fcode TYPE TABLE OF sy-ucomm,完整程序 *&---------…

上位机图像处理和嵌入式模块部署(python opencv)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们谈到了qt&#xff0c;谈到了opencv&#xff0c;也谈到了嵌入式&#xff0c;但是没有说明python在这个过程当中应该扮演什么样的角色。open…