IPC进程间通信-管道

news2024/11/29 1:44:42

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

文章目录

  • 一、了解进行间通信
    • ①进程间通信的必要性
    • ②进程间通信的技术背景
  • 二、管道
    • ①管道原理
      • 管道原理,三步走
      • 管道pipe
    • ②匿名管道
      • 进程间通信demo代码(管道)
    • ③管道特点总结
    • ④简单的进程池--管道应用
      • 完整代码
    • ⑤命名管道
      • 原理
      • mkfifo
      • 小实验

一、了解进行间通信

进程间通信,简称IPC(inter-process communication)

进程的运行具有独立性,带来的直接结果就是进程间想要通信的话,提高了难度,进程间通信的本值就是让不同的进程看到同一份资源(内存空间),并且这一份资源不能属于任何一个进程(就算是写入缓冲区,另一个进程再去读,也是读不到的,因为写时拷贝

所谓的独立性,不是绝对的独立,而是大部分情况下,是独立的,该进程运行终止后,不会去引用其他进程

①进程间通信的必要性

如果是单进程,那么也就无法使用并发的能力,更无法实现多进程协同
进程间通信的目的就在于:传输数据,同步执行流,消息通知等等---->也就是进程间协同

1.数据传输:一个进程需要将它的数据发送给另一个进程(一个进程对数据加工成半成品,再交给另一个)
2.资源共享:多个进程之间共享同样的资源(某些资源就是需要他们共享)
3.通知事件:一个进程需要向另一个或一组进程发送信息,通知它发生了某种事件(比如进程终止时通知父进程)
4.进程控制:有些进程希望完全控制另一个进程的执行(比如Debug进程),此时控制进程希望能够拦截另一个进程的所有行为,并能够及时知道它的状态改变

进程间通信不是目的,而是手段,目的在于让多进程协同

②进程间通信的技术背景

1.进程是具有独立性的,进程地址空间+页表 保证进程运行的独立性
2.通信的成本比较高,设计的时候就把进程设计成了天然的独立性强的模块

二、管道

①管道原理

管道是当代Linux计算机上,比较简单的一种通信方式

什么是管道?
管道,有入口,有出口
管道的特带就是只能单向通信,管道内部传送的都是数据

管道就是用来传送数据的,至于什么数据,依据应用场景,并且管道是一种单向通信的方式。

管道原理,三步走

管道通信 背后是进程之间通过 管道进行通信,每一个进程的task_struct,里面有files_struct指针,这个结构体当中就有fd_array文件描述符表,找到对应的file_struct(这个也就是文件对应的结构体,里面可以找到inode,内核缓冲区)

过程:
1.分别以读和写的mode,打开一个文件
比如读对一个的fd文件描述符是3,写对应的fd是4
在这里插入图片描述

2.fork()创建子进程
创建新的task_struct,又因为进程的独立性,子进程也会有自己的文件描述符表,父进程的部分PCB会拷贝给子进程,像文件描述符这种东西也会。这时候,一个文件就被两个进程以读写的方式打开
在这里插入图片描述

这不就是让不同的进程看到同一份内存空间吗?管道底层就是通过文件的方式实现的

3.双方进程各自关闭自己不需要的文件描述符
这一步是在规定方向,比如我让父进程写,让子进程读,就保留父进程写的fd,关闭读的fd,子进程保留读的fd,关闭写的fd
在这里插入图片描述

以上的这种方式就叫做管道通信,所以管道其实就是文件,两个进程在通信的时候也完全没必要将内容刷新到磁盘,应为刷新到磁盘对于通信双方来说是没有任何意义的,这是纯内存级的通信,如果还要刷到外设,就太慢了,所以进程间通信是不需要持久化的,这样效率才是最高的,并且进程间通信的数据大部分属于临时数据

管道pipe

在这里插入图片描述

管道是Unix当中最古老的进程间通信方式(但不代表不存在了),我们把一个进程链接到另一个进程的数据流称为一个"管道"
在这里插入图片描述

其次,管道分为1.匿名管道 2.命名管道

②匿名管道

在这里插入图片描述
参数说明:
pipe是创建一匿名管道
需要一个数组 fd[0]是读端 fd[1]是写端
返回值:pipe成功返回0,失败返回错误码

pipe的三个过程在上面已经说过
1.父进程创建管道(打开一个文件流)
2.fork子进程(子进程复制部分父进程PCB,与管道建立联系)
3.父子进程关闭不需要fd,控制管道方向(其实不关也行,只要控制好代码就可以了,关了严谨一些,避免代码写错)

这种fork让子进程继承,能够让具有血缘关系的进程继承进程间通信,常用于父子进程

进程间通信demo代码(管道)

#include <iostream>
#include <unistd.h>
#include <string>
#include <cstdio>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
int main()
{
	int pipefd[2]={0];//pipe需要使用的数组
	int n = pipe(pipefd);//床架管道成功
	assert(n!=-1);
	void(n);
	//release模式下,assert直接就没了,那么n就只是被定义而没有被使用,就可以出现大量告警
	//避免这些告警,那么void(n)一下,证明被使用过
#ifdef DEBUG//条件编译
	pid_t id = fork();
	assert(id>=0);//<0即fork错误
	if(id==0)//子
	{
		close(pipdfd[1]);//关闭写
		char buffer[1024];//读
		while(true)
		{
			ssize_t read(pipefd[0],buffer,sizeof buffer-1);//留一个位置存'\0',系统调用
			if(s>0)
			{
				buffer[s]=0;
				//Father说的话
				cout<<"child get a message"<<getpid()<<"]"<<"Father#"<<buffer<<endl;
			{
		}
		exit(0);//子进程退出,文件描述符会被自动关闭,所以不用写close(pipefd[0]);
	}
		//父
		close(pipdfd[0]);//关闭读
		string message="我是父,我正在给你发消息";//发消息
		int count=0;//记录消息条数
		char send_buffer[1024];
		while(true)
		{
			//构造一个变化的字符串
			//写入send_buffer
			snprintf(send_buffer,sizeof send_buffer,"%s[%d] : %d",message.c_str(),getpid)_,count++);
			
			//写消息
			write(pipefd[1],send_buffer,strlen(send_buffer));
			sleep(1);
		}
		pid_t ret = waitpid(id,nullptr,0);//阻塞子进程
		assert(ret<0);
		(void)ret;
		close(pipefd[1]);//父进程并没有退出
	return 0;
}

在这里插入图片描述

③管道特点总结

1.管道是一种进程间通信方式,最大的特点就是用来具有血缘关系的进程进程进程间通信,常用于父子通信
2.父进程在写的时候,按照1s的时间间隔打印,但是在子进程中,并没有按照子进程的规则来立马进行读取,也是难找1s的时间间隔读取—具有访问控制(子进程在等待父进程写入,这个访问控制是由于单向管道导致的。例如显示器也是一个文件,父子同时向显示器写的时候,并不会等待,还会互相干扰,这就是缺乏访问控制)
3.管道提供的是面向流式的服务(面向字节流),一般需要通过指定协议来进行数据区分(我可能读一次,就是别人上千次的写,这就是流式服务)
4.管道是基于文件的,文件的声明周期随进程,所以管道的生命周期是随进程的(需要所有进程都关闭,管道才会自动释放)
5.管道是单向通信的,就是半双工通信的一种特殊模式(要么在发,要么在收就是半双工)

①写快,读慢,写满就不能写了,管道是文件,缓冲区有固定大小
②写慢,读快,管道没有数据的时候,必须等待(具有访问控制)
③写关,读0(read返回0),表示读到结尾
④读关,写继续写,OS会终止写进程,因为写的数据没有任何意义,没人读

④简单的进程池–管道应用

在这里插入图片描述
过程描述

1.创建多个进程(进行for循环,先打开管道pipe,然后fork出子进程,建立同喜,while(true)让该子进程等待任务,for多少次就有多少继承,把子进程的pid和子进程的管道pipefd放进slots表中,也就是知道他们在哪里等命令就可以了)
2.父进程写命令进fd对应文件,子进程读取并执行,通过洗牌算法(分发算发),达到单机的负载均衡
3.派发任务,这里采用随机数方案(进程池)
4.关闭fd与继承退出

完整代码

//Task.hpp代码
#pragma once//避免重复包含头文件
#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <unordered_map>
#include <vector>

//typedef std::function<void> func;
using func = std::function<void()>;//包装器
//C++11的用法

std::vector<func> callbacks;//函数回调数组。条件调用以下方法
std::unordered_map<int,std::strig> desc;//方法描述


//定义四个任务!!
void readMySQL()
{//就取个名字,不访问MySQL
	std::cout<<"sub process["<<getpid()<<"]执行访问数据库的任务"<<std::endl;
}
void execuleUrl()
{
	std::cout<<"sub process["<<getpid()<<"]执行url解析"<<std::endl;
}
void cal()
{
	std::cout<<"sub process["<<getpid()<<"]执行加密任务"<<std::endl;
}
void save()
{
	std::cout<<"sub process["<<getpid()<<"]执行数据持久化任务"<<std::endl;
}

void load()
{
	//装载  方法描述
	//第一个参数是方法描述desc的对应下标
	desc.insert({callback.size(),"readMySQL: 读取数据库"});//列表初始化转成pair
	callbacks.push_back(readMySQL);
	desc.insert({callback.size(),"execuleUrl: 进行Url解析"});
	callbacks.push_back(execuleUrl);
	desc.insert({callback.size(),"cal: 进行加密计算"});
	callbacks.push_back(cal);
	desc.insert({callback.size(),"save: 进行数据的保存"});
	callbacks.push_back(save);
}

//ProcessPool.cpp代码
#include <iostream>
#include <unistd.h> //pipe
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <cstring>
#include <string>
#include <cassert>
#include <vector>
#include <utility>//实用工具 --> make_pair
#include <Task.hpp>
#include <cstdlib>
#include <ctime>
using namespace std;
#define PROCESS_NUM 5//五个进程

int waitCommand(int waitFd,bool &quit)
{//哪个fd的进程等待命名
	uint32_t/*四个字节的命令*/ command=0;
	//fd这个进程,读取命令,多长
	ssize_t s= read(waitFd,&command,sizeof command);
	if(s==0)//==0就是读到结尾
	{
		quit=true;
		return -1;
	}
	assert(s==sizeof(unit32_t));
	//必须是四个字节
	return command;
}

void SendAndWakeUp(pid_t who,int fd,uint32_t command)
{//发送命令并唤醒子进程
	write(fd,&command,sizeof(command));
	cout<<"main process: call process "<<who<<"execute "<<desc[command]<<"through "<<fd<<endl;
}

int main()
{
	load();//装载方法和方法的描述
	vector<pair<pid_t,int>> slots;//pid pipefd;
	//记录进程与pipefd的关系,便于从进程池中找到进程
	
	//创建多个进程
	for(int i=0;i<PROCESS_MIN;i++)
	{
		//创建管道
		int pipefd[2]={0};
		int n=pipe(pipefd);
		assert(n==0);
		void(n);
		//创建子进程
		pid_t id = fork();
		assert(id!=-1);
		if(id==0)//父
		{
			close(pipefd[1]);
			while(true)
			{
				//等命令
				bool quit=false;
				int command = waitCommand(pipefd[0],quit);
				if(quit) break;
				//执行命令
				if(command>=0&&command<HandlerSize())
				{
					callbacks[command]();//调用对应回调函数
				}
				else
				cout<<"非法command"<<command<<endl;
			}
			exit(1);
		}
		//father
		close(pipefd[0]);
		slots.push_back(make_pair(id,pipefd[1]));
		//创建出一堆子进程,放入slots,然后通过slots选择给进程池中哪个进程发消息
	}
	
	srand((unsigned int)time(nullptr)^getpid()^23323123123L);//让数据更随机
	//srand(time(0));
	
	while(true)
	{
		it command= rand()%HanderSize();
		int choice = rand()%slots.size();
		SendAndMakeUp(slots[choice].first,slots[choice].second,command);
		//PID,pipefd,command;  发送命令并唤醒子进程
		sleep(1);
	}

	for(const auto slot:slots)
	{//关闭fd,读到文件结尾,子进程退出
		close(slot.second);
	}
	for(const auto slot:clots)
	{//回收子进程信息
		waitpid(slot.first,nullptr,0);
	}
	return 0;
}

⑤命名管道

原理

命名管道是通过一个进程创建一个文件流,用PCB找到files_struct结构体,然后通过fd_array找到对应文件的file_struct,让另一个进程来指向这个被打开的文件即可完成通信

但是,普通的文件,打开操作了之后都是会持久化的,那效率就太低了,因为进程间通信是纯内存级的,并且大部分数据都是临时数据,不需要写入磁盘,所以操作系统就提出可以在磁盘创建一种新的文件–管道文件,特点就是可以被打开使用,但是并不会进行数据的持久化(管道文件除了文件内容之外什么都有)

mkfifo

取名为make fifo ,因为管道本来就是先进先出的

mkfifo 文件名

在这里插入图片描述

//创建管道文件
mkfifo(path,mode);//路径和权限
//操作管道文件
int fd = open(path,读写方式);//O_RDONLY等等
//通信代码
...操作文件即可
//关闭文件
close(fd);
unlike(path);//删除管道文件

小实验

在这里插入图片描述
一个进程写入,此时出阻塞状态,对方还没有打开读
在这里插入图片描述

成功读取

在这里插入图片描述

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

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

相关文章

CANoe-System And Communication Setup介绍

如同经典通信模式中,在Simulation Setup界面创建网络、添加节点、导入dbc数据库一样,新型通信模式使用System And Communication Setup完成这些配置 System And Communication Setup是通信配置的中心起点,在这里你可以配置: 应用层(application models)通信层(communic…

【自学Python】Python bytes类型

Python bytes类型 Python bytes类型教程 Python3 新增了 bytes 类型&#xff0c;用于代表字节序列。 字符串(string) 是一串字符组成的序列&#xff0c;字符串处理的基本单位是字符&#xff0c;bytes 类型是一串字节组成的序列&#xff0c;bytes 类型处理的基本单位是字节。…

三维重建基础: 坐标系 (更新中)

文章目录概述计算机视觉&#xff1a;投影矩阵M&#xff1a; 世界坐标系 -> 像平面坐标系相机坐标系 -> 像平面坐标系&#xff1a;世界坐标系 -> 相机坐标系计算机图形&#xff1a;MVP变换view/camera 变换常见坐标系定义colmap中的坐标系线性代数相关基础待办附录概述…

微信小程序安装 Vant 组件库与API Promise组件库

在项目内右键空白处选择在外部终端打开2、在终端窗口输入 npm init -y,创建package-lock.jsonnpm init -y3、在终端输入npm i vant/weapp1.3.3 -S --production&#xff0c;创建node_modules文件夹npm i vant/weapp1.3.3 -S --production4、详情&#xff0d;本地设置&#xff0…

安信证券资管清算重要业务在原生分布式数据库的创新实践

作者&#xff1a;安信证券信息技术委员会运维部系统平台室 刘盛彩、肖昭、张杰 来源:《金融电子化》 近日&#xff0c;《国务院关于数字经济发展情况的报告》&#xff08;以下简称报告&#xff09;提请十三届全国人大常委会第三十七次会议审议&#xff0c; 报告提出&#xff1a…

【Vue + Koa 前后端分离项目实战8】使用开源框架==>快速搭建后台管理系统 -- part8 【权限控制联调】

不要为了不值得的事和人感伤悲秋 本章主要关联章节&#xff1a;【Vue Koa 前后端分离项目实战3】使用开源框架&#xff1e;快速搭建后台管理系统 -- part3 权限控制行为日志_小白Rachel的博客-CSDN博客_koa权限管理 本章主要实现综合的系统权限管理 目录 一、权限控制说明 二…

【Linux】Linux常用指令(28个,以及热键,关机重启)

文章目录1、ls 指令2、pwd 命令3、cd 指令4、touch 指令5、mkdir 指令6、rmdir和rm 指令7、man 指令8、cp 指令9、mv 指令10、cat11、echo(输出&#xff0c;输入&#xff0c;追加重定向)12、wc13、more14、less15、head16、tail(以及管道 | )17、date18、cal19、find(which、wh…

KubeSphere使用外部ES进行日志收集(多行日志)

环境kubesphere &#xff1a; v3.3.1Docker&#xff1a;20.10.8Fluent-Bit&#xff1a;2.0.6-2.0.8ESKibana&#xff1a;7.9.3Docker日志示例{"log":"2023-01-10 11:32:50.021 - INFO --- [scheduling-1] traceId: p6spy : 1|conn-0|statement|SELECT fd_id A…

Java 泛型的介绍和使用

什么是泛型? 1.数据类型变得广泛,或者是数据类型变量化,这就是泛型.(广泛的类型). 2.有一个变量是用来存放一个数据类型,这就叫做泛型. 比如泛型T存放的是一个int 就表示为:Tint 当然T也可以是其他的数据类型,就取决我们给它数据类型. 3.泛型允许程序员在强类型程序设计语…

kafka zookeeper单机版安装部署全教程 含安装包

kafka zookeeper安装部署全教程 含安装包 大家好&#xff0c;我是酷酷的韩~ 一.基础软件安装 1.JDK安装部署 linux安装jdk1.8全教程(包含安装包) 二.安装包准备 1.kafka (kafka_2.12-2.8.0.tgz) https://kafka.apache.org/downloads 2.zookeeper (apache-zookeeper-3.7.…

最近爆火的电子血氧仪的基本原理

继口罩、抗原、药品之后,最近电子血氧仪的价格也开始水涨船高。从一个多月前的100多元,暴涨到了300多元。 那么,这类家用的电子血氧仪是如何工作的呢?测量数据到底准不准?今天就带大家来分析一下。 一、血氧仪工作原理 血氧仪是一种监测脉搏、血氧饱和度等指标的医疗器械…

人工智能-EM算法

目录1、EM算法2、EM算法介绍3、EM算法实例1、EM算法 EM算法&#xff1a;期望最大化算法。解决数据缺失情况下的参数估计问题 它是一个基础算法&#xff0c;是HMM等的基础 期望步:E步,最大步M步 步骤&#xff1a; 1&#xff0c;根据已经给出的观测数据&#xff0c;估计出模型参…

世界上第一款启用 QMK/VIA 的薄型无线机械键盘 Keychron K3 Pro 我刚刚入手

文章目录1. Keychron K3 Pro 是什么2. 键盘参数3. 打字音效测试4. 超薄5. 支持热插拔6. 薄型 Gateron 开关7. 薄型双射 PBT 键帽8. 多功能键9. 键盘部件可自定制10. 电池11. QMK 和 VIA 定制12. 产品清单13. 与 keychron k3 对比14. 与 keychron 其他系列对比14.1 为什么没有选…

Swin Transformer阅读笔记

Swin Transformer 使用了移动窗口的层级式的Vit&#xff08;Hierarchical Vision Transformer using Shifted Windows&#xff09; 总体来说&#xff1a;Swin Transformer想让Transformer像卷积神经网络一样&#xff0c;可以分为多个block&#xff0c;可以做层级式特征提取&a…

Flowable工作流——基础篇

1. 介绍 Flowable是BPMN的一个基于Java的软件实现&#xff0c;但是不仅仅限于BPMN&#xff0c;还有DMN决策表和CMMN Case管理引擎&#xff0c;并且有自己的用户管理&#xff0c;微服务API的功能&#xff0c;是一个服务平台。 是由开发了Acitivity6的开发人员&#xff0c;再次升…

nacos的使用

此篇博客是对nacos安装运行进行简单的介绍&#xff0c;后续博客会介绍下nacos的简单使用。nacos的安装安装可以去github上进行下载&#xff0c;下载地址&#xff08;不建议下载最新版本&#xff0c;可以找比较稳定的版本&#xff09;可以点解Tags查看所有历史版本我下载的是2.2…

基于 js 制作一个贪吃蛇小游戏

目录前言&#xff1a;项目效果展示&#xff1a;代码实现思路&#xff1a;使用方法&#xff1a;实现代码&#xff1a;总结&#xff1a;前言&#xff1a; 在工作学习之余玩一会游戏既能带来快乐&#xff0c;还能缓解生活压力&#xff0c;跟随此文一起制作一个小游戏吧。 描述&…

ESXI8.0一键安装黑群晖DSM7

&#x1f388; 作者&#xff1a;互联网-小啊宇 &#x1f388; 简介&#xff1a; CSDN 运维领域创作者、阿里云专家博主。目前从事 Kubernetes运维相关工作&#xff0c;擅长Linux系统运维、开源监控软件维护、Kubernetes容器技术、CI/CD持续集成、自动化运维、开源软件部署维护…

如何做好项目管理

项目管理概述 什么是项目 项目 是为创造独特的产品、服务或者成果而进行的临时性工作 项目三要素&#xff1a;临时性、独特性、渐进明确性 什么是项目管理 项目管理通过合理运营和整合项目相关活动&#xff0c;以满足项目目标达成的过程 项目与日常工作的区别 项目&…

设计测试用例

⭐️前言⭐️ 这篇文章主要介绍测试用例相关的知识&#xff0c;一个优秀的测试人员&#xff0c;需要具备设计优秀测试用例的能力。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主将持续更新学习记录收获&…