当前位置: 首页 > 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…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...

倒装芯片凸点成型工艺

UBM&#xff08;Under Bump Metallization&#xff09;与Bump&#xff08;焊球&#xff09;形成工艺流程。我们可以将整张流程图分为三大阶段来理解&#xff1a; &#x1f527; 一、UBM&#xff08;Under Bump Metallization&#xff09;工艺流程&#xff08;黄色区域&#xff…...