【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&…

福州大学《物联网中间件技术实验》题库选择与实验要求

一、 实验目的 1.掌握中间件系统框架的搭建&#xff1b; 2.掌握JAVA EE服务器开发技术&#xff1b; 二、 实验仪器、开发环境 1.开发工具&#xff1a;MyEclipse Professional&#xff08;注意不是Eclipse&#xff09; 2.开发环境&#xff1a;JDK1.6&#xff0c;MySQL&…

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:/…

flask实现session开发

要在Flask应用中实现会话&#xff08;session&#xff09;开发&#xff0c;你可以使用Flask内置的session模块。以下是一个示例代码&#xff0c;演示在Flask应用中启用和使用会话功能&#xff1a; from flask import Flask, session, redirect, url_for, requestapp Flask(__…

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

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

easyExcel注解详情

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

快速弄懂Python3.10的一些新特性与使用场景

Python 3.10的新特性不仅增强了语言的功能性&#xff0c;也提供了更丰富的工具&#xff0c;让开发者能更高效、更准确地编写代码。接下来将通过一些实际的使用场景和方法来探索这些新特性。 1. “精确类型”参数化内置集合 Python 3.10引入了更精确的方式来指定内置集合的类型…

GoogleTest测试框架-Gest和GMock

在B站和油管上面找到涉及到GoogleTest单元测试框架的Gtest和Gmock的三个不错的视频资源&#xff1a; 油管视频链接&#xff1a; Google Test and Mock Platform, Complete Tutorial. Part 1: Google TestGoogle Test and Mock Platform - Part 2: GMock MatchersGoogle Test …

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;每一个执行线路可以同时完…

java面试八股文2023完整版详解110题附带答案

以下是一份Java面试八股文2023&#xff0c;涵盖了Java编程语言的核心概念和常用技术&#xff0c;帮助你更好地准备面试。 1. Java语言有哪些特点&#xff1f; Java语言是一种面向对象的编程语言&#xff0c;具有简单、面向对象、分布式、多线程、动态等优点。它是一种跨平台的…

vue 自定义指令

vue 自定义指令 分类 全局指令 Vue.directive()局部指令 directives vue 自定义指令 钩子函数 vue3 自定义指令 分类 全局指令 app.directive()局部指令 directives vue3 自定义指令 钩子函数 vue 自定义指令 除了Vue提供的内置指令之外&#xff0c;你还可以自定义指令来扩展…

P1281 书的复制

P1281 书的复制 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 书按顺序给k个人&#xff0c;进行抄写&#xff0c;求抄写页数最多的人所用的时间的最小值。最大值最小&#xff0c;考虑二分。 又因为题目要求要尽可能让前面的人少抄写&#xff0c;那么就要求后面的多抄写&…

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;访问登录页面。…