进程池的制作(linux进程间通信,匿名管道... ...)

目录

一、进程间通信的理解

1.为什么进程间要通信

2.如何进行通信

二、匿名管道

1.管道的理解

2.匿名管道的使用

3.管道的五种特性

4.管道的四种通信情况

5.管道缓冲区容量

三、进程池

1.进程池的理解

2.进程池的制作

四、源码 

1.ProcessPool.hpp

2.Task.hpp

3.test.cc


        本文旨在分享我对管道技术的理解和见解。由于个人知识和经验的限制,文中可能存在错误或遗漏。我真诚地邀请各位读者在阅读过程中发现任何问题时,指出错误并提供宝贵的意见。您的反馈将是我不断进步和完善的重要动力。

一、进程间通信的理解

1.为什么进程间要通信

        首先进程之间是相互独立的,尽管是父子进程之间,它们虽然资源共享,但当子进程需要修改数据时仍然需要进行写时拷贝,保持独立性。

        而让进程间通信可以实现数据之间的交互资源共享事件通知,又或者是让一个进程对另一个进程进行控制

        进程间通信是操作系统中实现进程间协作和数据交换的重要机制,它使得多个进程能够共同完成任务,提高系统的效率和可靠性。

2.如何进行通信

        进程间通信的原理其实很简单,只需要两个进程共同访问一个资源,而一个进程对资源的更改能被另一进程感知到,从而做出相应的操作。

        所以通信的前提是进程之间能够访问同一个资源,而且该资源是公共的,而不是某进程内部的。

二、匿名管道

1.管道的理解

        我们把进程之间通信的介质(资源)叫作管道。开发者在设计管道技术时文件系统已经比较成熟,所以为了方便管理该资源就使用文件来实现,而对文件的读写就是通信的过程,但它与一般的文件还是有些区别,文件都是储存到磁盘上的,而进程之间通信用的文件并不需要把它储存到磁盘上,它只是作为一个传输介质。

        它比较特殊,所以起名为管道。管道其实是一个内存级的文件

        注意:父子进程之间的管道叫作匿名管道,顾名思义就是没有名字,也不需要名字,因为子进程能够继承下来父进程开辟的管道资源。 

2.匿名管道的使用

创建匿名管道常用的接口是:

                int pipe(int pipefd[2]);

需要包含头文件:

                #include<unistd.h>

  • 返回值:创建成功返回0,失败返回-1
  • 参数:这个是一个输出型参数,传入一个int类型长度为2的数组,然后得到pipefd[0]:以读的方式打开的文件描述符pipefd[1]:以写的方式打开的文件描述符

示例:

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
int main()
{int pipefd[2];pipe(pipefd);int rfd = pipefd[0],wfd = pipefd[1];pid_t id = fork();if(id == 0){close(wfd);//关闭子进程的写文件,只让它读int k=0;while(true){read(rfd,&k,sizeof(k));printf("read:%d\n",k);}}else{close(rfd);//关闭父进程的读文件,只让它写。int num=0;while(true){write(wfd,&num,sizeof(num));num++;sleep(1);}}return 0;
}

        因为只是一个小测试,代码写的并不严谨(没有检查调用是否成功,没有关闭文件,没有进程等待)大家不用太在意,能说明问题就行。

        要记住pipefd[2]中哪个是读哪个是写有一个小技巧,0像嘴巴,所以下标为0的是读,1像钢笔,所以1下标是写。

3.管道的五种特性

  1. 匿名管道,只能用来进行具有血缘关系的进程间通信(用于父与子)。
  2. 管道文件,自带同步机制。如上代码示例,父进程写一次休眠一秒,而子进程是一直不断地读,快的一端会迁就于慢的一端,最后实现同步。
  3. 管道是面向字节流的。怎么读与怎么写并没有联系,比如写入“hello world”,但可能读到“hel”,这取决于你要读多少字节。
  4. 管道是单向通信的。也就是a(表示进程)写的时候b读。b写的时候a在读。而不是既在写同时也在读。
  5. 管道(文件)的生命周期是随进程的。进程结束管道也随之销毁。

4.管道的四种通信情况

  1. 写慢,读快 --- 读端就要阻塞(等待写端写入)。
  2. 写快,读慢 --- 到管道容量满了后,写端就要阻塞(等待读端读取数据,然后就可以覆盖式地继续往管道写入)。
  3. 写关闭,读继续 --- read就会返回0,表示文件结尾。
  4. 写继续,读关闭 --- 写端不再有意义,系统会杀掉写端进程。

5.管道缓冲区容量

        管道缓冲区容量为64kb,大家可以根据管道的性质与通信特点,自行进行测试,我这里就不再展示。

三、进程池

1.进程池的理解

        在程序使用内存的时候,比如vector扩容机制,会提前给你开辟一块空间供你使用,尽管现在用不到,相当于做一下预备。减少开辟空间的频次,从而达到提高效率的效果。

        那么进程池也同样,给父进程提前开辟一些子进程,提供父进程使用。其中我们使用匿名管道建立联系。

        在父进程给子进程派发任务时,为了提高效率会让每个子进程均匀地分配到任务(称为负载均匀),而不是把大部分的任务都派发到一个子进程上,通常会有以下策略:

  • 轮询:按顺序一一分配。
  • 随机:随机进行分配。
  • 负载因子:设计一个负载因子,让子进程按负载因子的大小排成一个小根堆,每次取出堆头的子进程派发任务,然后更新负载因子插回到堆中。

2.进程池的制作

        在面向对象的编程中最重要的就是对对象的描述与组织,这里我们的核心就是对管道进行管理。那么首先需要一个类对管道进行描述。

class Channel
{
public:Channel(int wfd, int id) : _wfd(wfd), _id(id){}//... ...~Channel(){}
private:int _wfd;int _id;
};

_wfd是该管道对应写端的fd,_id是该管道对应的子进程的pid。 

        这里我们不必把rfd(读端fd)加入,因为我们现在对管道的描述组织,目的是方便父进程管理,而rfd是给子进程用的,所以不用添加为变量。

        这里我们就以轮询的方式派发任务,刚才创建的Channel相当于对管道的描述,接下来创建ChannelManage进行组织。这里选择使用数组来管理,派发任务方式选择轮询,所以需要记录下一个需要派发到的管道的下标。

class ChannelManage
{
public:ChannelManage():_next(0){}//... ...~ChannelManage(){}
private:vector<Channel> _channels;int _next;
};

接下来还需要创建一个类对整体的进程池做管理。

class ProcessPool
{
public:ProcessPool(int num) : _n(num){}// ... ...~ProcessPool(){}
private:ChannelManage _cm;int _n;
};

其中_n表示需要创建多少子进程,这是由使用者来决定的。

在ProcessPool中我们准备实现这些方法

  • void Create():用于创建子进程。

        由于我们是要生成多个通道所以需要循环来进行,而单趟循环需要做以下这些操作:

        1.创建管道,然后创建子进程。(这样能让子进程继承到管道信息)

        2.关于子进程:写端关闭,然后执行Work(),最后把读端关闭,并exit退出。

        3.关于父进程:读端关闭,然后把wfd,pid存入_cm中。

  • void Work(int  rfd):用于子进程读取任务码并执行命令。
  • void Run():用于获取派发任务。
  • void Stop():用于关闭写端回收子进程

最后为方便测试我们还需要一个管理任务的类和方法。我们可以单独创建一个Task.hpp文件。

class TaskManage
{
public:TaskManage(){   //随机数种子srand((unsigned int)time(nullptr));}int GetCode(){   //随机生成任务码(数组下标)return rand()%_tasks.size();}void ExeTask(int code){   //执行任务_tasks[code]();}// ... ...~TaskManage(){}
private:vector<function<void()>> _tasks;//用于储存任务的数组
};

        然后需要在ProcessPool中放入TaskManage成员变量,并在ProcessPool的构造函数中完成对_tasks中内容的插入。具体操作参考下面源码。

四、源码 

1.ProcessPool.hpp

#pragma once
#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <sys/wait.h>
#include <vector>
#include "Task.hpp"
#define NUM 5
using namespace std;
class Channel
{
public:Channel(int wfd, int id) : _wfd(wfd), _id(id){}void Write(int code){write(_wfd,&code,sizeof(code));}void Close(){close(_wfd);}void WaitPid(){waitpid(_id,nullptr,0);cout<<"等待进程"<<_id<<"成功"<<endl;}~Channel(){}private:int _wfd;int _id;
};
class ChannelManage
{
public:ChannelManage():_next(0){}void Insert(int wfd, int id){_channels.emplace_back(wfd, id);}int GetChannel()//轮询访问子进程{int tmp = _next;_next++;_next %= _channels.size();return tmp;}void WriteManage(int code,int index){_channels[index].Write(code);}void Close(){for(int i=0;i<_channels.size();i++){_channels[i].Close();}}void WaitPid(){for(int i=0;i<_channels.size();i++){_channels[i].WaitPid();}}~ChannelManage(){}
private:vector<Channel> _channels;int _next;
};class ProcessPool
{
public:ProcessPool(int num) : _n(num){_tm.InsertTask(PrintLog);_tm.InsertTask(Download);_tm.InsertTask(Upload);}void Work(int rfd){while (true){int code;ssize_t n = read(rfd, &code, sizeof(code));if (n > 0){if(n != sizeof(code)) continue;else{//执行任务_tm.ExeTask(code);}}else{cout<<"子进程"<<getpid()<<"退出"<<endl;break;}}}void Create(){for (int i = 0; i < _n; i++){int pipefd[2];pipe(pipefd);pid_t id = fork();if (id == 0){close(pipefd[1]);Work(pipefd[0]);close(pipefd[0]);exit(0);}else{close(pipefd[0]);_cm.Insert(pipefd[1], id);}}}void Run(){int ChannelCode = _cm.GetChannel();int TaskCode = _tm.GetCode();_cm.WriteManage(TaskCode,ChannelCode);}void Stop(){_cm.Close();_cm.WaitPid();}~ProcessPool(){}
private:ChannelManage _cm;int _n;TaskManage _tm;
};

2.Task.hpp

#pragma once
#include <iostream>
#include <vector>
#include <functional>
#include <ctime>
#include <cstdlib>
using namespace std;
void PrintLog()
{std::cout << "我是一个打印日志的任务" << std::endl;
}void Download()
{std::cout << "我是一个下载的任务" << std::endl;
}void Upload()
{std::cout << "我是一个上传的任务" << std::endl;
}
class TaskManage
{
public:TaskManage(){srand((unsigned int)time(nullptr));}void InsertTask(void(*fun)()){_tasks.push_back(fun);}int GetCode(){return rand()%_tasks.size();}void ExeTask(int code){_tasks[code]();}~TaskManage(){}
private:vector<function<void()>> _tasks;
};

3.test.cc

#include "ProcessPool.hpp"
int main()
{ProcessPool pp(NUM);pp.Create();int k = 10;while(k--){pp.Run();}pp.Stop();return 0;
}

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

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

相关文章

Linux学习笔记——用户管理

一、用户管理命令 useradd #用户增加命令 usermod #用户修改命令 passwd #密码修改命令 userdel #用户删除命令 su #用户提权命令 1、useradd命令&#xff08;加用户&#xff09;&#xff1a; 创建并设置用户信息&#xff0c;使用us…

怎样在PPT中启用演讲者视图功能?

怎样在PPT中启用演讲者视图功能&#xff1f; 如果你曾经参加过重要的会议或者演讲&#xff0c;你就会知道&#xff0c;演讲者视图&#xff08;Presenter View&#xff09;对PPT展示至关重要。它不仅能帮助演讲者更好地掌控演讲节奏&#xff0c;还能提供额外的提示和支持&#…

【Unity3D】实现2D小地图效果

目录 一、玩家脚本Player 二、Canvas组件设置 三、小地图相关 四、GameLogicMap脚本修改 基于&#xff1a;【Unity3D】Tilemap俯视角像素游戏案例-CSDN博客 2D玩家添加Dotween移动DOPath效果&#xff0c;移动完成后进行刷新小地图&#xff08;小地图会顺便刷新大地图&…

四.3 Redis 五大数据类型/结构的详细说明/详细使用( hash 哈希表数据类型详解和使用)

四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09; 文章目录 四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09;2.hash 哈希表常用指令(详细讲解说明)2.1 hset …

C#通过3E帧SLMP/MC协议读写三菱FX5U/Q系列PLC数据案例

C#通过3E帧SLMP/MC协议读写三菱FX5U/Q系列PLC数据案例&#xff0c;仅做数据读写报文测试。附带自己整理的SLMP/MC通讯协议表。 SLMP以太网读写PLC数据20191206/.vs/WindowsFormsApp7/v15/.suo , 73216 SLMP以太网读写PLC数据20191206/SLMP与MC协议3E帧通讯协议表.xlsx , 10382…

【算法】经典博弈论问题——威佐夫博弈 python

目录 威佐夫博弈(Wythoff Game)【模板】 威佐夫博弈(Wythoff Game) 有两堆石子&#xff0c;数量任意&#xff0c;可以不同&#xff0c;游戏开始由两个人轮流取石子 游戏规定&#xff0c;每次有两种不同的取法 1)在任意的一堆中取走任意多的石子 2)可以在两堆中同时取走相同数量…

具身智能研究报告

参考&#xff1a; &#xff08;1&#xff09;GTC大会&Figure&#xff1a;“具身智能”奇点已至 &#xff08;2&#xff09;2024中国具身智能创投报告 &#xff08;3&#xff09;2024年具身智能产业发展研究报告 &#xff08;4&#xff09;具身智能行业深度&#xff1a;发展…

把本地搭建的hexo博客部署到自己的服务器上

配置远程服务器的git 安装git 安装依赖工具包 yum install -y curl-devel expat-devel gettext-devel openssl-devel zlib-devel安装编译工具 yum install -y gcc perl-ExtUtils-MakeMaker package下载git&#xff0c;也可以去官网下载了传到服务器上 wget https://www.ke…

STM32 旋转编码器

旋转编码器简介 旋转编码器&#xff1a;用来测量位置、速度或旋转方向的装置&#xff0c;当其旋转轴旋转时&#xff0c;其输出端可以输出与旋转速度和方向对应的方波信号&#xff0c;读取方波信号的频率和相位信息即可得知旋转轴的速度和方向 类型&#xff1a;机械触点式/霍尔传…

后盾人JS--闭包明明白白

延伸函数环境生命周期 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> <…

Python爬虫之——Cookie存储器

目录 专栏导读1、背景介绍2、库的安装3、核心代码4、完整代码总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;请点击——> 一晌小贪欢的博客主页求关注 &…

MFC开发,给对话框添加垂直滚动条并解决鼠标滚动响应的问题

无论在使用QT或者MFC进行界面开发时&#xff0c;都会出现在一个对话框里面存在好多的选项&#xff0c;导致对话框变得非常长或者非常大&#xff0c;就会显现的不美观&#xff0c;在这种情况下通常是添加一个页面的滚动条来解决这个问题&#xff0c;下面我们就来介绍给MFC的对话…

(undone) MIT6.S081 2023 学习笔记 (Day6: LAB5 COW Fork)

网页&#xff1a;https://pdos.csail.mit.edu/6.S081/2023/labs/cow.html 任务1&#xff1a;Implement copy-on-write fork(hard) (doing) 现实中的问题如下&#xff1a; xv6中的fork()系统调用会将父进程的用户空间内存全部复制到子进程中。如果父进程很大&#xff0c;复制过…

分享| RL-GPT 框架通过慢agent和快agent结合提高AI解决复杂任务的能力-Arxiv

结论 “RL-GPT: Integrating Reinforcement Learning and Code-as-policy” RL-GPT 框架为解决大语言模型在复杂任务处理中的难题提供了创新有效的途径&#xff0c; 旨在将强化学习&#xff08;RL&#xff09;和代码即策略相结合&#xff0c; 以解决大语言模型&#xff08…

【Linux权限】—— 于虚拟殿堂,轻拨密钥启华章

欢迎来到ZyyOvO的博客✨&#xff0c;一个关于探索技术的角落&#xff0c;记录学习的点滴&#x1f4d6;&#xff0c;分享实用的技巧&#x1f6e0;️&#xff0c;偶尔还有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原创✍️&#xff0c;感谢支持❤️&#xff01;请尊重原创&#x1…

一个简单的自适应html5导航模板

一个简单的 HTML 导航模板示例&#xff0c;它包含基本的导航栏结构&#xff0c;同时使用了 CSS 进行样式美化&#xff0c;让导航栏看起来更美观。另外&#xff0c;还添加了一些 JavaScript 代码&#xff0c;用于在移动端实现导航菜单的展开和收起功能。 PHP <!DOCTYPE htm…

【算法应用】基于A*-蚁群算法求解无人机城市多任务点配送路径问题

目录 1.A星算法原理2.蚁群算法原理3.结果展示4.代码获取 1.A星算法原理 A*算法是一种基于图搜索的智能启发式算法&#xff0c;它具有高稳定性和高节点搜索效率。主要原理为&#xff1a;以起点作为初始节点&#xff0c;将其加入开放列表。从开放列表中选择具有最小总代价值 f (…

Python-基于PyQt5,json和playsound的通用闹钟

前言&#xff1a;刚刚结束2024年秋季学期的学习&#xff0c;接下来我们继续来学习PyQt5。由于之前我们已经学习了PyQt5以及PyUIC,Pyrcc和QtDesigner的安装&#xff0c;配置。所以接下来我们一起深入PyQt5&#xff0c;学习如何利用PyQt5进行实际开发-基于PyQt5&#xff0c;json和…

预测不规则离散运动的下一个结构

有一个点在19*19的平面上运动&#xff0c;运动轨迹为 一共移动了90步&#xff0c;顺序为 y x y x y x 0 17 16 30 10 8 60 15 15 1 3 6 31 10 7 61 14 15 2 12 17 32 9 9 62 16 15 3 4 12 33 10 9 63 18 15 4 3 18 34 15 12 6…

供应链系统设计-供应链中台系统设计(十)- 清结算中心概念片篇

综述 我们之前在供应链系统设计-中台系统设计系列&#xff08;五&#xff09;- 供应链中台实践概述文章中针对中台到底是什么进行了描述&#xff0c;对于中台的范围也进行划分&#xff0c;如下图所示&#xff1a; 关于商品中心&#xff0c;我们之前用4篇文章介绍了什么是商品中…