当前位置: 首页 > news >正文

【Linux】从零开始认识进程间通信 —— 管道

在这里插入图片描述
送给大家一句话:
人要成长,必有原因,背后的努力与积累一定数倍于普通人。所以,关键还在于自己。 – 杨绛


从零开始认识进程间通信

  • 1 为什么要进程间通信
  • 2 进程如何通信
  • 3 进程通信的常见方式
  • 4 管道
    • 4.1 什么是管道
    • 4.2 管道通信的系统调用
    • 4.3 小试牛刀
  • 5 总结
  • Thanks♪(・ω・)ノ谢谢阅读!!!
  • 下一篇文章见!!!

1 为什么要进程间通信

以前我们学习的过程中,进程是具有独立性的。但有些时候需要多个进程进行协同,这时候就需要进程间的通信来保证信息的互通。

就比如学校就分有教务处 , 学生处,教研组,班主任等部分。如果学校想要组织一场考试,就通知教务处安排好考场和监考员,告诉教研组老师需要出卷子,等教务处与教研组完成对应工作再告知学生处和班主任,然后通知学生进行考试,班主任和学生处做好考试监督工作。

这里面就少不了沟通交流,传递信息。进程工作也是这样:进程的协同工作需要一个前提提交——通信。通信就是传递数据,控制相关信息

2 进程如何通信

首先 , 我们知道进程是具有独立性的,一个进程的状态不会影响其他进程的运行。而独立性的本质是进程 = 内核数据结构 + 代码和数据 ,内核数据结构PCB是独立的,代码和数据是独立的 , 进程自然就是独立的。

所以,进程间通信的成本的成本稍微高一些,因为进程本身是独立的,两个进程天然是无法进行数据共享的!

可是子进程建立的时候不是会拷贝(继承)一份父进程的数据吗,这不是进行通信吗???
这就要我们明确区分两个概念:能通信可以一直通信是不一样的。子进程继承的父进程数据是只读的,而且只进行一次。而一直通信是时不时就“打个电话”。

所以进程间通信的前提是:先让不同的进程看到同一份(操作系统)资源(“一段内存”)。

在这里插入图片描述

  1. 首先,一定是某一个进程先需要通信,让OS创建一个共享资源
  2. 那么OS必须通过对应的系统调用来创建共享资源
  3. OS创建的共享资源的不同 , 系统调用接口的不同----就导致进程间通信会有不同的种类

3 进程通信的常见方式

一般通信有以下种类:

  1. 管道
    • 匿名管道pipe
    • 命名管道
  2. System V IPC 标准 (早期的本地通信)
    • System V 消息队列
    • System V 共享内存
    • System V 信号量
  3. POSIX IPC 标准(现代版本)
    • 消息队列
    • 共享内存
    • 信号量
    • 互斥量
    • 条件变量
    • 读写锁

今天来讲解管道

早期的时候,程序员们面对通信的需求时,不想再单独设计一个通信模块,直接复用内核级代码,就产生了管道!!!管道分为命名管道和匿名管道。

4 管道

4.1 什么是管道

【Linux】 拿下 系统 基础文件操作!!!
【Linux】开始了解重定向

这两篇文章了我们讲解了文件的底层,知道每一个进程都有对应的文件管理结构体,文件管理结构体中有管理已经打开文件的数组。数组下标为文件描述符,指向文件结构体,而文件结构体又会指向文件真正属性inode。
在这里插入图片描述
当我们以不同方式打开文件时,只需要在内存中加载一份数据(通过引用计数来管理),以读写方式打开,便会有两个对应的文件结构体。他们共同使用一份代数据,那自然就使用同一个内核级缓冲区。

那么为了要通信,不用在写一个新的模块,直接建立一个子进程来通信多简单。子进程会以父进程为模版进行写时拷贝。
在这里插入图片描述
进行拷贝的只有进程对应的结构体,因为进程具有独立性,而文件系统我们可没提过什么对立性,所以文件管理数组进行浅拷贝,同样指向原先的文件结构体。

这时也就理解为什么父子进程会向同一块显示器终端打印数据了。

也理解为什么进程会默认打开012三个标准输入输出:因为所有进程都是bash的子进程,而bash打开了这三个文件,所以自然就打开了!!!

子进程要主动close(0 / 1 /2)不影响父进程继续使用显示器文件!只有引用计数(类似硬链接数)归零才会清理数据

今天我们进行进程间通信的前提——先让不同的进程看到同一份(操作系统)资源,不就解决了吗!!!

文件的内存缓冲区不就是两个进程共享的一份资源吗!而所谓的管道文件就是这个文件缓冲区!

但是呢,管道只允许进行单向通信(父->子 或 子->父),因为管道如果允许父子进程都可以写,就会导致数据紊乱!进行通信的时候,每个进程关闭不需要的文件描述符,然后通过缓冲区来单向通信。一个进程把信息写入缓冲区,另一个进程从缓冲区读取数据,不需要刷新到硬盘,直接从内存进行操作!

在这里插入图片描述

有个问题:父子既然要关闭不需要的fd那为什么曾经还要打开呢?可以不关闭吗?
如果父进程只打开读写的fd,那么子进程也就只能继承读写的fd,这就坏事了,总得有人写入吧!那为什么不直接以读写方式打开一个fd呢?这样肯定不可以,子进程继承后也具有读写,也坏事了!
所以不关闭是为了让子进程可以继承下去,到时候关闭不需要的就可以了!当然也可以不关闭,只要你不乱使用,所以为了排除风险,建议直接关闭

4.2 管道通信的系统调用

了解了管道是什么,我们就来看看关于管道的系统调用是什么吧?
通过手册我们可以看到:

PIPE(2)                                                             Linux Programmer's Manual                                                            PIPE(2)NAMEpipe, pipe2 - create pipeSYNOPSIS#include <unistd.h>/* On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64; see NOTES */struct fd_pair {long fd[2];};struct fd_pair pipe();/* On all other architectures */int pipe(int pipefd[2]);#define _GNU_SOURCE             /* See feature_test_macros(7) */#include <fcntl.h>              /* Obtain O_* constant definitions */#include <unistd.h>int pipe2(int pipefd[2], int flags);

新的pipe(int pipefd[2]) 是今天的主角(Ubuntu提供了新的选择pipe2),其实底层就是open

pipefd[2] 这是一个输出型参数,把以读方式打开的文件描述符rfd和以写方式打开的文件描述符wfd记录下来!

open不同的是,这个系统调用不需要文件路径和文件名,所以才叫匿名管道!

那么如果我们想要双向通信呢???
干脆建两个管道不就行了!

那为什么要进行单向通信呢?
因为这个管道的单向通信简单,对代码的复用率很高!

4.3 小试牛刀

接下来我们就来写一个demo,来试试管道接口。

首先我们来搭建一个框架:

  1. 建立一个管道,得到对应的文件描述符
  2. 创建子进程,关闭对应文件
  3. 我们进行子进程写入,父进程读取
  4. 等待子进程退出,避免僵尸进程出现!
#include<iostream>
#include<unistd.h>
#include<cstring>
#include<sys/wait.h>
#include<cerrno>using namespace std;void SubProcessWrite(int wfd)
{
}
void FatherProcessRead(int rfd)
{
}
int main()
{//创建管道int pipefd[2];//[0] -> r | [1] -> wint n = pipe(pipefd);if(n != 0){perror("创建管道错误!\n");}cout << "pipefd[0] : " << pipefd[0] << " pipefd[1] :" << pipefd[1] << endl;//创建子进程//关闭对应文件pid_t id = fork();if(id == 0){//子进程 -- writeclose(pipefd[0]);SubProcessWrite(pipefd[1]);//使用完都关闭close(pipefd[1]);exit(0);}//父进程 -- readclose(pipefd[1]);FatherProcessRead(pipefd[0]);//使用完都关闭close(pipefd[0]);pid_t rid = waitpid(id ,nullptr , 0 );if(rid > 0){cout << "wait child process done!" << endl;}return 0;
}

管道本质也是文件,我们读写同样使用write和read。
我们完善一下代码:

  1. 子进程每隔一秒写入一次数据
  2. 父进程每隔一秒读取一次数据
#include<iostream>
#include<unistd.h>
#include<cstring>
#include<string>
#include<sys/wait.h>
#include<cerrno>using namespace std;string GetOtherMessage()
{static int cnt = 0;string messageid = to_string(cnt);cnt++;pid_t self_id = getpid();string stringid = std::to_string(self_id);string message = "messageid : ";message += messageid;message += "my pid :";message += stringid;return message;
}void SubProcessWrite(int wfd)
{string messages = "father,I am your soon process !";while (true){string info = messages + GetOtherMessage();//写入管道时没有写入'\0',没有必要。在读取时添加write(wfd , info.c_str() , info.size());sleep(1);}}
#define size 1024void FatherProcessRead(int rfd)
{char inbuffer[size];while (true){//注意传入的参数 , 读取 rfd 内的数据到inbuffer中,返回成功读取的个数。ssize_t n = read(rfd , inbuffer , sizeof(inbuffer) - 1);if( n > 0 ) {inbuffer[n] = '\0';//添加'\0'cout << "father get message: " << inbuffer << endl;}}
}
int main()
{//创建管道int pipefd[2];//[0] -> r | [1] -> wint n = pipe(pipefd);if(n != 0){perror("创建管道错误!\n");}cout << "pipefd[0] : " << pipefd[0] << " pipefd[1] :" << pipefd[1] << endl;sleep(1);//创建子进程//关闭对应文件pid_t id = fork();if(id == 0){cout << "子进程关闭不需要的fd , 准备开始发消息" << endl;sleep(1);//子进程 -- writeclose(pipefd[0]);SubProcessWrite(pipefd[1]);//使用完都关闭close(pipefd[1]);exit(0);}cout << "子进程关闭不需要的fd , 准备开始接收消息" << endl;sleep(1);//父进程 -- readclose(pipefd[1]);FatherProcessRead(pipefd[0]);//使用完都关闭close(pipefd[0]);pid_t rid = waitpid(id ,nullptr , 0 );if(rid > 0){cout << "wait child process done!" << endl;}return 0;
}

运行看看:
在这里插入图片描述
这样验证管道通信的可行性。

5 总结

管道的4种情况

  1. 如果管道内部是空的 && write fd没有关闭:
    读取条件不具备,读取进程会被阻塞 – wait 等待条件具备(写入了数据)
  2. 管道别写满 && read fd 不读且没有关闭 :
    管道被写满,写进程会被阻塞,写条件不具备-- wait 等待条件具备(读取走一部分数据才能继续写)
  3. 管道一直在被读 && 写端关闭了wfd:
    读端read的返回值会读到 0 ,表示到了文件结尾!!!所以可以在读取的时候进行一下判断,为0就直接退出读取!
  4. rfd 直接关闭 , 写端wfd一直在写入:
    首先管道只有一对读写端,读端被关闭了,那么管道就不能称之为管道了。OS系统也不会做这样的无用功,直接把broken pipe坏的管道 进行杀掉!会发送对应的13号信号SIGPIPE:
    在这里插入图片描述

我们可以总结出管道的5 种特征:

  1. 匿名管道:只用来进行父子进程之间,因为他们可以看到同一资源
  2. 同步性管道内部自带进程之间的同步机制!同步是多执行流代码的时候,具有明显的顺序性。父子进程的读写一定要同步进行,不然可能会出现并发读取的情况,出现错误!
  3. 文件的生命周期是随进程的:当一个文件没有进程调用的时候,就会释放掉!
  4. 管道在通信的时候,是面向字节流的:write 的次数和read的次数不是一一匹配的!
    我们让子进程疯狂的写,父进程也一直读。子进程每 1 s写一次,写入时也向标准错误里进行打印(为了好观察)。父进程每5s读一次,并打印到显示器:
    在这里插入图片描述
    可以看到,右侧的子进程,左边是父进程。子进程写入好几次的数据,会被父进程一次读取一大批!!!
  5. 管道的通信模式,是一种特殊的半双工模式:与之对应的是全双工模式,即双方交流可以同时说话。半双工是只能一方说话,一方聆听,不能同时说(对讲机模式)。

这里提一个概念,在管道读写是"原子"的,每个"原子"是 4096 bytes。只有小于这个大小,就不会在读写时被其他人影响。如果大于一个原子的大小,就不能保证安全了。

下一篇文章我们进行管道的实战——进程池项目!

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

相关文章:

【Linux】从零开始认识进程间通信 —— 管道

送给大家一句话&#xff1a; 人要成长&#xff0c;必有原因&#xff0c;背后的努力与积累一定数倍于普通人。所以&#xff0c;关键还在于自己。 – 杨绛 从零开始认识进程间通信 1 为什么要进程间通信2 进程如何通信3 进程通信的常见方式4 管道4.1 什么是管道4.2 管道通信的系…...

Top3专业课150满分,怎么考的?

这个系列会邀请上岸学长学姐进行经验分享~ 今天经验分享的同学是小马哥上海交大819的全程班学员&#xff0c;专业课150分满分&#xff0c;这位同学也是819期末考试的第一名&#xff0c;非常厉害&#xff01;大家吸吸欧气&#xff01; 初试成绩单 前言 先介绍下自己&#xff0…...

Windows Presentation Foundation(WPF)要点总结

Windows Presentation Foundation&#xff08;WPF&#xff09;是微软推出的一种用于构建Windows桌面应用程序的框架。自从WPF在.NET Framework 3.0中引入以来&#xff0c;它以其强大的功能和灵活性&#xff0c;逐渐成为开发人员构建现代、富用户界面应用程序的首选。本文将概述…...

【研发日记】嵌入式处理器技能解锁(一)——多任务异步执行调度的三种方法

文章目录 前言 Timer中断调度 Event中断调度 StateFlow调度 分析和应用 总结 参考资料 前言 近期在一些嵌入式系统开发项目中&#xff0c;在使用嵌入式处理器时&#xff0c;遇到了挺多费时费力的事情。所以利用晚上和周末时间&#xff0c;在这些方面深入研究了一下&…...

揭秘Python的魔法:装饰器的超能力大揭秘 ‍♂️✨

文章目录 Python进阶之装饰器详解1. 引言装饰器的概念与意义装饰器在Python编程中的作用 2. 背景介绍2.1 函数作为对象2.2 高阶函数 3. 装饰器基础3.1 理解装饰器3.2 装饰器的工作原理 4. 带参数的装饰器4.1 为什么需要带参数4.2 实现带参数的装饰器使用函数包裹装饰器使用类实…...

怎么一键消除路人?教你三个消除方法

怎么一键消除路人&#xff1f;在数字时代&#xff0c;摄影已成为我们记录生活、表达情感的重要方式。然而&#xff0c;完美的照片背后往往隐藏着一些不那么完美的元素——比如那些不经意间闯入镜头的路人。他们或许只是匆匆过客&#xff0c;但却足以破坏你精心构图的美好瞬间。…...

Android Settings系统属性读写

Settings系统属性存储均为xml&#xff0c;分三种&#xff1a; 1.global&#xff1a;所有的偏好设置对系统的所有用户公开&#xff0c;第三方APP有读没有写的权限&#xff1b; 源码地址&#xff1a;frameworks/base/core/java/android/provider/Settings.java 对应xml路径&…...

2024年,企业的人才管理怎么做?这5点是关键!

当今时代&#xff0c;各行各业都面临着激烈的竞争。这些竞争归根结底都是人才的竞争。企业若想在竞争中掌握主动权&#xff0c;实现基业长青&#xff0c;就必须努力留住人才&#xff0c;并充分发挥他们的积极性、主动性和创造性。因此&#xff0c;做好人才管理是企业实现长期可…...

数据库DDL语句

数据库DDL语句&#xff1a; 查询所有数据库&#xff1a; show databases;查询当前数据库的名称 select database();创建数据库 create database [if not exists] 数据库名 [default charset 字符集] [collate 排序规则]注意&#xff1a;排序规则指定后&#xff0c;它会影响…...

《艺术大观》知网艺术刊:可加急, 出刊上网快

《艺术大观》 《艺术大观》征文通知 《艺术大观》期刊诚邀学者、艺术家和文化工作者积极投稿&#xff0c;共同探索艺术领域的前沿问题&#xff0c;促进学术交流和艺术创作的发展。我们欢迎各类艺术形式的研究与评论&#xff0c;包括但不限于绘画、雕塑、音乐、舞蹈、戏剧、电…...

如何在go语言中调用c语言代码

1.安装c语言编译器 要使用cgo&#xff0c;需要安装c语言编译器 gcc 2.检查CGO_ENABLED时候开启 使用以下命令查看&#xff1a; go env CGO_ENABLED 如果go env CGO_ENABLED被禁用(为0),需要将其设置为开启(为1) 3.编写c语言程序&#xff0c;并用go语言调用c语言程序 1&#xff…...

Monodle centerNet3D 瑞芯微RKNN、地平线Horizon芯片部署、TensorRT部署

一直想做一点3D目标检测&#xff0c;先来一篇单目3D目标检测Monodle&#xff08;基于centernet的&#xff09;&#xff0c;训练代码参考官方【代码】&#xff0c;这里只讲讲如何部署。 模型和完整仿真测试代码&#xff0c;放在github上参考链接【模型和完整代码】。 1 模型训练…...

Android Studio 使用MQTT协议开发应用时怎样关闭MQTT连接

Android Studio 使用MQTT协议开发应用时怎样关闭MQTT连接 Android Studio 使用MQTT协议开发应用时关闭MQTT连接 在使用mqtt开发的时候&#xff0c;有时候需要通过 返回 按钮关闭界面或者Activity时&#xff0c;关闭当前页面使用的mqtt连接&#xff0c;这里有两种方式彻底销毁…...

Sping源码(八)—registerBeanPostProcessors

序言 之前我们用大量的篇幅介绍过invokeBeanFactoryPostProcessors()方法的执行流程。 而invokeBeanFactoryPostProcessors的主要逻辑就是遍历执行实现了BeanDefinitionRegistryPostProcesso类(主要是针对BeanDefinition的操作)和BeanFactoryPostProcessor(主要针对BeanFacrot…...

MaxEnt模型文章中存在的问题和处理方法(050B更新)2024.5.24

目前多数MaxEnt文章中存在的问题和处理方案。 **问题一&#xff1a;**变量数据使用问题&#xff0c;很多文章把所有变量数据直接使用&#xff0c;但是温度和土壤、植被类型等属于不同数据类型&#xff0c;在数据使用时参数配置是不一样的&#xff0c;产生的结果文件也是不一样的…...

Modular RPG Hero PBR

-掩码着色着色器提供了无限的颜色变化。(适用于标准/HDRP/URP 11.0.0) -为剑与盾/双剑/双剑姿态提供了简单的角色控制器。(不包括弓和魔杖控制器)(它是用旧的输入系统建造的) -HDRP/URP(11.0.0)SRP 100%支持常规着色器和遮罩着色着色器(基于着色器图形) -具有许多模块…...

机器学习之常用算法与数据处理

一、机器学习概念&#xff1a; 机器学习是一门多领域交叉学科&#xff0c;涉及概率论、统计学、计算机科学等多门学科。它的核心概念是通过算法让计算机从数据中学习&#xff0c;改善自身性能。机器学习专门研究计算机怎样模拟或实现人类的学习行为&#xff0c;以获取新的知识…...

Git管理

git作用:代码回溯 版本切换 多人协作 远程备份 git仓库&#xff1a;本地仓库&#xff1a;开发人员自己电脑上的Git仓库 原程仓库&#xff1a;远程服务器上的Git仓库 commit&#xff1a;提交&#xff0c;将本地文件和版本信息保存到本地仓库 push&#xff1a;推送&#xff0…...

osgearth 3.5 vs 2019编译

下载源码 git clone --recurse-submodules https://github.com/gwaldron/osgearth.git 修改配置文件 主要是修改bootstrap_vcpkg.bat&#xff0c;一处是vs的版本&#xff0c;第二处是-DCMAKE_BUILD_TYPERELEASE 构建 执行bootstrap_vcpkg.bat vs中生成安装 vs2019打开bu…...

2024最新 Jenkins + Docker 实战教程(六)- Jenkins配置邮箱接收构建通知

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...