【lesson44】进程间通信之匿名管道

文章目录

  • 理解进程通信
  • 管道理解
  • 使用管道
    • 1.创建管道
    • 2.fork创建子进程
    • 3.构建单向通信信道
      • 子进程构建通信
      • 父进程构建通信
  • 使用管道的完整版代码
  • 扩展
    • Task.hpp
    • ProcessPool.cc

理解进程通信

进程运行具有独立性—>进程想通信,难度其实是比较大的---->进程通信的本质:先让不同的进程看到同一份资源(内存空间)----->而这个内存空间不能隶属与任何一个进程,更改强调共享

为什么要进程间通信:
为了达到交互数据、控制、通知等目标

进程通信不是目的,而是手段

进程间通信目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信发展

  • 管道---->Linux原生提供的通信方式
  • System V进程间通信-----多进程------单机通信
  • POSIX进程间通信----->多线程----->网络通信

他们都有一定的标准,标准在使用者看来,更多是接口上具有一定规律性

管道理解

什么是管道?
感性理解:
管道有出口,有入口
天然气管道,石油管道,自来水管道…
只能单向通信
传输的都是资源
计算机管道也是传输的资源是数据!

什么是管道?
管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
计算机通信领域的设计者,设计了一种单向通信的方式—管道
管道通信背后是进程之间通过管道进行通信。
在这里插入图片描述
1.分别一读写方式打开一个文件
在这里插入图片描述
2.fork创建子进程
在这里插入图片描述
3.双方各自关闭自己不需要的文件描述符
假设我们只要父进程写入,子进程读取
在这里插入图片描述
管道通信是纯内存形的通信方式,不要把数据写到磁盘

以前的指令

who | wc -l

其中|就是管道
在这里插入图片描述

使用管道

如何做到让不同的进程,看到同一份资源呢?-----fork让子进程继承的----能够让具有血缘关系的进程进行进程间通信—常用于父子进程

管道通信的步骤

1.创建管道

接口
在这里插入图片描述
其中参数pipefd是什么?
pipefd是输出型参数,我们调用pipe把管道的读写两端fd放进pipefd数组中。
在这里插入图片描述
开始创建pipe管道。
在这里插入图片描述
那么pipe的返回值,是什么呢?
在这里插入图片描述
因为错误会放回-1,所以我们直接进行断言,管道都没创建成功我们也没有必要通信。
在这里插入图片描述

2.fork创建子进程

在这里插入图片描述

3.构建单向通信信道

子进程读取,父进程写入
子进程保留读端关闭写端
父进程保留写端关闭读端
在这里插入图片描述

子进程构建通信

在这里插入图片描述

父进程构建通信

在这里插入图片描述

使用管道的完整版代码

#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <assert.h>int main()
{//1.创建管道int pipefd[2] = {0};//pipefd[0]是读端,pipefd[1]是写端int n = pipe(pipefd);assert(n != -1);(void)n;//因为后面不使用,所以直接转成(void)以免报错//2.创建子进程int id = fork();//3.构建单向通信信道,父进程写入子进程读取if(id == -1){//出错perror("create child failed:");}else if(id == 0){//子进程  读取//关闭写端close(pipefd[1]);ssize_t n = 0;//定义数据换出去char buffer[1024];//读取数据while(true){//读取数据n = read(pipefd[0],buffer,sizeof(buffer));//read返回值差错初六if(n > 0){//第n为置为'\0'因为系统不带'\0'buffer[n] = 0;//输出获取到的数据std::cout << "child get a message["<<getpid()<<"] Father# " << buffer << std::endl;}else if(n == 0){//Father写端进程退出不再写数据,child读端进程也退出不再读取数据std::cout << "Father quit,I quit too!" << std::endl;break;}}close(pipefd[0]);exit(0);}else{//父进程 写入//关闭读端close(pipefd[0]);//要发送的数据std::string message = "I am father,I am writting message from you";int count = 0;char buffer[1024];while(true){//构建一个能发生变化的字符串snprintf(buffer,sizeof(buffer),"%s[%d] : %d",message.c_str(),getpid(),count++);//发送数据write(pipefd[1],buffer,strlen(buffer));//故意发送数据慢点以便观察sleep(2);}close(pipefd[1]);//进程等待pid_t ret = waitpid(id,nullptr,0);std::cout << "id: " <<id << "ret: "<<ret << std::endl;assert(ret > 0);(void)ret;}return 0;
}

为什么前面我们不定义全局的buffer来进行通信呢?
因为有写时拷贝的存在,无法更改通信。

扩展

1.管道是用来进行具有血缘关系的进程之间进行通信(常用于父子进程),管道子进程读取数据会等父进程写入后再读,否则就不会读
显示器是一个文件,父子进程同时往显示器写入的时候,有没有说一个进程会等待另一个进程的情况?没有!----缺乏访问控制
管道文件—读取具有访问控制

2.管道具有通过让进程间协同的能力,因为提供了访问控制

3.管道提供面向流式服务----面向字节流

管道写的一方fd关闭,读取的一方read会返回0表示读到了文件结尾。

4.管道是基于文件的,文件的什么周期是随进程的,管道的什么周期也随进程。

管道特点

  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  • 管道提供流式服务
  • 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  • 一般而言,内核会对管道操作进行同步与互斥
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

管道是单向通信就是半双工通信的一种特殊情况,既可以读内容又可以写内容,但是写内容和读内容不能同时进行。

全双工:既可以读内容又可以写内容,读和写可以同时进行。

特殊情况:
1.读快写慢,管道没有数据的时候,读的一端必须等待写的一端。
2.写块读慢,管道写满了就不能再写了。
3.写端关闭,读端读取到0,表示读到文件结尾
4.读端关闭,写端继续写OS会自动终止进程

管道的作用?
父进程可以给子进程派发任务,也称进程池
在这里插入图片描述

Task.hpp

任务代码

#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <functional>
#include <sys/types.h>
#include <unistd.h>typedef std::function<void()> func;std::vector<func> callbacks;
std::unordered_map<int,std::string> desc;void readMySQL()
{std::cout << "sub process["<<getpid()<<"] execute read MySQL task\n" << std::endl;
}void execuleUrl()
{std::cout << "sub process["<<getpid()<<"] execute execuleUrl task\n" << std::endl;
}void encryption()
{std::cout << "sub process["<<getpid()<<"] execute encryption task\n" << std::endl;
}void persistence()
{std::cout << "sub process["<<getpid()<<"] execute persistence task\n" << std::endl;
}void Load()
{desc.insert({callbacks.size(),"readMySQL"});callbacks.push_back(readMySQL);desc.insert({callbacks.size(),"execuleUrl"});callbacks.push_back(execuleUrl);desc.insert({callbacks.size(),"encryption"});callbacks.push_back(encryption);desc.insert({callbacks.size(),"persistence"});callbacks.push_back(persistence);
}void showHandler()
{for(const auto &iter : desc){std::cout << iter.first << "\t" << iter.second << std::endl;}
}int handlerSize()
{return callbacks.size();
}

ProcessPool.cc

#include <iostream>
#include <vector>
#include <unordered_map>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "Task.hpp"int waitcommand(int waitfd,bool &quit)//阻塞式等待
{uint32_t command = 0;//读取指令ssize_t n = read(waitfd,&command,sizeof(command));if(n == 0){quit = true;return -1;}assert(n == sizeof(uint32_t));return command;
}
void sendAndWakeup(pid_t who,int fd,uint32_t command)
{//发送指令write(fd,&command,sizeof(command));std::cout << "main process: call process " << who << " execute " << desc[command] << " through " << fd << std::endl;}#define PROCESS_NUM 5
int main()
{//加载任务Load();// 存储pid : pipefd的映射std::vector<std::pair<pid_t, int>> slots;// 创建子进程for (int i = 0; i < PROCESS_NUM; i++){// 创建管道int pipefd[2] = {0};int n = pipe(pipefd);assert(n == 0);//创建子进程int id = fork();if (id < 0){perror("create chlid failed");}else if (id == 0){// 子进程close(pipefd[1]);while (true){bool quit = false;//读取任务指令int command = waitcommand(pipefd[0], quit);if (quit)break;//执行任务if (command >= 0 && command < callbacks.size()){callbacks[command]();}else{std::cout << "the command unlawful" << std::endl;}}exit(1);}else{// 父进程,关闭读端close(pipefd[0]);//将每个进程的pid和pipefd存入pair<pid_t,int>数组中slots.push_back(std::pair<pid_t, int>(id, pipefd[1]));}}// 父进程派发任务srand((unsigned long)time(nullptr) ^ getpid() ^ 23323123123L); // 让数据源更随机while (true){int select = 0;int command = 0;printf("*****************************************\n");printf("**1.show handlers        2.send command**\n");printf("*****************************************\n");std::cout << "Please Enter Select:";std::cin >> select;if (select == 1){showHandler();}else if (select == 2){std::cout << "Please Enter command:";//选择任务std::cin >> command;//随机选择进程int choice = rand() % slots.size();//发送任务函数sendAndWakeup(slots[choice].first,slots[choice].second,command);}else{std::cout << "the select unlawful" << std::endl;}}//关闭所有子进程的fdfor(const auto& slot :  slots){close(slot.second);}//等待子进程退出,回收子进程所有信息for(const auto& slot :  slots){waitpid(slot.first,nullptr,0);}return 0;
}

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

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

相关文章

DFS——剪枝

dfs在每个点&#xff08;状态&#xff09;的情况比较多&#xff0c;但是节点比较少的时候很常用&#xff0c;我们将每个状态的情况延伸出去&#xff0c;可以画出一棵搜索树。dfs会搜到每一种情况&#xff0c;所以我们实际上可以按照任意顺序来判否。为了优化搜索我们可以在搜索…

喵的一下午(分享今天的趣事)

2024/2/7 晚 喵喵我呀&#xff0c;起的很晚。又没有敲算法。于是&#xff0c;今天就没法发CSDN了。 每天&#xff0c;我还是特别喜欢看阅读量增加没&#xff0c;有没有人点赞&#xff0c;或者说添加收藏&#xff0c;当然&#xff0c;如果又多一个关注的朋友&#xff0c…

数据结构——单链表详解

目录 前言 一.什么是链表 1.概念 ​编辑 2.分类 二.单链表的实现(不带头单向不循环链表) 2.1初始化 2.2打印 2.3创建新节点 2.4头插、尾插 2.5头删、尾删 2.6查找 2.7在指定位置之前插入 2.8在指定位置之后插入 2.9删除pos位置 2.10删除pos之后的 2.11销毁链表…

《java 从入门到放弃》1.1 jdk 安装

1.jdk 是啥&#xff1f; jdk&#xff08;Java Development Kit&#xff09;&#xff0c;简单来说&#xff0c;就是java的开发工具。允许java 程序就是用它了。 jre &#xff0c;里面放的是java用的那些公用的包。 2.jdk下载 2.1 官网下载地址&#xff1a;Java Downloads | …

32USART串口

目录 一.通信接口 二.时序 三.USART简介 ​编辑四.数据帧 五.起始位侦测和采样位置对齐 &波特率计算 六.相关函数 七.编码格式设置 &#xff08;1&#xff09; UTF-8编码&#xff08;有的软件兼容性不好&#xff09;​编辑 &#xff08;2&#xff09;GB2312编码 八.…

【python数据分析基础】—dataframe中index的相关操作(添加、修改index的列名、修改index索引值等)

文章目录 前言一、添加、修改index的列名二、修改index索引值 前言 本文主要讲dataframe结构中index的相关操作&#xff0c;index相当于是数据表的行。 一、添加、修改index的列名 新建一个dataframe表&#xff0c;我们可以自定义index的值&#xff0c;如下&#xff1a; imp…

SSL协议是什么?关于SSL和TLS的常见问题解答

SSL&#xff08;安全套接字层&#xff09;及其后继者TLS&#xff08;传输层安全&#xff09;是用于在联网计算机之间建立经过身份验证和加密的链接的协议。尽管SSL协议在 1999年已经随着TLS 1.0的发布而被弃用&#xff0c;但我们仍将这些相关技术称为“SSL”或“SSL/TLS”。那么…

Instagram SEO如何优化?10个技巧

Instagram SEO 是优化 Instagram 内容以使其在平台搜索结果中被发现的做法。如果你希望你可以更快的让你的Ins获得流量&#xff0c;做好SEO就成功了一半。Instagram 搜索结果包括相关内容、帐户、音频、主题标签和地点&#xff0c;下面为你总结10个策略技巧&#xff01; 一、In…

解析spritf和sscanf与模拟常用字符串函数strchr,strtok(二)

今天又来继续我们的字符串函数的文章&#xff0c;这也是最后一篇了。希望这两篇文章能让各位理解透字符串函数。 目录 strchr strtok sprintf和sscanf strchr strchr 是一个用于在字符串中查找特定字符首次出现位置的函数。以下是解析和模拟实现 strchr 函数的示例&…

【Linux】信号-下

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;【LeetCode】winter vacation training 目录 &#x1f449;&#x1f3fb;信号递达&#xff0c;信号未决&#x…

vue3学习——自定义插件,注册组件(引入vue文件报红线)

在src/components文件夹目录下创建一个index.ts文件 import { App, Component } from Vue import SvgIcon from /components/SvgIcon/index.vue import Pagination from /components/Pagination/index.vue const globalComponents: { [name: string]: Component } { SvgIcon,…

第三模块 面向对象网络并发编程

第三模块 面向对象&网络&并发编程 面向对象基础1. 初识面向对象1.1 对象和self1.2 常见成员1.3 应用示例 2. 三大特性2.1 封装2.2 继承练习题2.3 多态 3. 扩展&#xff1a;再看数据类型总结作业 从今天开始&#xff0c;我们将进入系列课程第3个模块的的学习&#xff0c…

TCP和UDP相关问题(重点)(5)——5.TCP三次握手和四次挥手(非常重要)

5.1三次握手的过程 一次握手&#xff1a;客户端发送带有SYN(x)标志的数据包到服务端&#xff0c;然后客户端进入SYN_SEND状态&#xff0c;等待服务器端的确认。 二次握手&#xff1a;服务端发送带有SYN(y)ACK(x1)标志的数据包到客户端&#xff0c;然后服务端进入SYN_RECV状态。…

Java设计模式大全:23种常见的设计模式详解(三)

本系列文章简介&#xff1a; 设计模式是在软件开发过程中&#xff0c;经过实践和总结得到的一套解决特定问题的可复用的模板。它是一种在特定情境中经过验证的经验和技巧的集合&#xff0c;可以帮助开发人员设计出高效、可维护、可扩展和可复用的软件系统。设计模式提供了一种在…

收到微信发的年终奖。。。

大家好&#xff0c;我是小悟 还剩一天就过除夕了&#xff0c;很多单位都已经放假了&#xff0c;街上的人越来越少&#xff0c;门店关着的很多&#xff0c;说明大家都陆陆续续回自己的家乡过年了。 或许你还在搬砖&#xff0c;坚守节前最后一波工作&#xff0c;或许你正在回家的…

Java学习16-- 面向对象学习45. 面向对象三大特征抽象类和接口

面向对象学习4. 面向对象三大特征 1封装&#xff1a;高内聚(内部细节自己用&#xff0c;外部不能介入)&#xff0c;低耦合(保留很少接口给外部使用)&#xff0c;信息隐藏&#xff08;禁止外界直接访问内部数据(private)&#xff0c;如需要&#xff0c;可通过get/set接口访问&a…

【评论送书】AIGC重塑教育:AI大模型驱动的教育变革与实践

作者&#xff1a;刘文勇 来源&#xff1a;IT阅读排行榜 本文摘编自《AIGC重塑教育&#xff1a;AI大模型驱动的教育变革与实践》&#xff0c;机械工业出版社出版 这次&#xff0c;狼真的来了。 AI正迅猛地改变着我们的生活。根据高盛发布的一份报告&#xff0c;AI有可能取代…

第九个知识点:内部对象

Date对象: <script>var date new Date();date.getFullYear();//年date.getMonth();//月date.getDate();//日date.getDay();//星期几date.getHours();//时date.getMinutes();//分date.getSeconds();//秒date.getTime();//获取时间戳&#xff0c;时间戳时全球统一&#x…

Qt 常用算法及正则表达式

目录 常用算法 正则表达式 常用算法 double c qAbs(a)&#xff0c;函数 qAbs() 返回 double 型数值 a 的绝对值 double max qMax(b,c)&#xff0c;函数 qMax() 返回两个数值中的最大值 int bnqRound(b)&#xff0c;返回一个与浮点数最接近的整数值(四舍五入) int cn q…

玩转量子代码:量子软件入门指南

量子计算领域关注的焦点往往落在硬件上&#xff1a;量子比特和超导电路等。但现在是时候把我们的注意力转移到幕后英雄量子软件上&#xff0c;从将抽象的量子算法转化为可执行的代码到优化电路设计&#xff0c;量子软件起到了举足轻重的作用。 我们在本文中将探究量子编程的基…