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

进程池的制作(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;
}

相关文章:

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

目录 一、进程间通信的理解 1.为什么进程间要通信 2.如何进行通信 二、匿名管道 1.管道的理解 2.匿名管道的使用 3.管道的五种特性 4.管道的四种通信情况 5.管道缓冲区容量 三、进程池 1.进程池的理解 2.进程池的制作 四、源码 1.ProcessPool.hpp 2.Task.hpp 3…...

【Linux】Linux C比较两个 IPv6 网关地址是否相等,包括前缀

功能说明 在 Linux 环境下使用 C 语言比较两个 IPv6 网关地址是否相等&#xff0c;包括前缀 实现步骤 解析 IPv6 地址&#xff1a;使用 inet_pton 将字符串形式的 IPv6 地址转换为二进制形式。解析前缀长度&#xff1a;从地址字符串中提取前缀长度&#xff08;如 /64&#xf…...

【uniapp】uniapp使用java线程池

标题 由于js是性能孱弱的单线程语言&#xff0c;只要在渲染中执行了一些其他操作&#xff0c;会中断渲染&#xff0c;导致页面卡死&#xff0c;卡顿&#xff0c;吐司不消失等问题。在安卓端可以调用java线程池&#xff0c;把耗时操作写入线程池里面&#xff0c;优化性能。 实…...

面试题-Java集合框架

前言 Java集合框架&#xff08;Java Collections Framework&#xff09;是Java平台提供的一套用于表示和操作集合的统一架构。它位于java.util包中&#xff0c;并且自Java 1.2&#xff08;也称为Java 2平台&#xff0c;标准版&#xff0c;即Java SE 2&#xff09;起成为Java平…...

Java基础教程(007):方法的重载与方法的练习

文章目录 6.5 方法的重载6.6 方法练习数组遍历数组最大值 6.5 方法的重载 在 Java 中&#xff0c;方法的重载是指在同一个类中定义多个方法&#xff0c;这些方法具有相同的名称&#xff0c;但参数列表不同。方法的重载是一种实现多态的方式&#xff0c;允许一个方法名以不同的…...

【ESP32】ESP-IDF开发 | WiFi开发 | TCP传输控制协议 + TCP服务器和客户端例程

1. 简介 TCP&#xff08;Transmission Control Protocol&#xff09;&#xff0c;全称传输控制协议。它的特点有以下几点&#xff1a;面向连接&#xff0c;每一个TCP连接只能是点对点的&#xff08;一对一&#xff09;&#xff1b;提供可靠交付服务&#xff1b;提供全双工通信&…...

npm cnpm pnpm npx yarn的区别

npm、cnpm、pnpm、npx、yarn 这几个工具都与 Node.js 项目的包管理和命令执行相关&#xff0c;它们的区别具体如下&#xff1a; 本质与功能定位 npm&#xff1a;是 Node.js 官方的包管理工具&#xff0c;提供了安装、卸载、更新、发布等全方位的包管理功能&#xff0c;还能通…...

debian12.9编译freeswitch1.10.12【默认安装】

服务器操作系统 cat /etc/os-release PRETTY_NAME"Debian GNU/Linux 12 (bookworm)" NAME"Debian GNU/Linux" VERSION_ID"12" VERSION"12 (bookworm)" VERSION_CODENAMEbookworm IDdebian HOME_URL"https://www.debian.org/&quo…...

使用 C/C++ 调用 libcurl 调试消息

在使用 C/C 调用 libcurl 进行 HTTP 请求时&#xff0c;有时我们需要查看请求的/应答消息的内容&#xff08;包括请求头和请求体&#xff09;以方便调试。libcurl 提供了多种方法来捕获和输出这些信息&#xff0c;本文介绍具体的使用方式。 1. libcurl 调试工具简介 libcurl 是…...

【愚公系列】《循序渐进Vue.js 3.x前端开发实践》030-自定义组件的插槽Mixin

标题详情作者简介愚公搬代码头衔华为云特约编辑&#xff0c;华为云云享专家&#xff0c;华为开发者专家&#xff0c;华为产品云测专家&#xff0c;CSDN博客专家&#xff0c;CSDN商业化专家&#xff0c;阿里云专家博主&#xff0c;阿里云签约作者&#xff0c;腾讯云优秀博主&…...

大一计算机的自学总结:异或运算

前言 异或运算这个操作看上去很匪夷所思&#xff0c;实际上作用非常大。 一、异或运算的性质 1.异或运算就是无进位相加。 2.满足交换律、结合律。 3.0^nn&#xff0c;n^n0。 4.若集合B为集合A子集&#xff0c;集合A异或和为x&#xff0c;集合B异或和为y&#xff0c;则集…...

通过protoc工具生成proto的pb.go文件以及使用protoc-go-inject-tag工具注入自定义标签

1.ProtoBuf认识,安装以及用法 参考:[golang 微服务] 3. ProtoBuf认识&#xff0c;安装以及golang 中ProtoBuf使用 2. 使用protoc-go-inject-tag工具注入自定义标签 这里有一个案例: syntaxproto3; package test;option go_package ".;test";message MyMessage {int6…...

C语言练习(29)

13个人围成一圈&#xff0c;从第1个人开始顺序报号1、2、3。凡报到“3”者退出圈子&#xff0c;找出最后留在圈子中的人原来的序号。本题要求用链表实现。 #include <stdio.h> #include <stdlib.h>// 定义链表节点结构体 typedef struct Node {int num;struct Nod…...

Android实训九 数据存储和访问

实训9 数据存储和访问 一、【实训目的】 1、 SharedPreferences存储数据; 2、 借助Java的I/O体系实现文件的存储&#xff0c; 3、使用Android内置的轻量级数据库SQLite存储数据; 二、【实训内容】 1、实现下图所示的界面&#xff0c;实现以下功能&#xff1a; 1&#xff…...

实验一---典型环节及其阶跃响应---自动控制原理实验课

一 实验目的 1.掌握典型环节阶跃响应分析的基本原理和一般方法。 2. 掌握MATLAB编程分析阶跃响应方法。 二 实验仪器 1. 计算机 2. MATLAB软件 三 实验内容及步骤 利用MATLAB中Simulink模块构建下述典型一阶系统的模拟电路并测量其在阶跃响应。 1.比例环节的模拟电路 提…...

SOME/IP--协议英文原文讲解2

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 4.1 Speci…...

matlab中,fill命令用法

在 MATLAB 中&#xff0c;fill 命令用于创建填充多边形的图形对象。使用 fill 可以在二维坐标系中绘制填充的区域&#xff0c;通常用于绘制图形的背景或显示数据分布。 基本语法 fill(X, Y, C)X 和 Y 是同样长度的向量&#xff0c;定义了多边形的顶点坐标。C 是颜色&#xff0…...

【Linux】Linux C判断两个IPv6地址是否有包含关系

功能说明 要判断两个 IPv6 地址是否具有包含关系&#xff0c;包括前缀的比较&#xff0c;可以通过以下步骤实现&#xff1a; 解析 IPv6 地址和前缀&#xff1a;将两个 IPv6 地址和它们的前缀长度解析为二进制形式。生成掩码&#xff1a;根据前缀长度生成掩码。按位比较&#…...

【玩转全栈】----Django基本配置和介绍

目录 Django基本介绍&#xff1a; Django基本配置&#xff1a; 安装Django 创建项目 创建app 注册app Django配置路由URL Django创建视图 启动项目 Django基本介绍&#xff1a; Django是一个开源的、基于Python的高级Web框架&#xff0c;旨在以快速、简洁的方式构建高质量的Web…...

mysql 学习6 DML语句,对数据库中的表进行 增 删 改 操作

添加数据 我们对 testdatabase 数据中 的 qqemp 这张表进行 增加数据&#xff0c;在这张表 下 打开 命令行 query console 在 软件中就是打开命令行的意思 可以先执行 desc qqemp; 查看一下当前表的结构。 插入一条数据 到qqemp 表&#xff0c;插入时要每个字段都有值 insert…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...