linux——进程间通信——管道

 ✅<1>主页::我的代码爱吃辣
📃<2>知识讲解:Linux——进程间通信——管道通信
☂️<3>开发环境:Centos7
💬<4>前言:进程间通信(InterProcess Communication,IPC)是指在不同进程之间传播或交换信息。

目录

一.什么是进程间通信

二.进程间通信目的

 三.进程间通信发展

四.什么是管道

五.匿名管道

 六.父子进程管道通信

1.匿名管道的场景与特点

2. 用fork来共享管道原理

 七.基于匿名管道实现进程池


一.什么是进程间通信

进程间通信(InterProcess Communication,IPC)是指在不同进程之间传播或交换信息。

二.进程间通信目的

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

 三.进程间通信发展

管道

  • 匿名管道pipe
  • 命名管道

System V IPC

  • System V 消息队列
  • System V 共享内存
  • System V 信号量

POSIX IPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

四.什么是管道

  • 管道是Unix中最古老的进程间通信的形式。
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。

五.匿名管道

头文件:#include <unistd.h>
功能:创建一无名管道。
原型:int pipe(int fd[2]);
参数:fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端。
返回值:成功返回0,失败返回错误代码。

 测试代码:

pipe.cc:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{int fds[2]; // f[0]管道读端,f[1]管道写端char buf[100];int len;// 创建管道if (pipe(fds) == -1)perror("make pipe"), exit(1);// read from stdinwhile (fgets(buf, 100, stdin)){len = strlen(buf);// 写入管道if (write(fds[1], buf, len) != len){perror("write to pipe");break;}memset(buf, 0, sizeof(buf));// 从管道中读取if ((len = read(fds[0], buf, 100)) == -1){perror("read from pipe");break;}// 写入显示器if (write(1, buf, len) != len){perror("write to stdout");break;}}
}

 makefile:

pipe:pipe.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -rf  pipe

测试结果:

 六.父子进程管道通信

我们知道fork之后,子进程会继承父进程的代码,数据会发生写时拷贝,那么父进程的文件描述符会不会被继承呢?会的。那么父进程创建的管道,其中的两个文件描述符一个指向管道的读端,一个指向管道的写端,也会被子进程继承。

1.匿名管道的场景与特点

管道的特点:

  1. 管道只具有单向通信的功能。
  2. 管道的本质是文件,因为fd的生命周期是随进程的,所以管道的生命周期也是随进程的。
  3. 管道通信,通常用来进行具有“血缘”关系的进程,进行进程间的通信。常用父子进程通信,
  4. 在管道的通信中,写入的次数,和读取的次数,不是严格匹配的,读写次数没有强相关,是面向字节流。
  5. 具有一定的协同能力,如果写端没有写入,读端会被阻塞——自带同步机制。

特殊场景:

  1. 如果我们read读取完毕了所有的管道数据,如果对方不发,我就只能等待。
  2. 如果我们writer端将管道写满了,我们还能写吗?不能
  3. 如果我关闭了写端,读取完毕管道数据,在读,就会read返回0,表明读到了文件结尾
  4. 写端一直写,读端关闭,会发生什么呢?没有意义。OS不会维护无意义,低效率,或者浪费资源的事情。OS会杀死一直在写入的进程!OS会通过信号来终止进程,(13)SIGPIPE。

测试代码:

#include <iostream>
#include <string>
#include <cerrno>
#include <cassert>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int main()
{// 让不同的进程看到同一份资源!!!!// 任何一种任何一种进程间通信中,一定要 先 保证不同的进程之间看到同一份资源int pipefd[2] = {0}; // pipefd[0] 读端, pipe[1]写端// 1. 创建管道int n = pipe(pipefd);if (n < 0){std::cout << "pipe error, " << errno << ": " << strerror(errno) << std::endl;return 1;}std::cout << "pipefd[0]: " << pipefd[0] << std::endl; // 读端std::cout << "pipefd[1]: " << pipefd[1] << std::endl; // 写端// 2. 创建子进程pid_t id = fork();assert(id != -1);if (id == 0) // 子进程{// 3. 关闭不需要的fd,让父进程进行读取,让子进程进行写入close(pipefd[0]);// 4. 开始通信 -- 结合某种场景const std::string namestr = "hello,我是子进程";int cnt = 1;char buffer[1024];while (true){snprintf(buffer, sizeof buffer, "%s, 计数器: %d, 我的PID: %d", namestr.c_str(), cnt++, getpid());write(pipefd[1], buffer, strlen(buffer));sleep(1);}// 退出时关闭打开的文件描述符close(pipefd[1]);exit(0);}// 父进程// 3. 关闭不需要的fd,让父进程进行读取,让子进程进行写入close(pipefd[1]);// 4. 开始通信 -- 结合某种场景char buffer[1024];int cnt = 0;while (true){// sleep(10);// sleep(1);int n = read(pipefd[0], buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = '\0';std::cout << "我是父进程, child give me message: " << buffer << std::endl;}else if (n == 0){std::cout << "我是父进程, 读到了文件结尾" << std::endl;break;}else{std::cout << "我是父进程, 读异常了" << std::endl;break;}sleep(1);if (cnt++ > 5)break;}// 父进程读端关闭,子进程会收到13号信号close(pipefd[0]);// 回收子进程的僵尸状态int status = 0;waitpid(id, &status, 0);std::cout << "子进程pid:" << id << ",收到的信号sig: " << (status & 0x7F) << std::endl;sleep(1);return 0;
}

测试结果:

2. 用fork来共享管道原理

 

所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想”。

 七.基于匿名管道实现进程池

当没有数据可读时,read调用阻塞,即进程暂停执行,一直等到有数据来到为止。

如果我们使用,父进程来控制写端,子进程进行读取,发送数据让子进程执行特定的任务,我们就可以实现对子进程的控制。

代码:

CtrlProc.cc:

#include <iostream>
#include <vector>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"using namespace std;
#define NUM_PROC 5struct child_pip
{child_pip(int fd, pid_t pid): _fd(fd), _pid(pid){}~child_pip(){}int _fd;pid_t _pid;
};void WaitCommand()
{Task task;int command;while (1){size_t n = read(0, &command, sizeof(int));if (n == 4) // 读取成功{task.funcs[command]();}else if (n == 0) // 读取失败{break;}else{break;}}
}
void creatproc(vector<child_pip> &child_pip_v)
{for (int i = 0; i < NUM_PROC; i++){// 1.创建管道int pipfd[2];pipe(pipfd);// 2.创建子进程pid_t pid = fork();if (pid < 0)perror("fork");// 我们想让子进程从管道读,父进程向管道写if (pid == 0) // 子进程{// 3.关闭不必要的文件描述符close(pipfd[1]);// 3.1重定向,将来子进程指向0号文件描述符读取dup2(pipfd[0], 0);WaitCommand();exit(0);}// 父进程//  3.关闭不必要的文件描述符cout << "子进程pid:" << pid << endl;close(pipfd[0]);// 建立好子进程与管道的映射,父进程的写端口,和子进程pidchild_pip_v.push_back(child_pip(pipfd[1], pid));}
}void ctrlproc(vector<child_pip> &child_pip_v)
{while (1){int command = 0;cin >> command;if (command == -1)break;int i = rand() % NUM_PROC;write(child_pip_v[i]._fd, &command, sizeof(int));}
}void waitproc(vector<child_pip> &child_pip_v)
{int status = 0;for (int i = 0; i < child_pip_v.size(); i++){close(child_pip_v[i]._fd);}// sleep(5);for (int i = 0; i < child_pip_v.size(); i++){waitpid(child_pip_v[i]._pid, &status, 0);cout << "子进程:" << child_pip_v[i]._pid << "退出" << endl;}
}int main()
{vector<child_pip> child_pip_v;creatproc(child_pip_v);ctrlproc(child_pip_v);waitproc(child_pip_v);return 0;
}

 Task.cc:

#include <vector>
#include <iostream>
#include <unistd.h>using namespace std;
typedef void (*fun_t)();void beatxyy()
{cout << "子进程:" << getpid() << ",执行数据库" << endl;
}void beatxyf()
{cout << "子进程:" << getpid() << ",写入日志" << endl;
}void beatwy()
{cout << "子进程:" << getpid() << ",读取网卡" << endl;
}void beatwj()
{cout << "子进程:" << getpid() << ",刷新缓冲区" << endl;
}void beatxjy()
{cout << "子进程:" << getpid() << ",数据比对" << endl;
}struct Task
{Task(){funcs.push_back(beatxyy);funcs.push_back(beatxyf);funcs.push_back(beatwy);funcs.push_back(beatwj);funcs.push_back(beatxjy);}vector<fun_t> funcs;
};

makefile:

CtrlProc:CtrlProc.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -rf CtrlProc

测试结果:

 注意:

为什么这里的waitproc我们要分开成两个循环,如果一个循环,文件描述符会无法关闭完,子进程也就无法退出。

我们关闭了第一个文件描述符,第一个管道由于继承问题,第一个管道还会有后面的子进程也会指向。最终导致我们只能有最后一个子进程先退出了,其他子进程进程陆续退出,此时进程等待也已经结束了,除了最后一个子进程。其他子进程的僵尸状态都没有被回收。

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

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

相关文章

Matlab杂项记录

文章目录 其他do nothing command in matlab代码格式化在同一个m文件中写多个独立的功能函数改变启动时的默认文件夹博文链接 table使用 其他 do nothing command in matlab disp() % Does nothing but allows developer to set a breakpoint here.代码格式化 Matlab编辑器具…

Python的函数

近期遇到了一个没怎么看懂的Python函数的形式。 def twoSum(self, nums: List[int], target: int) -> List[int]: 后来上网查了资料。

(二)激光线扫描-相机标定

1. 何为相机标定? 当相机拍摄照片时,我们看到的图像通常与我们实际看到的不完全相同。这是由相机镜头引起的,而且发生的频率比我们想象的要高。 这种图像的改变就是我们所说的畸变。一般来说,畸变是指直线在图像中出现弯曲或弯曲。 这种畸变我们可以通过相机标定来进行解…

阿里云服务器地域和可用区查询表_地域可用区大全

阿里云服务器地域和可用区有哪些&#xff1f;阿里云服务器地域节点遍布全球29个地域、88个可用区&#xff0c;包括中国大陆、中国香港、日本、美国、新加坡、孟买、泰国、首尔、迪拜等地域&#xff0c;同一个地域下有多个可用区可以选择&#xff0c;阿里云服务器网分享2023新版…

【AntDesign】封装全局异常处理-全局拦截器

[toc] 场景 本文前端用的是阿里的Ant-Design框架&#xff0c;其他框架也有全局拦截器&#xff0c;思路是相同&#xff0c;具体实现自行百度下吧 因为每次都需要调接口&#xff0c;都需要单独处理异常情况&#xff08;code !0&#xff09;&#xff0c;因此前端需要对后端返回的…

【Linux】Linux常用命令—文件管理(上)

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

基于python的网络爬虫搜索引擎的设计

项目介绍 随着互联网的飞速发展&#xff0c;web已经成为人们主要的检索&#xff0c;和发布的主要平台&#xff0c;在海量的数据中如何快速&#xff0c;准确的找到用户所需要的信息成为人们当前所需求的&#xff0c;而网络爬虫就是为了满足这一需要而产生的研究领域。在现实中我…

【typescript】面向对象(下篇),包含接口,属性的封装,泛型

假期第八篇&#xff0c;对于基础的知识点&#xff0c;我感觉自己还是很薄弱的。 趁着假期&#xff0c;再去复习一遍 面向对象&#xff1a;程序中所有的操作都需要通过对象来完成 计算机程序的本质就是对现实事物的抽象&#xff0c;抽象的反义词是具体。比如照片是对一个具体的…

【C语言】八大排序算法

文章目录 一、冒泡排序1、定义2、思想及图解3、代码 二、快速排序1、hoare版本2、挖坑法3、前后指针法4、非递归快排5、快速排序优化1&#xff09;三数取中选key值2&#xff09;小区间优化 三、直接插入排序1、定义2、代码 四、希尔排序1、定义2、图解3、代码 五、选择排序1、排…

基于YOLOv8的安全帽检测系统(3):EMA基于跨空间学习的高效多尺度注意力、效果优于ECA、CBAM、CA,助力行为检测 | ICASSP2023

目录 1.Yolov8介绍 2.安全帽数据集介绍 3.EMA介绍 4.训练结果分析 1.Yolov8介绍 Ultralytics YOLOv8是Ultralytics公司开发的YOLO目标检测和图像分割模型的最新版本。YOLOv8是一种尖端的、最先进的&#xff08;SOTA&#xff09;模型&#xff0c;它建立在先前YOLO成功基础上…

超大视频如何优雅切片

背景 有一次录屏产生了一个大小为33G的文件, 我想把他上传到B站, 但是B站最大只支持4G. 无法上传, 因此做了一个简单的探索. 质疑与思考 a. 有没有一个工具或一个程序协助我做分片呢? 尝试 a. 必剪 > 有大小限制, 添加素材加不进去(而且报错信息也提示的不对) b. PR &…

GO 中优雅编码和降低圈复杂度

本次主要是聊聊关于使用接口抽象和降低圈复杂度的方式 工作中&#xff0c;难免会遇到老项目老代码&#xff0c;不仅仅需要我们维护&#xff0c;可能还需要我们在原来的垃圾代码上进行新增功能或者是进行优化调整 例如 现有的老代码中关于用户系统这一块就已经经是摇摇欲坠&a…

OpenMMLab【超级视客营】——支持InverseForm Loss(MMSegmentation的第三个PR)

文章目录 1. 任务目标1.1 issue1.2 原理相关资料&#xff08;论文讲解&#xff09;InverseFormSTN(Spatial Transformer Networks) 1.3 实现相关资料&#xff08;相关PR&#xff09; 2. 理解原理3. 代码实现3.X checklist3.0 Issue中的有效内容3.1 MMSegmentation支持multiple …

Context应用上下文理解

文章目录 一、Context相关类的继承关系Context类ContextIml.java类ContextWrapper类ContextThemeWrapper类 二、 什么时候创建Context实例创建Context实例的时机 小结 Context类 &#xff0c;说它熟悉&#xff0c;是应为我们在开发中时刻的在与它打交道&#xff0c;例如&#x…

大数据-玩转数据-双流JOIN

一、双流JOIN 在Flink中, 支持两种方式的流的Join: Window Join和Interval Join 二、Window Join 窗口join会join具有相同的key并且处于同一个窗口中的两个流的元素. 注意: 1.所有的窗口join都是 inner join, 意味着a流中的元素如果在b流中没有对应的, 则a流中这个元素就不会…

棉花叶病害数据集

Bacterial Blight&#xff08;细菌性枯萎病&#xff09;&#xff1a;细菌性枯萎病是由细菌引起的棉花疾病&#xff0c;主要受害部位是棉花的叶子和茎。这种病害可以导致叶片枯萎、变色和腐烂&#xff0c;对棉花产量产生不利影响。 Curl Virus&#xff08;卷叶病毒&#xff09;…

仿真调试说明——摘抄龙芯杯官方文件

1.仿真调试说明 你需要具备以下知识&#xff1a; 仿真工具的使用&#xff0c;比如Vivado的XsimVerilog的基本语法 通过本文的学习&#xff0c;你将获得&#xff1a;各类仿真错误排查的方法CPU逻辑出错的调试指导Verilog 运算符的优先级 1.1 调试指导思想概述 全局上的调试原…

多卡片效果悬停效果

效果展示 页面结构 从页面的结构上看&#xff0c;在默认状态下毛玻璃卡片是有层次感的效果叠加在一起&#xff0c;并且鼠标悬停在卡片区域后&#xff0c;卡片整齐排列。 CSS3 知识点 transform 属性的 rotate 值运用content 属性的 attr 值运用 实现页面整体布局 <div …

案例题--Web应用考点

案例题--Web应用考点 负载均衡技术微服务XML和JSON无状态和有状态真题 在选择题中没有考察过web的相关知识&#xff0c;主要就是在案例分析题中考察 负载均衡技术 应用层负载均衡技术 传输层负载均衡技术 就近的找到距离最近的服务器&#xff0c;并进行分发 使用户就近获取…

S32K144 GPIO编程

前面的文章介绍了如何在MDK-Keil下面进行S32K144的开发&#xff0c;下面就使用该工程模板进行GPIO LED的编程试验。 1. 开发环境 S32K144EVB-Q100开发板MDK-Keil Jlink 2. 硬件连接 S32K144EVB-Q100开发板关于LED的原理图如下&#xff1a; 也就是具体连接关系如下&#xf…