深入理解 Linux 管道:创建与应用详解(匿名管道进程池)

在现代操作系统中,进程间通信(IPC)是实现多任务、多进程协作的关键技术之一。Linux 提供了多种 IPC 机制,本博客将帮助您详细的理解进程间通信的原理

首先,在学习管道之前,我们先理解一下管道的存在是为了什么,顾名思义,是为了实现进程间的通信。而进程间的通信难道随随便便就可以进行某种交流了吗?显然是不可能的,所以我们讲

进程的通信也是需要某些协同的!所以协同的前提条件是什么呢? 是不是要先能够通信,通信的内容就是数据,数据间又是有类别的,所以要传递的不可能仅仅是数据还有它的相关属性!

那这就难办了,难办怎么办,wc那就别办,当然了进程可不像我们这么任性,难办归难办,还是得办,由于进程间是具有独立性的,进程 = 内核数据结构pcb + 代码数据

所以我们的第一个条件就来了,要想通信,我们是不是需要先让这些独立的进程之间可以看到一份共同的数据啊,你能看到我也能看到,你改动我也能看到,这不就完成初步通信了吗,可我们怎么能看到同一份资源呢,因为进程之间是独立的啊,他俩是不可能干这个事情了,这个时候我们的老大哥OS就来了,操作系统大哥说了:你们俩先别独立了,我这有个桥正好把你两家连上,你俩以后通信就在这通信,这OS发话谁敢不听,没办法,两个进程就这样完成了初步通信,

由上图可以看出,两个进程的通信都在同一个内核级文件缓冲区内,可是现在问题是,进程间的通信是需要成本的,而且成本还很高,那我操作系统的目的是什么,不就是让系统变的简单吗,那如何能缩减工作量呢?很简单--复用内核代码,所有就有了我们的两种管道--命名管道匿名管道 

1.匿名管道,也就是适用于具有血缘关系的进程,什么意思,父进程和子进程还有孙子进程,在我们之前讲子进程的时候知道父子进程之间是应用了写时拷贝技术的,大部分内容是浅拷贝的,因此这时我们采用的是匿名管道,那什么是管道呢?

管道顾名思义就是一端流入,一端流出,也就是单向通信,向我们平常打电话是不是你可以给对方说话,你说话的同时对方也能说话,这叫做双工通信,我们管道是一种特殊的半双工通信,什么意思呢,半双工通信就是你们之间是不能一起沟通的,这样会冲突,双方会听不清对方说的什么,可是它本质上还是支持你们互相说话的,我们特殊就特殊在它不支持互相说话,所以只能一端写,一端读,这样是不是就可以理解管道的通信了,那我们接下来了解一下管道的用法

#include<unistd.h>

int pipe(int fd[2]);

这个fd[2]是输出型参数,

pipe函数定义中的fd参数是一个大小为2的一个数组类型的指针。该函数成功时返回0,并将一对打开的文件描述符值填入fd参数指向的数组。失败时返回 -1并设置errno。

通过pipe函数创建的这两个文件描述符 fd[0] 和 fd[1] 分别构成管道的两端,往 fd[1] 写入的数据可以从 fd[0] 读出。并且 fd[1] 一端只能进行写操作,fd[0] 一端只能进行读操作,不能反过来使用。要实现双向数据传输,可以使用两个管道。

 也就是说我们的文件描述符数组中的文件描述符会作为输出参数,默认pipefd[0]是读,pipefd[1]是写端,此时我们的匿名管道也就完成了基础的构建,所以如何使用管道通信呢,就涉及到我们的代码构建

#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.hpp"
class Channel
{
public:Channel(int wfd, pid_t subprocesspid, std::string name): _wfd(wfd), _subprocesspid(subprocesspid), _name(name){}int getWfd() const{return _wfd;}pid_t getSubprocesspid() const{return _subprocesspid;}std::string getName() const{return _name;}// 形参类型和命名规范// const &: 输出// & : 输入输出型参数// * : 输出型参数//  task_t task: 回调函数void Wait(){int status;if (waitpid(_subprocesspid, &status, 0) == -1){std::cerr << "waitpid failed: " << strerror(errno) << std::endl;}if(waitpid(_subprocesspid, &status, 0) > 0){std::cout << "waitpid success" << std::endl;}if (WIFEXITED(status)){std::cout << _name << " exited with status " << WEXITSTATUS(status) << std::endl;}else{std::cerr << _name << " exited abnormally" << std::endl;}}void CloseChannel(){if (close(_wfd) == -1){std::cerr << "close " << _name << " failed: " << strerror(errno) << std::endl;}}private:int _wfd;pid_t _subprocesspid;std::string _name;
};void CreateChannelsandSubprocesses(int subprocessnum, std::vector<Channel> *channels, task_t task){for (int i = 0; i < subprocessnum; i++){int pipefd[2];if (pipe(pipefd) == -1){std::cerr << "pipe failed: " << strerror(errno) << std::endl;exit(1);}pid_t id = fork();if (id == -1){std::cerr << "fork failed:" << strerror(errno) << std::endl;}if (id == 0){if (i != 0){// 说明当前是第二个子进程for (int j = 0; j < i; j++){close((*channels)[j].getWfd());}}close(pipefd[1]);dup2(pipefd[0], STDIN_FILENO);task();close(pipefd[0]);exit(0);}close(pipefd[0]);channels->push_back(Channel(pipefd[1], id, "subprocess" + std::to_string(i)));}}int NextChannelIndex(int size){static int index = 0;return index++ % size;}void SendTask(int wfd, int tasknum){if (write(wfd, &tasknum, sizeof(tasknum)) == -1){std::cerr << "write failed: " << strerror(errno) << std::endl;}}void controlProcessonce(std::vector<Channel>& channels){int tasknum = Selecttask();int channel_index = NextChannelIndex(channels.size());SendTask(channels[channel_index].getWfd(), tasknum);}void controlProcess(std::vector<Channel>& channels,int times = -1){if(times > 0){while(times--){controlProcessonce(channels);}}else{while(1){controlProcessonce(channels);}}}void cleanupchannels(std::vector<Channel>& channels){for(auto& channel:channels){channel.CloseChannel();channel.Wait();}}
int main(int argc, char *argv[])
{if (argc < 2){std::cerr << "Usage:" << argv[0] << "subprocessnum" << std::endl;}int subprocessnum = atoi(argv[1]);if (subprocessnum <= 0){std::cerr << "subprocessnum must be greater than 0" << std::endl;}Loadtask();std::vector<Channel> channels;// 1. create subprocessnum subprocesses and establish communication channelsCreateChannelsandSubprocesses(subprocessnum, &channels, work1);// 2. control the subprocessescontrolProcess(channels, 10);// 3. close the channelscleanupchannels(channels);return 0;
}

大概看完以上代码的小伙伴就可以跟我进行下一步的验证了,这里时间有限,我们仅将实验过程中的代码出现的情况进行一个总结:

管道的四种情况:

1.如果管道内部是空的并且 write fd没有关闭,读取的条件就不具备,读进程就会堵塞,直到读取的条件具备之后也就是写入了数据才会进行读取

2.管道被写满并且 read fd不读且没有关闭,此时管道被写满了,写进程会被堵塞,直到读取后再写入

3.管道如果一直在读而写端突然关闭了wfd,那么read会返回0代表读到了文件结尾EOF

4.rfd直接关闭,可写端还在写入,那么此时OS会用13号信号关掉子进程,表示遇到异常。

5大特征:

1.匿名管道:只用来进行具有血缘关系的进程之间进行通信,常用于父子进程 

2.管道内部,自带进程之间同步的机制

3.管道文件的生命周期是随进程的

4.管道文件在通信的时候是面向字节流的,write的次数和读取的次数不是一一匹配的

5.管道的通信模式是一种特殊的半双工模式 

到这我们就初步完成了匿名管道的学习,紧接着就是对于进程池的学习,我们的匿名管道正好是父子进程之间的通信,而如果我们有多个子进程,想让他们同时执行不同的任务,就需要我们的进程池了,既可以完成父子间的通信,还可以完成对不同任务的分发,从而显著减少进程创建和销毁的开销,提升系统性能和响应速度。请看下一篇博客哦 

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

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

相关文章

SWM221系列芯片之电机应用及控制

经过对SWM221系列的强大性能及外设资源&#xff0c;TFTLCD彩屏显示及控制进行了整体介绍后&#xff0c;新迎来我们的电控篇---SWM221系列芯片之电机应用及控制。在微控制器市场面临性能、集成度与成本挑战的当下&#xff0c;SWM221系列芯片以其卓越性能与创新设计&#xff0c;受…

qt qss文件的使用

qt样式的修改方式 一 通过ui界面的改变样式表来直接修改显示效果。 不推荐&#xff0c;其他人不好修改&#xff0c;不够直观&#xff0c;不易维护。 二 通过setStyleSheet接口修改。 一般&#xff0c;界面很少的时候可以使用。一旦界面多起来&#xff0c;代码部分就显得杂乱…

Centos文件已删除空间未释放

原创作者&#xff1a;运维工程师 谢晋 Centos文件已删除空间未释放 释放已删除空间 释放已删除空间 很多时候&#xff0c;你会发现&#xff0c;明明已经删除了文件或日志&#xff0c;但是系统空间就是未缩减&#xff0c;很明显&#xff0c;有空间被已删除文件占用&#xff…

reactor中的并发

1. reactor中的并发有两种方式 1.1 flatmap&#xff0c;底层是多线程并发处理。在reactor的演讲中&#xff0c;flatmap对于io类型的并发效果较好. flamap有两个参数: int concurrency, int prefetch。分别代表并发的线程数和缓存大小 注意凡是参数中有prefetch的&#xff0c;都…

深入 Redis:高级特性与最佳实践

引言 在分布式系统和高并发环境中&#xff0c;Redis 已经成为了一个不可或缺的工具。作为一个内存数据结构存储系统&#xff0c;Redis 不仅支持丰富的数据类型&#xff0c;还提供了高效的操作和极低的延迟&#xff0c;这使得它广泛应用于缓存、消息队列、计数器、排行榜等场景…

如何在 JavaScript 中实现日期格式化?

在 JavaScript 中&#xff0c;日期格式化的常见方法是通过使用内置的 Date 对象来进行处理。JavaScript 本身并没有直接提供一个强大的日期格式化函数&#xff0c;因此通常会使用一些流行的第三方库&#xff0c;比如 date-fns 或 moment.js&#xff0c;但如果我们不依赖外部库&…

Trimble天宝X9三维扫描仪为建筑外墙检测提供了全新的解决方案【沪敖3D】

随着城市化进程的快速推进&#xff0c;城市高层建筑不断增多&#xff0c;对建筑质量的要求也在不断提高。建筑外墙检测&#xff0c;如平整度和垂直度检测&#xff0c;是衡量建筑质量的重要指标之一。传统人工检测方法不仅操作繁琐、效率低下&#xff0c;还难以全面反映墙体的真…

浅谈棋牌游戏开发流程二:后端技术选型与基础环境搭建

一、前言&#xff1a;客户端只是台前&#xff0c;后端才是幕后“指挥中心” 在上一篇“客户端技术”中&#xff0c;我们聊到玩家看到的一切动作、动画、界面逻辑&#xff0c;都靠客户端去渲染和交互。但若没有后端的支撑&#xff0c;玩家点了“出牌”可能就像一拳打在空气里—…

机器人手眼标定

机器人手眼标定 一、机器人手眼标定1. 眼在手上标定基本原理2. 眼在手外标定基本原理 二、眼在手外标定实验三、标定精度分析 一、机器人手眼标定 要实现由图像目标点到实际物体上抓取点之间的坐标转换&#xff0c;就必须拥有准确的相机内外参信息。其中内参是相机内部的基本参…

unity中的UI系统---GUI

一、工作原理和主要作用 1.GUI是什么&#xff1f; 即即时模式游戏用户交互界面&#xff08;IMGUI&#xff09;&#xff0c;在unity中一般简称为GUI&#xff0c;它是一个代码驱动的UI系统。 2.GUI的主要作用 2.1作为程序员的调试工具&#xff0c;创建游戏内调测试工具 2.2为…

【Golang 面试题】每日 3 题(二十)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…

【JS】Promise的执行顺序

概述 理解 Promise 的执行顺序时&#xff0c;需要牢记以下两点&#xff1a; 微任务与宏任务的优先级&#xff1a; 微任务&#xff1a;Promise.then()、catch、finally 是微任务。宏任务&#xff1a;setTimeout、setInterval 是宏任务。微任务的优先级高于宏任务&#xff1a;在…

Java开发 PDF文件生成方案

业务需求背景 业务端需要能够将考试答卷内容按指定格式呈现并导出为pdf格式进行存档&#xff0c;作为紧急需求插入。导出内容存在样式复杂性&#xff0c;包括特定的字体&#xff08;中文&#xff09;、字号、颜色&#xff0c;页面得有页眉、页码&#xff0c;数据需要进行表格聚…

SpringCloud微服务架构

文章目录 认识微服务&#xff1a;SpringCloud 服务拆分及远程调用实现夸远程服务调用使用RestTemplateEureka注册中心 搭建EruekaServer注册服务服务发现 Ribbon负载均衡 修改负载均衡规则解饿加载 Nacos注册中心&#xff08;nacos一部分功能&#xff09; 服务注册到nacosnacos…

【设计模式-02】23 种设计模式的分类和功能

在软件工程领域&#xff0c;设计模式是解决常见设计问题的经典方案。1994 年&#xff0c;Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides&#xff08;四人帮&#xff0c;GoF&#xff09;在《设计模式&#xff1a;可复用面向对象软件的基础》一书中系统性地总结了…

大模型在自动驾驶领域的应用和存在的问题

大模型在自动驾驶领域的应用与挑战 大模型&#xff08;如 GPT-4、BERT等&#xff09;已经在多个领域取得了突破&#xff0c;自动驾驶是其中一个受益颇多的行业。随着人工智能和深度学习的快速发展&#xff0c;自动驾驶技术正在向更加智能化、自动化和安全的方向发展。大模型在…

简历_专业技能_熟悉分布式锁Redisson的原理以及使用

系列博客目录 文章目录 系列博客目录怎么样才能够在简历上写熟悉redisson的应用以及原理1. 清晰描述技能与经验示例&#xff1a; 2. 列举具体应用场景示例项目经验&#xff1a; 3. 展示你对原理的理解示例&#xff1a; 4. 用简历中的关键词突出你的能力示例段落&#xff1a; 5.…

在 IntelliJ IDEA 中开发 GPT 自动补全插件

背景与目标 随着 AI 的发展&#xff0c;GitHub Copilot 等智能代码补全工具在开发者中获得了广泛的应用&#xff0c;极大地提高了编程效率。本篇文章将教你如何开发一个 IntelliJ IDEA 插件&#xff0c;使用 OpenAI 的 GPT API 来实现类似 Copilot 的代码自动补全功能。通过这…

分布式任务调度xxl-job入门案例

XXL-JOB是一个分布式任务调度平台&#xff0c;简单来说就是可以在你指定的时间内调用某个功能&#xff0c;就例如购物某个商品的限时抢购从什么时候开始以及结束抢购类似于这样的。 下面是它的一个仓库地址 http://gitee.com/xuxueli0323/xxl-job 下载之后将项目导入进idea中&…

瑞芯微rk3566刷机流程(黑豹X2)

文章目录 概要 刷机方式 卡刷流程 线刷流程 小结 概要 记录rk3566刷机的过程&#xff0c;纯纯的小白&#xff0c;艰难而有意义的一天。 刷机方式 1、卡刷&#xff08;tf卡&#xff09; 2、线刷&#xff08;双公头usb线&#xff09; 卡刷流程 1、下载armbian镜像 1、…