进程间通信--匿名管道
进程间通信介绍
进程间通信目的
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源。
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
管道
什么是管道
管道是Unix中最古老的进程间通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”如:

用fork来共享管道原理

站在文件描述符角度-深度理解管道 (内存级)
左边是进程管理,右边是文件管理,进程通过全局变量能找到文件描述符标,也就找到了对应的file文件也能打开磁盘上的文件拷贝到缓冲区里进行读写,父进程创建子进程,发生写实拷贝,类似于浅拷贝,此时不做文件操作,文件描述符里面的对于关系和父进程一样,struc_file没有关闭,因为父进程还在, struc_file的对应的引用计数不为0就一定不会关闭,既然访问的同一个struc_file,也可以同时访问其中的file文件,通过缓冲区可以进行文件的读写

- 父进程通过读写两种方式打开内存级的文件返回给上层完成管道的创建
- 子进程继承父进程的文件描述符表,发生浅拷贝,也能拿到父进程以读写打开的管道文件
- 父子都看得到,让父子单向通信,父进程写,子进程读,各自关闭掉自己不需要的文件描述符
这个是管道是OS单独设计的,得配上单独的系统调用:pipe 内存级的,不需要文件路径,没有文件名,所以叫匿名管道,那我们怎么保证,两个进程打开的是同一个管道的?
子进程继承了父进程的文件描述符表
站在内核角度-管道本质

匿名管道

#include <unistd.h>
功能:创建⼀⽆名管道
原型
int pipe(int fd[2]);
参数
fd:⽂件描述符数组,其中fd[0]表⽰读端, fd[1]表⽰写端
返回值:成功返回0,失败返回错误代码
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{int fds[2]={0};int n=pipe(fds);if(n<0){cerr<<"Pipe error"<<endl;return 1;}cout<<"fds[0]"<<fds[0]<<endl;cout<<"fds[1]"<<fds[1]<<endl;return 0;
}
0,1,2被三个标准占用了,从3,4开始

父写子读,实现通信
#include<iostream>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<cstring>
using namespace std;void ChildWrite(int fd)
{
char buffer[1024];
int cnt=0;
while(true)
{
snprintf(buffer,sizeof(buffer),"I am child,pid:%d cnt:%d\n",getpid(),cnt++);
write(fd,buffer,strlen(buffer));
sleep(1);
}
}
void FatherRead(int fd)
{char buffer[1024];while(true){buffer[0]=0;ssize_t n=read(fd,buffer,sizeof(buffer)-1);if(n>0){buffer[n]=0;cout<<"child says: "<<buffer<<endl;}}
}
int main()
{ //1.创建管道int fds[2]={0}; //fds[0]读端,fds[1]写端int n=pipe(fds);if(n<0){cerr<<"Pipe error"<<endl;return 1;}cout<<"fds[0]"<<fds[0]<<endl;cout<<"fds[1]"<<fds[1]<<endl;//3.创建子进程 f->r c->wpid_t id=fork();if(id==0){//childclose(fds[0]); //关闭读端ChildWrite(fds[1]);close(fds[1]);exit(0);}
close(fds[1]); //关闭写端
FatherRead(fds[0]);
waitpid(id,nullptr,0);
close(fds[0]);return 0;
}
父进程写的cnt在不断变化,子进程能读到,说明实现了管道通信


五种特性
1.匿名管道,只能用来进行具有血缘关系的进程进行进程间通信(常用父与子,如上述代码)
2.管道文件,自带同步机制(父子进程进行IO同时进行,一个读一个写,不管是父快还是子快,父不断写,子read读不到会阻塞住直到读到为止)
3.管道是面向字节流的

4.管道是单向通信的(要么父写子读,要么子写父读)
5. (管道)文件的生命周期随进程
4种通信情况
1.写慢,读快------>读端阻塞等待写端(进程)
2.写快,读慢------>缓冲区写满了,写要阻塞等待读端
3.写关,读开------>read会读到返回值0,表示文件结尾
//写一条就关
void ChildWrite(int fd)
{
char buffer[1024];
int cnt=0;
while(true)
{
snprintf(buffer,sizeof(buffer),"I am child,pid:%d cnt:%d\n",getpid(),cnt++);
write(fd,buffer,strlen(buffer));
sleep(1);
break;
}
}
//观察n的返回值
void FatherRead(int fd)
{char buffer[1024];while(true){buffer[0]=0;ssize_t n=read(fd,buffer,sizeof(buffer)-1);if(n>0){buffer[n]=0;cout<<"child says: "<<buffer<<endl;}else{cout<< "n:"<<n<<endl;}}
}

4.读关,写开------->写端再写没有意义,OS会杀掉写端进程
#include<iostream>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<cstring>
using namespace std;void ChildWrite(int fd)
{
char buffer[1024];
int cnt=0;
while(true)
{
snprintf(buffer,sizeof(buffer),"I am child,pid:%d cnt:%d\n",getpid(),cnt++);
write(fd,buffer,strlen(buffer));
sleep(1);
}
}
void FatherRead(int fd)
{char buffer[1024];while(true){buffer[0]=0;ssize_t n=read(fd,buffer,sizeof(buffer)-1);if(n>0){buffer[n]=0;cout<<"child says: "<<buffer<<endl;}else{cout<< "n:"<<n<<endl;sleep(4);}break;}
}
int main()
{ //1.创建管道int fds[2]={0}; //fds[0]读端,fds[1]写端int n=pipe(fds);if(n<0){cerr<<"Pipe error"<<endl;return 1;} cout<<"fds[0]"<<fds[0]<<endl;cout<<"fds[1]"<<fds[1]<<endl;//3.创建子进程 f->r c->wpid_t id=fork();if(id==0){//childclose(fds[0]); //关闭读端ChildWrite(fds[1]);close(fds[1]);exit(0);}
close(fds[1]); //关闭写端
FatherRead(fds[0]);
close(fds[0]);int status;
int ret=waitpid(id,&status,0);
//获取到子进程的退出状态
if(ret>0)
{printf("child code: %d exited status: %d\n",(status)>>8&0xff,(status)&0x7f);
}
return 0;
}
发送异常信号13 SIGPIPE

基于匿名管道----进程池
父进程创建多个子进程,用匿名管道分派任务

1.构建进程链,为进程池做准备
#ifndef __PROCESS__POOL_HPP__
#define __PROCESS__POOL_HPP__
#include <iostream>
#include <vector>
#include <unistd.h>
#include<cstdlib>
using namespace std;
const int gdefaultnum = 5; // 要创建几个进程
// 先描述 单个进程
class Channel
{
public:Channel() {}~Channel() {}private:int _wfd;
};// 在组织 进程链
class ChannelManager
{
public:ChannelManager() {}~ChannelManager() {}private:vector<Channel> _channels;
};
2.创建进程池,提供管道条件
// 进程池
class ProcessPool
{
public:ProcessPool(int num) : _process_num(num) {}~ProcessPool() {}bool Create(){int pipefd[2] = {0};for (int i = 0; i < _process_num; i++){// 1.创建管道int n = pipe(pipefd);if (n < 0)return false;}// 2.创建子进程 各自关闭不需要的文件描述符pid_t id = fork();if (id < 0)return false;else if (id == 0){// 子进程 --->读// 3.关闭不需要的文件描述符close(pipefd[1]);exit(0);}else{// 父进程 --->写// 3.关闭不需要的文件描述符close(pipefd[0]);}return true;}private:ChannelManager _cm; // 进程链int _process_num; // 进程个数
};
3.父子各自打印验证是否通信
void BuildChannel(int wfd, pid_t subid){_channels.emplace_back(wfd, subid);// Channel c(wfd,subid);// _channels.push_back(c);}void Print()
{for(auto &chnnel : _channels){
cout<<chnnel.Name()<<endl;}
}void Work(int rfd){while (true){cout << "我是子进程,我的rfd是:" << rfd << endl;sleep(2);}}
//ProcessPool.hpp
#ifndef __PROCESS__POOL_HPP__
#define __PROCESS__POOL_HPP__
#include <iostream>
#include <vector>
#include <unistd.h>
#include <cstdlib>
using namespace std;
const int gdefaultnum = 5; // 要创建几个进程
// 先描述 单个进程
class Channel
{
public:Channel(int fd, pid_t id) : _wfd(fd), _subid(id) { _name = "chnnel-" + std::to_string(_wfd) + "-" + std::to_string(_subid); }~Channel() {}int Fd(){return _wfd;}pid_t Subid(){return _subid;}string Name(){return _name;}
private:int _wfd;pid_t _subid;std::string _name;
};// 在组织 进程链
class ChannelManager
{
public:ChannelManager() {}~ChannelManager() {}void BuildChannel(int wfd, pid_t subid){_channels.emplace_back(wfd, subid);// Channel c(wfd,subid);// _channels.push_back(c);}void Print()
{for(auto &chnnel : _channels){
cout<<chnnel.Name()<<endl;}
}
private:vector<Channel> _channels;
};// 进程池
class ProcessPool
{
public:ProcessPool(int num) : _process_num(num) {}~ProcessPool() {}void Work(int rfd){while (true){cout << "我是子进程,我的rfd是:" << rfd << endl;sleep(2);}}bool Create(){for (int i = 0; i < _process_num; i++){int pipefd[2] = {0};// 1.创建管道int n = pipe(pipefd);if (n < 0)return false;// 2.创建子进程 父子各自关闭不需要的文件描述符pid_t id = fork();if (id < 0)return false;else if (id == 0){// 子进程 --->读// 3.关闭不需要的文件描述符close(pipefd[1]);Work(pipefd[0]);exit(0);}else{// 父进程 --->写// 3.关闭不需要的文件描述符close(pipefd[0]);_cm.BuildChannel(pipefd[1], id);close(pipefd[1]);}}return true;}void Debug(){_cm.Print();}
private:ChannelManager _cm; // 进程链int _process_num; // 进程个数
};#endif
//Main.cc#include"ProcessPool.hpp"int main()
{ProcessPool pp(gdefaultnum);//创建进程池pp.Create();//打印进程池pp.Debug();sleep(1000);return 0;
}
实现通信

4.分配任务,子写父读
void Work(int rfd){while (true){int code = 0;ssize_t n = read(rfd, &code, sizeof(code));if (n > 0){if (n == sizeof(code)){continue;}cout << "子进程[]"<<getpid()<<"]收到一个任务码:" << code << endl;}else if (n == 0){cout << "子进程退出" << endl;break;}else{// 读失败cout << "读取错误" << endl;break;}}}void PushTack(int taskcode){// 1.选择一个子进程,采用轮询,防止负载均衡和负载不均衡auto &c = _cm.Select();cout << "选择一个子进程:" << c.Name() << endl;// 2.发送任务c.Send(taskcode);cout << "发送了一个任务码:" << taskcode << endl;}
完整代码
//ProcessPool.hpp
#ifndef __PROCESS__POOL_HPP__
#define __PROCESS__POOL_HPP__
#include <iostream>
#include <vector>
#include <unistd.h>
#include <cstdlib>
using namespace std;
const int gdefaultnum = 5; // 要创建几个进程
// 先描述 单个进程
class Channel
{
public:Channel(int fd, pid_t id) : _wfd(fd), _subid(id) { _name = "chnnel-" + std::to_string(_wfd) + "-" + std::to_string(_subid); }~Channel() {}int Fd() { return _wfd; }pid_t Subid() { return _subid; }string Name() { return _name; }void Send(int code){int n = write(_wfd, &code, sizeof(code));(void)n;}private:int _wfd;pid_t _subid;std::string _name;
};// 在组织 进程链
class ChannelManager
{
public:ChannelManager() : _next(0) {}~ChannelManager() {}void BuildChannel(int wfd, pid_t subid){_channels.emplace_back(wfd, subid);// Channel c(wfd,subid);// _channels.push_back(c);}// 轮询Channel &Select(){auto &c = _channels[_next];_next++;_next %= _channels.size();return c;}void Print(){for (auto &chnnel : _channels){cout << chnnel.Name() << endl;}}private:vector<Channel> _channels;int _next;
};// 进程池
class ProcessPool
{
public:ProcessPool(int num) : _process_num(num) {}~ProcessPool() {}void Work(int rfd){while (true){int code = 0;ssize_t n = read(rfd, &code, sizeof(code));if (n > 0){if (n == sizeof(code)){continue;}cout << "子进程[]"<<getpid()<<"]收到一个任务码:" << code << endl;}else if (n == 0){cout << "子进程退出" << endl;break;}else{// 读失败cout << "读取错误" << endl;break;}}}bool Create(){for (int i = 0; i < _process_num; i++){int pipefd[2] = {0};// 1.创建管道int n = pipe(pipefd);if (n < 0)return false;// 2.创建子进程 父子各自关闭不需要的文件描述符pid_t id = fork();if (id < 0)return false;else if (id == 0){// 子进程 --->读// 3.关闭不需要的文件描述符close(pipefd[1]);Work(pipefd[0]);exit(0);}else{// 父进程 --->写// 3.关闭不需要的文件描述符close(pipefd[0]);_cm.BuildChannel(pipefd[1], id);close(pipefd[1]);}}return true;}void Debug(){_cm.Print();}void PushTack(int taskcode){// 1.选择一个子进程,采用轮询,防止负载均衡和负载不均衡auto &c = _cm.Select();cout << "选择一个子进程:" << c.Name() << endl;// 2.发送任务c.Send(taskcode);cout << "发送了一个任务码:" << taskcode << endl;}private:ChannelManager _cm; // 进程链int _process_num; // 进程个数
};#endif
//Main.cc#include"ProcessPool.hpp"int main()
{ProcessPool pp(gdefaultnum);//创建进程池pp.Create();//打印进程池//pp.Debug();int task_code = 1;while(true){pp.PushTack(task_code++);sleep(1);}return 0;
}
创建进程池后,OS关闭了没有意义的管道,每次选择一个管道接受消息

相关文章:
进程间通信--匿名管道
进程间通信介绍 进程间通信目的 数据传输:一个进程需要将它的数据发送给另一个进程资源共享:多个进程之间共享同样的资源。通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件&…...
人工智能治理的两会声音:技术规范与伦理风险探讨
摘要 在最近的两会期间,科技界代表聚焦人工智能(AI)治理问题,提出多项建议。这些建议涵盖AI技术规范、伦理风险预防与控制等方面。代表们强调建立健全法律法规和技术标准体系,确保AI健康发展。同时,重视公众…...
C#opencv 遍历图像中所有点 不在圆范围内的点变为黑色,在圆范围内的保持原色
C#opencv 遍历图像中所有点 不在圆范围内的点变为黑色,在圆范围内的保持原色 安装 Install-Package OpenCvSharp4 Install-Package OpenCvSharp4.Windows 普通实现 using System; using System.Collections.Generic; using System.Linq; using OpenCvSharp; // 添加OpenCV引用…...
基于SSM + JSP 的图书商城系统
基于SSM的图书商城 网上书城、图书销售系统、图书销售平台 |Java|SSM|HTML|JSP| 项目采用技术: ①:开发环境:IDEA、JDK1.8、Maven、Tomcat ②:技术栈:Java、…...
Powershell如何查询 windows defender是否开启
可以通过PowerShell使用以下方法检查Windows Defender(Microsoft Defender Antivirus)的状态: 方法1:使用 Get-MpComputerStatus 命令 此命令会返回Microsoft Defender的全面状态,包括实时保护、病毒定义版本等&#…...
【漫话机器学习系列】133.决定系数(R²:Coefficient of Determination)
决定系数()详解 决定系数()是回归分析中用于评估模型拟合优度的一个重要统计指标。它表示自变量(特征变量)能够解释因变量(目标变量)变异的程度,取值范围为 [0,1] 或 (−…...
【MySQL】数据库简要介绍和简单应用
目录 数据库简要介绍 SQL 的简单应用 需要注意的: 数据库简要介绍 数据库(database)是指长期存储在计算机内,有组织的、可共享的数据集合。它可视为一个电子化的文件柜,用来存储电子文件,用户可以对文件中的数据进行査询、新增、更新、删…...
【Kubernets】Deployment 和 StatefulSet 有什么区别?什么时候用 StatefulSet?
Deployment 和 StatefulSet 的区别 在 Kubernetes 中,Deployment 和 StatefulSet 都用于管理 Pod,但它们适用于不同的场景。 1. Deployment:管理无状态应用 特点: 无状态:Pod 之间相互独立,不需要保持顺…...
Machine Learning: 十大基本机器学习算法
机器学习算法分类:监督学习、无监督学习、强化学习 基本的机器学习算法: 线性回归、支持向量机(SVM)、最近邻居(KNN)、逻辑回归、决策树、k平均、随机森林、朴素贝叶斯、降维、梯度增强。 机器学习算法大致可以分为三类: 监督学习算法 (Sup…...
洛谷 P2801 教主的魔法 题解
之前学过 莫队 算法,其运用了分块思想;但是我居然是第一次写纯种的分块题目。 题意 给你一个长度为 n n n 的序列 a a a(一开始 ∀ a i ∈ [ 1 , 1000 ] \forall a_i\in[1,1000] ∀ai∈[1,1000])。要求执行 q q q 次操作&…...
【八股文】ArrayList和LinkedList的区别
先讲讲两者是如何实现的 ArrayList public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {transient Object[] elementData; private int size; } 通过源码可以看出,ArrayLis…...
函数的引用/函数的默认参数/函数的占位参数/函数重载
函数的引用 #include<iostream> using namespace std;//引用的本质在c内部实现,是一个指针常量//交换函数 //1.值传递 void mySwap01(int a, int b) {int temp a;a b;b temp; }//2.地址传递 void mySwap02(int *a, int *b) {int temp *a;*a *b;*b temp…...
《鸿蒙系统下AI模型训练加速:时间成本的深度剖析与优化策略》
在当今数字化浪潮中,鸿蒙系统凭借其独特的分布式架构与强大的生态潜力,为人工智能的发展注入了新的活力。随着AI应用在鸿蒙系统上的日益普及,如何有效降低模型训练的时间成本,成为了开发者与研究者们亟待攻克的关键课题。这不仅关…...
.npy文件介绍
.npy 文件是 NumPy 库专用的二进制文件格式,用于高效存储和加载 NumPy 数组(即矩阵或多维数组)。这种格式保留了数组的维度、数据类型(dtype)、形状(shape)等元信息,加载时无需手动解…...
汇编语言 | 王爽 | 学习笔记
汇编语言 | 王爽 | 学习笔记 文章目录 汇编语言 | 王爽 | 学习笔记一、基础知识1、指令2、存储器3、总线1、总线2、CPU对存储器的读写3、CPU对外设的控制 4、内存地址空间 二、寄存器1、寄存器2、通用寄存器3、8086CPU给出物理地址的方法4、段寄存器1、CS和IP2、DS 和 [address…...
JumpServer基础功能介绍演示
堡垒机可以让运维人员通过统一的平台对设备进行维护,集中的进行权限的管理,同时也会对每个操作进行记录,方便后期的溯源和审查,JumpServer是由飞致云推出的开源堡垒机,通过简单的安装配置即可投入使用,本文…...
java字符串案例 //要求:将输入的字符串中的数字转换为罗马数字,长度小于9(运用方法:查表法)
package test13; import test11.S;import java.util.Scanner; public class Num {public static void main(String[] args){ // I II III IV V VI VII VIII IX//要求:将输入的字符串中的数字转换为罗马数字,长度小于9(运用方法:查表法&#x…...
EDID读取学习
简介 Video BIOS可以被认为是一个具有独立硬件抽象层的操作系统。它不会阻止或监视操作系统、应用程序或设备驱动程序对硬件的直接访问。虽然不推荐,但一些DOS应用程序确实可以改变基本的硬件设置,而根本不需要通过视频BIOS。大多数现代应用程序和操作系统都避免直接使用硬件…...
【笔记】深度学习模型训练的 GPU 内存优化之旅:综述篇
开设此专题,目的一是梳理文献,目的二是分享知识。因为笔者读研期间的研究方向是单卡上的显存优化,所以最初思考的专题名称是“显存突围:深度学习模型训练的 GPU 内存优化之旅”,英文缩写是 “MLSys_GPU_Memory_Opt”。…...
车载以太网测试-13【网络层-IGMP协议】
目录 1 摘要2 IGMP协议概述2.1 IGMP 在 TCP/IP 协议栈中的位置2.2 IGMP 与以太网的关系2.3 为什么需要IGMP协议?2.4 IGMP报文结构2.4.1 IGMPv1 报文结构2.4.2 IGMPv2 报文结构2.4.3 IGMPv3 报文结构 3 IGMP通信原理3.1 GMP 的通信流程3.2 IGMP协议完整流程示例 4 总…...
2024山东大学计算机复试上机真题
2024山东大学计算机复试上机真题 2024山东大学计算机复试机试真题 历年山东大学计算机复试上机真题 历年山东大学计算机复试机试真题 在线评测:传动门:pgcode.cn 最长递减子序列 题目描述 输入数字 n,和 n 个整数,输出该数字…...
Vue 计算属性与 Data 属性同名问题深度解析
文章目录 1. 问题背景与核心概念1.1 Vue 响应式系统架构1.2 核心概念定义 2. 同名问题的技术分析2.1 同名场景示例2.2 问题发生机制 3. 底层原理剖析3.1 Vue 初始化流程3.2 响应式系统关键代码 4. 问题解决方案4.1 最佳实践建议4.2 错误处理机制 5. 性能影响分析5.1 递归调用性…...
深入理解 Xtensa 架构 ESP32 内存架构(SRAM、IRAM、IROM、DRAM、DROM详解)
在 ESP32 及其他 Xtensa 架构 MCU 中,内存被划分为不同的区域,以优化性能和存储管理。这些内存区域包括 SRAM, IRAM, DRAM, IROM, DROM,它们各有用途。 1. 内存区域总览 ESP32 的内存架构主要由: SRAM(Static RAM&am…...
每日一题——63. 不同路径 II
题目链接:63. 不同路径 II - 力扣(LeetCode) 代码: class Solution { public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m obstacleGrid.size();int n obstacleGrid[0].size();…...
如何配置 Docker 以实现无需 sudo 使用
1. 背景知识:为什么需要 sudo? Docker 是一个容器化平台,其核心组件包括: Docker 守护进程(dockerd):负责管理容器的创建、运行和销毁。Docker CLI:用户通过命令行工具(…...
[文献阅读] 可变形卷积DCN - Deformable Convolutional Networks
**文献信息:**Deformable Convolutional Networks arxiv.org/abs/1703.06211 发表于ICCV 2017,提出了可变形卷积DCN(Deformable ConvNets) 摘要 卷积神经网络(CNN)由于其构建模块固定的几何结构天然地局限…...
【统计学相关笔记】2. 多元正态的Cochran定理
fisher 引理 如何说明一个线性变换和二次型独立: 二次型矩阵和线性变换阵乘积0即可。...
蓝桥杯刷题——第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
一、0握手问题 - 蓝桥云课 算法代码: #include <iostream> using namespace std; int main() {int sum0;for(int i49;i>7;i--)sumi;cout<<sum<<endl;return 0; } 直接暴力,题意很清晰,累加即可。 二、0小球反弹 - 蓝…...
Canoe Panel常用控件
文章目录 一、Panel 中控件分类1. 指示类控件2. 功能类控件3. 信号值交互类控件4. 其他类控件 二、控件使用方法1. Group Box 控件2. Input/Output Box控件3. Static Text控件4. Button控件5. Switch/Indicator 控件 提示:Button 和 Switch 的区别参考 一、Panel 中…...
【软考-架构】11.3、设计模式-新
✨资料&文章更新✨ GitHub地址:https://github.com/tyronczt/system_architect 文章目录 项目中的应用设计模式创建型设计模式结构型设计模式行为型设计模式 💯考试真题题外话 项目中的应用 在实际项目中,我应用过多种设计模式来解决不同…...
