【Linux】进程间通信 -- 管道

对于进程间通信的理解

首先,进程间通信的本质是,让不同的进程看到同一份资源(这份资源不能隶属于任何一个进程,即应该是共享的)。而进程间通信的目的是为了实现多进程之间的协同。
但由于进程运行具有独立性(虚拟地址空间+页表 保证了进程运行的独立性),所以要实现进程间的通行难度会比较大。
管道通信作为进程间通信的一种方式,Linux原生就能提供。其通信方式又分为两种:匿名管道 和 命名管道。

匿名管道

匿名管道通信常用于父子进程间的通信。通过fork创建子进程,让具有血缘关系的进程能够进行通信。
其实现通信的步骤主要有3步:

  1. 父进程分别以读和写方式打开同一个文件
  2. fork()创建子进程
  3. 父子进程各自关闭自己不需要的文件描述符

在这里插入图片描述
如上图看管道本质还是文件。
既然管道通信,首先要能够创建出管道。pipe系统接口可以帮助创建管道。其参数pipefd是一个数组,

// pipefd[0]对应读端的文件描述符
// pipefd[1]对应写端的文件描述符
int pipe(int pipefd[2]);
// 匿名管道通信测试
void Test1()
{// 1.创建管道int pipefd[2] = {0};int ret = pipe(pipefd);if(ret != 0){perror("pipe");exit(1);}// 测试打开的文件描述符cout << "pipefd[0]: " << pipefd[0] << endl;cout << "pipefd[1]: " << pipefd[1] << endl;// 2.创建子进程pid_t pid = fork();if(pid > 0){// 3.构建单向通行的信道,父进程写入,子进程读取// 3.1.父进程 -- 写// 关闭读端close(pipefd[0]);int count = 0;while(true){// 不断写如变化的信息string msg = "hello world" + to_string(count++);write(pipefd[1], msg.c_str(), msg.size());sleep(1);if(count > 5){cout << "write quit" << endl;break;}}// 关闭写端close(pipefd[1]);// 4.等待子进程pid_t wpid = waitpid(pid, nullptr, 0);if(wpid == -1){perror("waitpid");exit(3);}}else if(pid == 0){// 3.2.子进程 -- 读// 关闭写端close(pipefd[1]);// 不断读取信息char receive[128] = {0};while(true){ssize_t size = read(pipefd[0], receive, 127);if(size > 0){cout << "receive: " << receive << endl;}else if(size == 0) {cout << "write quit, read quit" << endl;break;}else{perror("read");exit(4);}}// 关闭读端close(pipefd[0]);}else {perror("fork");exit(2);}
}

在这里插入图片描述
通过匿名管道我们还可以模拟进程池的设计。

// 简单的进程池设计
#define PROCESS_NUM 5using f = function<void()>;
unordered_map<int, f> task;void load()
{task[1] = [](){cout << "sub process[" << getpid() << "]->void Task1()" << endl;};task[2] = [](){cout << "sub process[" << getpid() << "]->void Task2()" << endl;};task[3] = [](){cout << "sub process[" << getpid() << "]->void Task3()" << endl;};task[4] = [](){cout << "sub process[" << getpid() << "]->void Task4()" << endl;};
}void sendTask(int fd, pid_t pid, int task_num)
{write(fd, &task_num, sizeof(task_num));cout << "process[" << pid << "] execute " << "task" << task_num << " by " << fd << endl;
}int waitTask(int fd)
{int task_num = 0;ssize_t size = read(fd, &task_num, sizeof(task_num));if(size == 0){return 0;}if(size == sizeof(task_num)){return task_num;}return -1;
}void Test2()
{load();vector<pair<int, pid_t>> process;// 创建多个进程for(int i = 0; i < PROCESS_NUM; ++i){// 创建管道int pipefd[2] = {0};int ret = pipe(pipefd);if(ret != 0){perror("pipe");exit(1);}// 创建子进程pid_t pid = fork();if(pid == 0){// 子进程 -- 读close(pipefd[1]);while(true){// 等待任务int task_num = waitTask(pipefd[0]);if(task_num == 0){break;}else if(task_num >= 1 && task_num <= task.size()){task[task_num]();}else{perror("waitTask");exit(3);}}exit(0);}else if (pid < 0){perror("fork");exit(2);}// 父进程读端关闭close(pipefd[0]);process.emplace_back(pipefd[1], pid);}// 父进程 -- 写srand((unsigned int)time(0));while(true){// 选择一个进程 -- 随机数方式的负载均衡int process_num = rand() % process.size();// 选择一个任务// int task_num = rand() % task.size() + 1;int task_num = 0;cout << "please enter your task num: ";cin >> task_num;// 派发任务sendTask(process[process_num].first, process[process_num].second, task_num);}// 关闭fdfor(const auto& e : process){close(e.first);}// 回收子进程for(const auto& e : process){waitpid(e.second, nullptr, 0);}
}

在这里插入图片描述

命名管道

可以用mkfifo命令创建一个命名管道。如下图是一个命名管道的小实验。
在这里插入图片描述
也可以通过mkfifo接口进行命名管道文件的创建。
在这里插入图片描述
命名管道通信的测试。

// 1. log.hpp
#include <iostream>enum ErrLevel
{lev_0,lev_1,lev_2,lev_3,lev_4
};const std::string error[] = {"err_0","err_1","err_2","err_3","err_4"
};std::ostream& Log(const std::string& msg, int level)
{std::cout << " | " << (unsigned int)time(0) << " | " << error[level] << " | " << msg << " |";return std::cout;
}// 2. comm.hpp
#include <sys/types.h>
#include <sys/stat.h>
#include <wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>using namespace std;#include "log.hpp"#define MODE 0666
#define SIZE 128// 命名管道,通过文件路径,让不同进程能看到这同一份资源
string named_pipe_path = "/home/zs/linux/testcpp/fifo.ipc";// 3. server.cpp
static void getMsg(int fd)
{char buffer[SIZE];while(true){memset(buffer, '\0', sizeof(buffer));ssize_t size = read(fd, buffer, sizeof(buffer) - 1); // ssize_t - long intif(size > 0){cout << "[" << getpid() << "]" << "client say:" << buffer << endl;}else if(size == 0){cerr << "[" << getpid() << "]" << "read end of file, client quit, then server quit" << endl;break;}else{perror("read");break;}}
}void test()
{// 1.创建管道文件if(0 != mkfifo(named_pipe_path.c_str(), MODE)){perror("mkfifo");exit(1);}Log("创建管道文件成功", lev_0) << endl;// 2.文件操作int fd = open(named_pipe_path.c_str(), O_RDONLY);if(fd < 0){perror("open");exit(2);}Log("打开管道文件成功", lev_0) << endl;for(int i = 0; i < 3; ++i){pid_t pid = fork();if(pid == 0){// 3.通信getMsg(fd);exit(0);}}for(int i = 0; i < 3; ++i){waitpid(-1, nullptr, 0);}// 4.关闭文件close(fd);Log("关闭管道文件成功", lev_0) << endl;unlink(named_pipe_path.c_str()); // 通信完毕,删除管道文件Log("删除管道文件成功", lev_0) << endl;
}// 4. client.cpp
void test()
{// 1.获取管道文件int fd = open(named_pipe_path.c_str(), O_WRONLY);if(fd < 0){perror("open");exit(1);}// 2.通信string message;while(true){cout << "please enter your message: ";getline(cin, message);write(fd, message.c_str(), message.size());}// 关闭close(fd);
}

在这里插入图片描述

管道通信总结

  1. 管道常用来进行具有血缘关系的进程间的通信
  2. 管道让进程间协同,提供了访问控制
  3. 管道提供的是面向流式的通信服务
  4. 管道是基于文件的,文件的生命周期跟随进程,管道的生命周期也跟随进程
  5. 管道用于单向通信,属于半双工通信的一种特殊情况

管道本质是文件,又和传统的文件又不一样。管道文件不会将数据刷新到磁盘。
匿名管道通过父子继承的方式看到同一份资源,命名管道通过文件路径的唯一性看到同一份资源,从而达到不同进程间通信的目的。
对于管道文件:
如果写的一方很快,读的一方很慢,当管道写满时,写端必须等待;
如果写的一方很慢,读的一方很快,当管道没有数据时,读端必须等待;
如果写端先被关闭了,读端会读到文件结尾;
如果读端先被关闭了,操作系统会终止写端进程。

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

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

相关文章

密码加密解密之路

1.背景 做数据采集&#xff0c;客户需要把他们那边的数据库连接信息存到我们系统里&#xff0c;那我们系统就要尽可能的保证这部分数据安全&#xff0c;不被盗。 2.我的思路 1.需要加密的地方有两处&#xff0c;一个是新增的时候前端传给后端的时候&#xff0c;一个是存到数…

异步爬取+多线程+redis构建一个运转丝滑且免费http-ip代理池 (三)

内容提要: 如果说,爬取网页数据的时候,我们使用了异步,那么将数据放入redis里面,其实也需要进行异步;当然,如果使用多线程或者redis线程池技术也是可以的,但那会造成冗余; 因此,在测试完多线程redis搭配异步爬虫的时候,我发现效率直接在redis这里被无限拉低下来! 因此: 最终的r…

从0开始学习JavaScript--JavaScript中的集合类

JavaScript中的集合类是处理数据的关键&#xff0c;涵盖了数组、Set、Map等多种数据结构。本文将深入研究这些集合类的创建、操作&#xff0c;以及实际应用场景&#xff0c;并通过丰富的示例代码&#xff0c;帮助大家更全面地了解和应用这些概念。 数组&#xff08;Array&…

SystemVerilog学习 (11)——覆盖率

目录 一、概述 二、覆盖率的种类 1、概述 2、分类 三、代码覆盖率 四、功能覆盖率 五、从功能描述到覆盖率 一、概述 “验证如果没有量化&#xff0c;那么就意味着没有尽头。” 伴随着复杂SoC系统的验证难度系数成倍增加&#xff0c;无论是定向测试还是随机测试&#xff…

安全框架springSecurity+Jwt+Vue-1(vue环境搭建、动态路由、动态标签页)

一、安装vue环境&#xff0c;并新建Vue项目 ①&#xff1a;安装node.js 官网(https://nodejs.org/zh-cn/) 2.安装完成之后检查下版本信息&#xff1a; ②&#xff1a;创建vue项目 1.接下来&#xff0c;我们安装vue的环境 # 安装淘宝npm npm install -g cnpm --registryhttps:/…

软件测试/测试开发/人工智能丨基于Spark的分布式造数工具:加速大规模测试数据构建

随着软件开发规模的扩大&#xff0c;测试数据的构建变得越来越复杂&#xff0c;传统的造数方法难以应对大规模数据需求。本文将介绍如何使用Apache Spark构建分布式造数工具&#xff0c;以提升测试数据构建的效率和规模。 为什么选择Spark&#xff1f; 分布式计算&#xff1a;…

easyExcel注解详情

前言11个注解字段注解 类注解基础综合示例补充颜色总结 11个注解 ExcelProperty ColumnWith 列宽 ContentFontStyle 文本字体样式 ContentLoopMerge 文本合并 ContentRowHeight 文本行高度 ContentStyle 文本样式 HeadFontStyle 标题字体样式 HeadRowHeight 标题高度 HeadStyle…

Python将原始数据集和标注文件进行数据增强(随机仿射变换),并生成随机仿射变换的数据集和标注文件

Python将原始数据集和标注文件进行数据增强&#xff08;随机仿射变换&#xff09;&#xff0c;并生成随机仿射变换的数据集和标注文件 前言前提条件相关介绍实验环境生成随机仿射变换的数据集和标注文件代码实现输出结果 前言 由于本人水平有限&#xff0c;难免出现错漏&#x…

OpenCV快速入门:图像滤波与边缘检测

文章目录 前言一、噪声种类与生成1.1 椒盐噪声1.2 高斯噪声1.3 彩色噪声 二、卷积操作2.1 卷积基本原理2.2 卷积操作代码实现 三、线性滤波3.1 均值滤波均值滤波原理均值滤波公式均值滤波代码实现 3.2 方框滤波方框滤波原理方框滤波公式方框滤波代码实现 3.3 高斯滤波高斯滤波原…

redis非关系型数据库(缓存型数据库)——中间件

【重点】redis为什么这么快&#xff1f;&#xff08;应届&#xff09; ①redis是纯内存结构&#xff0c;避免磁盘I/O的耗时 ②redis核心模块是一个单进程&#xff0c;减少线程切换和回收线程资源时间 ③redis采用的是I/O的多路复用机制&#xff08;每一个执行线路可以同时完…

npm install 下载不下来依赖解决方案

背景 最近在构建 前端自动化部署 的方案中发现了一个问题&#xff0c;就是我在npm install的时候&#xff0c;有时候成功&#xff0c;有时候不成功&#xff0c;而且什么代码也没发生更改&#xff0c;报错也就是那么几个错&#xff0c;所以在此也整理了一下遇到这种情况&#xf…

如何使用 WPF 应用程序连接 FastReport报表

随着期待已久的FastReport WPF的发布&#xff0c;您不再需要使用 FastReport .NET 来处理基于 WPF 的项目。 不久前&#xff0c;在 FastReport .NET 中使用 WPF 还相当不方便。并非一切都进展顺利&#xff1b;连接 FastReport.dll 和许多其他问题存在问题。我们重新思考了该方…

2023年中职“网络安全“—Web 渗透测试①

2023年中职"网络安全"—Web 渗透测试① Web 渗透测试任务环境说明&#xff1a;1.访问地址http://靶机IP/task1&#xff0c;分析页面内容&#xff0c;获取flag值&#xff0c;Flag格式为flag{xxx}&#xff1b;2.访问地址http://靶机IP/task2&#xff0c;访问登录页面。…

面试题c/c++--语言基础

一 、语言基础 1.1 指针 野指针&#xff1a;指针指向的位置是不可知的 悬空指针&#xff1a;指针最初指向的内存已经被释放了的一种指针 两种指针都指向无效内存空间&#xff0c; 即不安全不可控 。需要在定义指针后且在使用之前完成初始化或者使用 智能指针来避免 智能指针 智…

获取阿里云Docker镜像加速器

1、阿里云官网&#xff08;www.aliyun.com&#xff09;注册账号 2、打开“控制台首页” 控制台首页地址&#xff1a;https://home.console.aliyun.com/home/dashboard/ProductAndService 3、点击“概览->容器镜像服务 ACR” 4、打开“镜像工具->镜像加速器”页面&#x…

【grafana | clickhouse】实现展示多折线图

说明&#xff1a; 采用的是 Visualizations 的 Time series&#xff0c;使用的 clickhouse 数据源 在工作中遇到了一个需求&#xff0c;写好了代码&#xff0c;需要在grafana上展示在一个项目中所有人的&#xff0c;随时间的代码提交量变化图 目前遇到的问题&#xff1a;展示…

FFmpeg常用命令行讲解及实战一

文章目录 前言一、学习资料参考二、FFmpeg 选项1、主要选项①、主要命令选项②、举例 2、视频选项①、主要命令选项②、举例1&#xff09;提取固定帧2&#xff09;禁止输出视频3&#xff09;指定视频的纵横比 3、音频选项①、主要命令选项②、举例 4、字幕选项①、主要命令选项…

负载均衡简介

负载均衡 负载均衡&#xff08;Load Balance&#xff0c;简称 LB&#xff09;是高并发、高可用系统必不可少的关键组件&#xff0c;目标是 尽力将网络流量平均分发到多个服务器上&#xff0c;以提高系统整体的响应速度和可用性。 负载均衡的分类和OSI模型息息相关&#xff0c…

【CHI】Ordering保序

本节介绍CHI协议所包含的支持系统保序需求的机制&#xff0c;包括&#xff1a; • Multi-copy atomicity • Completion response and ordering • Completion acknowledgment • Transaction ordering 一、 Multi-copy atomicity CHI协议中所使用的memory model要求为mu…

【面试经典150 | 数学】Pow(x, n)

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;快速幂-递归方法二&#xff1a;快速幂-迭代 其他语言python3 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主…