进程间通信之管道
文章目录
- 一、管道
- 1. 匿名管道
- 2. 命名管道
进程具有独立性,因此进程间通信的前提是两个进程能看到同一份资源
一、管道
对于进程打开的内存文件,操作系统是以引用计数的方式创建的 file 结构体,如果让两个进程与同一个 file 结构体关联,便可以让两个进程看到同一份资源
由于 file 结构体的缓冲区只有一个,因此只能让一个进程以写的方式,另一个进程以读的方式打开同一个文件,这样便实现了单向的进程间通信
操作系统提供了仅在内存中创建 file 结构体(不在磁盘上创建对应的文件),这种特殊的文件称为管道文件,其中没有名字的称为 匿名管道,有名字的称为 命名管道
1. 匿名管道
进程创建匿名管道成功后,会以读和写两种方式打开管道文件,因此匿名管道通常用于父子进程的进程间通信
系统调用 pipe,头文件 unistd.h
- int pipe(int pipefd[2]),创建匿名管道
返回值:匿名管道创建成功返回 0,出错返回 -1,并且 errno 被设置为相应的出错信息
参数:pipefd 为输出型参数,pipefd[0] 存储读端文件描述符,pipefd[1] 存储写端文件描述符
父子进程通信的步骤:
- 父进程创建管道
父进程以读和写两种方式打开管道文件 - 创建子进程
子进程会拷贝父进程的文件描述符表,因此子进程也会以读和写两种方式打开同一个管道文件 - 父进程和子进程分别关闭自己不需要的读端或写端
一个进程向管道中写入,另一个进程从管道中读取
子进程向匿名管道写入,父进程从匿名管道读取
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{// 创建匿名管道// pipefd[0] 表示读// pipefd[1] 表示写int pipefd[2] = { 0 };if (pipe(pipefd) < 0){// 创建匿名管道失败cout << "匿名管道创建失败: " << errno << " " << strerror(errno) << endl;exit(1);}// 创建子进程pid_t id = fork();assert(id != -1);if (id == 0){// 子进程向匿名管道写入,需要关闭读close(pipefd[0]);// 开始通信int cnt = 0;char buffer[64];while (true){snprintf(buffer, sizeof(buffer), "我是子进程,这是我给你发的第 %d 个信息", ++cnt);// 向匿名管道中写入write(pipefd[1], buffer, strlen(buffer));cout << cnt << endl;// sleep(1); // 让写端慢一点// if (cnt == 3) break; // 模拟写端关闭}close(pipefd[1]);exit(0);}// 父进程从匿名管道读取,需要关闭写close(pipefd[1]);// 开始通信char buffer[64];while (true){// 从匿名管道中读取// sleep(3); // 让读端慢一点// sleep(3); break; // 模拟读端关闭int n = read(pipefd[0], buffer, sizeof(buffer) - 1);if (n > 0){ buffer[n] = '\0';cout << buffer << endl;}else if (n == 0){cout << "写端已经关闭,我读到文件结尾了" << endl;break;}else{cout << "读取错误" << endl;break;}}close(pipefd[0]);// 读端关闭,写端会收到 13 号信号 SIGPIPEint status;waitpid(id, &status, 0);cout << "子进程退出信号: " << (status & 0x7F) << endl;return 0;
}
- 放开子进程代码中的 sleep(1),让写端慢一点,匿名管道中没有数据时,读端会等待写端写入
- 放开父进程中的 sleep(3),让读端慢一点,匿名管道中写满数据时,写端会等待读端读取
- 放开子进程中模拟写端关闭的代码,并且让写端慢一点,写端关闭后,读端读取完匿名管道中的数据后,读端会读取到文件结尾
- 放开子进程中模拟读端关闭的代码,并且让写端慢一点,读端关闭时,此时匿名管道无意义,操作系统会向写端发送 SIGPIPE 13 号信号
命令行中的 | 即为匿名管道,| 会将前一个进程的标准输出重定向到匿名管道,后一个进程的标准输入重定向到匿名管道
通过匿名管道创建进程池:
// processpool.hpp
#include <iostream>
#include <vector>
#include <functional>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>namespace starrycat
{class EndPoint{public:EndPoint(){}EndPoint(pid_t cid, int wfd) : _cid(cid), _wfd(wfd){}~EndPoint(){}public:pid_t _cid;int _wfd;};const int NUM = 5;class ProcessPool{using fun_t = std::function<void(int)>;public:ProcessPool(fun_t Command, int num = NUM){std::vector<int> tmpfd; // 保存子进程不需要的父进程的文件描述符for (int i = 0; i < num; ++i){// 创建管道int pipefd[2] = {0};if (pipe(pipefd) < 0){std::cout << "创建管道失败: " << errno << " " << strerror(errno) << std::endl;exit(1);}// 创建子进程int id = fork();assert(id != -1);if (id == 0){// 子进程从管道中读取,需要关闭写close(pipefd[1]);for (auto e : tmpfd) close(e); // 关闭不需要的文件描述符// 开始通信while (true){// 读取四字节整数的命令int cmd = 0;int n = read(pipefd[0], &cmd, sizeof(int));if (n == sizeof(int)){// 测试std::cout << getpid() << " ";// 执行命令Command(cmd);}else if (n == 0){// 测试std::cout << getpid() << " 读取到文件结尾了" << std::endl;break;}else{std::cout << "读取异常" << std::endl;break;}}close(pipefd[0]);exit(0);}// 父进程向管道中写入,需要关闭读close(pipefd[0]);_endPoints.push_back(EndPoint(id, pipefd[1]));tmpfd.push_back(pipefd[1]);}}ProcessPool(const ProcessPool &p) = delete;ProcessPool &operator=(const ProcessPool &p) = delete;~ProcessPool(){for (auto& e : _endPoints) {close(e._wfd);waitpid(e._cid, nullptr, 0);// 测试std::cout << "等待子进程:" << e._cid << "成功" << std::endl;sleep(1);}}// 规定命令为四字节整数void push(int command){// 以轮训的方式调用子进程static int index = 0;write(_endPoints[index]._wfd, &command, sizeof(int));index++;index %= _endPoints.size();}private:std::vector<EndPoint> _endPoints;};
}// myctrlprocess.cc
#include "processpool.hpp"
#include <iostream>
#include <string>using namespace std;// 子进程个数
const int num = 5;void PrintLog()
{cout << "打印日志任务,正在被执行..." << endl;
}void InsertMySQL()
{cout << "执行数据库任务,正在被执行..." << endl;
}void NetQuest()
{cout << "执行网络请求任务,正在被执行..." << endl;
}void CommandError()
{cout << "任务不存在" << endl;
}void Command(int cmd)
{switch (cmd){case 0:PrintLog();break;case 1:InsertMySQL();break;case 2:NetQuest();break;default:CommandError();break;}
}int main()
{starrycat::ProcessPool ppool(Command);int command = 0;while (true){cout << "请输入命令: ";cin >> command;// 测试if (command == -1) break;ppool.push(command);sleep(1);}return 0;
}
2. 命名管道
命名管道支持两个毫不相关的进程通信,其使用和文件一样
系统调用 mkfifo,头文件 sys/types.h、sys/stat.h
- int mkfifo(const char *pathname, mode_t mode),创建命名管道
返回值:命名管道创建成功返回 0,出错返回 -1,并且 errno 被设置为相应的出错信息
参数:
- pathname 表示创建命名管道的路径名(如果只有文件名,则表示在进程所在的路径下创建)
- mode 表示创建命名管道的文件权限,受 umask 影响
客户端向命名管道写入,服务端从命名管道读取
// namepipe.h
#pragma once// 命名管道文件名
const char* const fifoname = "fifo";// client.cc
#include "namepipe.h"
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>using namespace std;int main()
{// 以写方式打开命名管道int wfd = open(fifoname, O_WRONLY);if (wfd < 0){cout << "打开文件失败" << errno << " " << strerror(errno) << endl;exit(1);}// 开始通信char buffer[1024];while (true){cout << "请输入: ";char* str = fgets(buffer, sizeof(buffer), stdin);if (str == NULL){cout << "客户端退出" << endl;break;}buffer[strlen(buffer) - 1] = '\0'; // 去掉输入的回车符// 输入 quit 表示客户端退出if(strcmp(buffer, "quit") == 0) {cout << "客户端退出" << endl;break;}// 向命名管道写入write(wfd, buffer, strlen(buffer));}close(wfd);return 0;
}// server.cc
#include "namepipe.h"
#include <iostream>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>using namespace std;int main()
{// 创建命名管道umask(0); // 设置进程的权限掩码if (mkfifo(fifoname, 0666) < 0){cout << "创建管道失败: " << errno << " " << strerror(errno) << endl;exit(1);}// 以读方式打开命名管道int rfd = open(fifoname, O_RDONLY);if (rfd < 0){cout << "打开文件失败" << errno << " " << strerror(errno) << endl;}// 开始通信char buffer[1024];while (true){// 从命名管道读取int n = read(rfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = '\0';cout << "客户端: " << buffer << endl;}else if (n == 0){cout << "客户端退出了,服务端也退出" << endl;break;}else{cout << "读取异常" << endl;break;}}close(rfd); unlink(fifoname); // 删除命名管道return 0;
}
先启动 myserver,在启动 myclient
命名管道和匿名管道的通信特性是一样的
mkfifo 文件名
功能:创建命名管道
相关文章:

进程间通信之管道
文章目录 一、管道1. 匿名管道2. 命名管道 进程具有独立性,因此进程间通信的前提是两个进程能看到同一份资源 一、管道 对于进程打开的内存文件,操作系统是以引用计数的方式创建的 file 结构体,如果让两个进程与同一个 file 结构体关联&…...

f12 CSS网页调试_css样式被划了黑线怎么办
我的问题是这样的 class加上去了,但是样式不生效,此时可能是样式被其他样式覆盖了, 解决方案就是 给颜色后边添加一个!important...

vue-制作自动滚动效果
第一步:下载 可以查看官方地址chenxuan0000 npm i vue-seamless-scroll -save 第二步:引用 import vueSeamlessScroll from "vue-seamless-scroll";//注册components: {vueSeamlessScroll,}, 第三步:使用 <vue-seamless…...

[国产MCU]-BL602-开发实例-DMA数据传输
DMA数据传输 文章目录 DMA数据传输1、DMA介绍2、DMA驱动API介绍3、DMA使用示例DMA(Direct Memory Access)是一种内存存取技术,可以独立地直接读写系统内存,而不需处理器介入处理。 在同等程度的处理器负担下,DMA是一种快速的数据传送方式。 BL602的DMA控制器有4组独立专用通…...

Redis压缩列表
区分一下 3.2之前 Redis中的List有两种编码格式 一个是LINKEDLIST 一个是ZIPLIST 这个ZIPLIST就是压缩列表 3.2之后来了一个QUICKLIST QUICKLIST是ZIPLIST和LINKEDLIST的结合体 也就是说Redis中没有ZIPLIST和LINKEDLIST了 然后在Redis5.0引入了LISTPACK用来替换QUiCKLIST中的…...

【SA8295P 源码分析】62 - Android GVM Kernel 内核 make bootimage 过程分析
【SA8295P 源码分析】62 - Android GVM Kernel 内核 make bootimage 过程分析 一、make bootimage 命令执行过程分析1.1 source buid/envsetup.sh 分析1.2 lunch msmnile_gvmq-userdebug 分析1.3 make bootimage:step 1 之 加载配置文件过程分析1.4 make bootimage:step 2 之…...

机器学习——SMO算法推导与实践
一、 硬间隔-SMO算法推导 明天再说,啊。。。。感觉天空明朗了很多,即使现在已经很晚了 还是要打开柯南,看看电视,等待天气预报所说的台风天吧! 一时之间,忽然失去了用markdown语法写下推导过程的勇气。。。…...

mac的终端通过code .指令快速启动vscode
通过在vscode中安装"code"命令工具 打开vsocode,使用快捷键⇧⌘P,然后输入shell,会弹出来“Shell命令:在PATH中安装‘code’命令”浮窗,选择安装就可以了,然后就可以在终端通过code .来快速启动…...

前端系统使用iframe下载文件
需求描述 前端调用后端的接口,获取到文件的路径,并下载。 碰到的问题 页面组件存在与云端的组件库,使用window.open()无法满足需求(在当前页面下载),因为路径是跨域的,所以决定使用iframe的方…...

RabbitMQ - 简单案例
目录 0.引用 1.Hello world 2.轮训分发消息 2.1 抽取工具类 2.2 启动两个工作线程接受消息 2.4 结果展示 3.消息应答 3.1 自动应答 3.2 手动消息应答的方法 3.3 消息自动重新入队 3.4 消息手动应答代码 4.RabbitMQ 持久化 4.1 队列如何实现持久化 4.2 消息实现持久化 5.不…...

《吐血整理》高级系列教程-吃透Fiddler抓包教程(30)-Fiddler如何抓Android7.0以上的Https包-番外篇
1.简介 通过宏哥前边几篇文章的讲解和介绍想必大家都知道android7.0以上,有android的机制不在信任用户证书,导致https协议无法抓包。除非把证书装在系统信任的证书里,此时手机需要root权限。但是大家都知道root手机是非常繁琐的且不安全&…...

服务器被攻击了怎么办?
服务器被攻击是无法避免的,但是我们能通过做好防护措施,提高服务器的安全性,降低被攻击的几率。那么当服务器已经被 攻击了,怎样才能降低损失呢?该怎样补救? 断开网络 全部的攻击都来自于网络,因…...

P1156 垃圾陷阱(背包变形)
垃圾陷阱 题目描述 卡门――农夫约翰极其珍视的一条 Holsteins 奶牛――已经落了到 “垃圾井” 中。“垃圾井” 是农夫们扔垃圾的地方,它的深度为 D D D( 2 ≤ D ≤ 100 2 \le D \le 100 2≤D≤100)英尺。 卡门想把垃圾堆起来,…...

[Docker实现测试部署CI/CD----构建成功后钉钉告警(7)]
目录 15、钉钉告警创建项目群,然后添加机器人添加机器人Jenkins 系统配置项目配置修改Jenkinsfile文件,添加钉钉提示信息测试 不修改Jenkinsfile文件,添加钉钉提示信息测试 15、钉钉告警 创建项目群,然后添加机器人 首先需要在钉…...

UE5 半透明覆层材质
文章目录 前言介绍示例1示例2示例3 前言 本文采用虚幻5.2.1版本演示,介绍半透明覆层材质(覆层材质)。 介绍 半透明覆层材质是 UE5.1 版本 更新的功能,使用半透明覆层材质,可以轻松的给物体表面附着一层材质。 在UE5…...

在Raspberry Pi 4上安装Ubuntu 20.04 + ROS noetic(不带显示器)
在Raspberry Pi 4上安装Ubuntu 20.04 ROS noetic(不带显示器) 1. 所需设备 所需设备: 树莓派 4 B 型 wifi microSD 卡:最小 32GB MicroSD 转 SD 适配器 (可选)显示器,鼠标等 2. 树莓派…...

CommStudio for .NET Crack
CommStudio for .NET Crack CommStudio for.NET使您的应用程序可以轻松地使用串行端口和调制解调器进行通信。CommStudio for.NET是一套全面的组件和可视化调试工具,可将远程系统和设备与visual Studio 2005和visual Studio 2008集成。开发与遗留系统和外部设备集成…...

蓝桥杯上岸考点清单 (冲刺版)!!!
大家好 我是寸铁💪 真题千千万万遍,蓝桥省一自然现! ✌️ 日更3000里,蓝桥眷顾你 🌟 暴力出奇迹,打表过样例 👊 冲刺蓝桥杯省一模板大全来啦 🔥 蓝桥杯4月8号就要开始了 &#…...

AI一键生成短视频
AI一键生成推文短视频 阅读时长:10分钟 本文内容: 结合开源AI,一键生成短视频发布到常见的某音,某手平台,狠狠赚一笔 前置知识: 1.基本的 python 编程知识 2.chatGPT 使用过 3.stable diffution 使用过 成果…...

基于MATLAB长时间序列遥感数据分析(以MODIS数据处理为例)
MATLAB MATLAB是美国MathWorks公司出品的商业数学软件,用于数据分析、无线通信、深度学习、图像处理与计算机视觉、信号处理、量化金融与风险管理、机器人,控制系统等领域。 [1] MATLAB是matrix&laboratory两个词的组合,意为矩阵工厂&a…...

postgresql之内存池-AllocsetContext
一、简介 postgresql大部分的内存分配管理都是通过MemoryContext进行操作的, 多个相关的MemoryContext构成了一个树型结构, 多个树构成了一个森林。 实现了三种MemoryContext: SlabContextGenerationContextAllocSetContext 使用全局变量CurrentMemo…...

QT 使用单例模式
目录 1. 单例模式介绍 2.单例模式实现 1. 单例模式介绍 有些时候我们在做 qt 项目的时候,要用到很多类. 例如我们用到的类有 A,B,C,D. 其中,A 是 B,C,D 中都需要用到的类,A 类非常的抢手. 但是,A 类非常的占内存,定义一个 A 对象需要 500M 内存,假如在 B,C,D 中都定义一个 A 类…...

接口测试——postman接口测试(三)
目录 1. postman介绍与安装 2. postman发送get请求 3. postman发送post请求 1. postman介绍与安装 安装网址:Postman安装教程:留言找我要即可 2. postman发送get请求 import pymysql from flask import Flask,request# 这里是mysql的基本连接信息 c…...

react中hooks的理解与使用
一、作用 我们知道react组件有两种写法一种是类组件,另一种是函数组件。而函数组件是无状态组件,如果我们要想改变组件中的状态就无法实现了。为此,在react16.8版本后官方推出hooks,用于函数组件更改状态。 二、常用API 1、use…...

STM32的电动自行车信息采集上报系统(学习)
摘要 针对电动自行车实时监管不便的问题,设计了一种基于STM32的电动自行车信息采集系统,通过获取电池、位置和行驶状态信息并上报到服务器中,实现实时监管。 通过多路串口请求电池、行驶状态和位置信息,以并发方式进行数据接收、…...

蓝桥杯上岸每日N题 第七期(小猫爬山)!!!
蓝桥杯上岸每日N题 第七期(小猫爬山)!!! 同步收录 👇 蓝桥杯上岸必背!!!(第四期DFS) 大家好 我是寸铁💪 冲刺蓝桥杯省一模板大全来啦 🔥 蓝桥杯4月8号就要开始了 &a…...

【Linux系统编程】冯诺依曼体系结构
目录 前言 什么是冯诺依曼体系结构? 冯诺依曼体系结构如何进行数据处理的? 存储器在冯诺依曼体系中有什么作用? 冯诺依曼体系结构为什么要这样设计? 冯诺依曼结构总结 前言 相信对于冯诺依曼这个人的名字大家一定不会感到陌…...

数据结构--动态顺序表
文章目录 线性表动态顺序表数组与顺序表 接口实现初始化:尾插:尾删头插头删指定位置插入指定位置删除查找摧毁 完整代码 线性表 线性表是数据结构中最基本、最简单也是最常用的一种数据结构。线性表是指由n个具有相同数据类型的元素组成的有限序列。 线…...

笔试数据结构选填题
目录 卡特兰数Catalan:出栈序列/二叉树数 树 二叉树 N01N2 哈夫曼树(最优二叉树)Huffman 度m的哈夫曼树只有度为0和m的结点:Nm(n-1)/(m-1) 平衡二叉树AVL Nh表示深度为h最少结点数,则N00,N11&#…...

# 鸢尾花的案例学习
# 鸢尾花的案例学习 # 1. 导入小型的数据 from sklearn.datasets import load_iris import numpy as np import pandas as pd import seaborn as sbn import matplotlib.pyplot as plt # 2. 获取数据 irisload_iris() # 3.查看数据print("数据集\n ",len(iris.d…...