Linux进程通信入门:匿名管道的原理、实现与应用场景

Linux系列


文章目录

  • Linux系列
  • 前言
  • 一、进程通信的目的
  • 二、进程通信的原理
    • 2.1 进程通信是什么
    • 2.2 匿名管道通讯的原理
  • 三、进程通讯的使用
  • 总结


前言

Linux进程间同通讯(IPC)是多个进程之间交换数据和协调行为的重要机制,是我们学习Linux操作系统中比较重要的一个模块。


一、进程通信的目的

Linux 进程间通信(IPC)的主要目的是让多个独立的进程能够协作、共享资源、交换数据或同步操作。这是现代操作系统中多任务、并行计算和分布式系统的核心基础。以下是具体的目的和场景:

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 协同工作:两进程需要更具对方的执行结果来协同完成某种任务
  • 资源同步与互斥:避免多个进程同时访问共享资源导致数据竞争或状态不一致

二、进程通信的原理

2.1 进程通信是什么

进程通信的本质其实就是多个进程可以同时访问同一份“资源”。对于进程来说“资源”就是空间中存储的数据,所以要实现进程通信我们只需要让多个进程可以同时访问同一块空间即可。这里我们就以两个进程为例:
在这里插入图片描述
这时我们只需要做到让进程1向这块空间中写数据,进程2可以将进程1写入的数据从空间中取出,这样就完成了通讯工作。那么这块空间该由谁提供呢?由于进程必须保证独立性(也就是说不论这个空间由哪个进程提供,另一个进程都不能对它访问,否则就破坏了独立性),也就注定了这块共享空间只能由第三方提供---------操作系统,那么这块空间就必须由操作系统管理了,空间的创建到释放都是由操作系统来完成的,所以我们对这块空间的访问,就变成了对操作系统的访问,而进程(代表用户)要想访问操作系统,只能通过系统调用接口进行。
为了满足通讯需求,一般操作系统都会有一个独立的通信模块。今天我们就来介绍基于文件级别的通讯方式------管道

2.2 匿名管道通讯的原理

根据上面的分析,我们首先要让两个进程看到同一份资源:

在这里插入图片描述

我们利用父进程创建子进程时的特点,这样就可以让两个进程(父和子)看到同一分空间,而我们的管道就是文件,它是内核文件,并不会向磁盘刷新内容,父子进程对他进行访问时并不需要在目录中查询,而是直接通过文件描述符查找,所以管道文件不用定义名字,因此被称为匿名管道。
到了这里我们仅解决了,让两个进程看到同一份空间,但是管道文件不支持以读写方式打开,也就是说我们上面的介绍,两个进程(父子进程)只能向管道中读,或只能向管道中写,而进程通讯一般为一个进程写,一个进程读。

在这里插入图片描述
所以进程在打开管道文件时,都会分别以读、写两种方式打开,当我们让父进程读取数据,子进程写入数据的形式通讯时(这个用户自己控制),父进程就关闭对应的写方式,子进程关闭对应的读方式,此时子进程就可以向管道文件的缓冲区写入数据供父进程读取。到了这里,我们就建立了进程通信的信道。

通过上面的介绍我们可以知道匿名管道通信的特点为:

  • 只能进行单向通信
  • 通信进程间需要有血缘关系,常用于父子

三、进程通讯的使用

首先我们先来认识需要用到的系统调用:

在这里插入图片描述
这个系统调用需要传递一个存储两个元素的整形数组,该参数为输出型参数,第一个元素为以读形式打开的文件描述符,第二从元素为以写形式打开的文件描述符。下面我们先实现一个简单的通信:
示例1:

include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<string>
#include<cstring>
using namespace std;int main()
{int pipefd[2];int n=pipe(pipefd);if(n<0)return 1;pid_t id=fork();//创建子进程if(id==0)//子进程{string str="Hello father process\n";close(pipefd[0]);//子关闭读权限write(pipefd[1],str.c_str(),strlen(str.c_str()));//向管道中写入数据exit(0);}close(pipefd[1]);//父关闭写权限char tmp[100]={0};read(pipefd[0],tmp,100);//从管道中读取数据cout<<tmp;return 0;
}

程序执行结果:
在这里插入图片描述
这给代码主要帮助我们理解上面介绍的,关闭文件描述符以及pipe()参数的作用。接下来我们会通过下面的代码示例,对我们的管道通信的四种情况进行总结。

情况1:读写端正常,管道为空,读端就要阻塞

#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<cstdio>
#include<cstring>
using namespace std;#define N 2
#define NUM 1024void Write(int wfd)
{char buff[NUM];string str="Hello ,I am child,pid:";pid_t self=getpid();int num=0;while(true){sleep(2);buff[0]=0;snprintf(buff,sizeof(buff),"%s%d---%d",str.c_str(),self,num++);//将要写入数据格式化到buffwrite(wfd,buff,strlen(buff));//将buff中的数据写入管道}
}void Read(int rfd)
{char buff[NUM];while(true){buff[0]=0;int n=read(rfd,buff,sizeof(buff));//从管道中读取数据if(n>0){cout<<"father get message:"<<buff<<endl;}else if(n==0){cout<<"father read fail"<<endl;}else break;}
}
int main()
{int pipefd[N];int n=pipe(pipefd);if(n<0)return 1;//调用失败程序返回pid_t id=fork();//创建子进程if(id<0)return 1;if(id==0)//child{close(pipefd[0]);//关闭读端Write(pipefd[1]);close(pipefd[1]);//这一步有没有都无所谓exit(0);}//fatherclose(pipefd[1]);//关闭写端Read(pipefd[0]);close(pipefd[0]);return 0;
}

在这里插入图片描述
想要的效果不好展示,大家可以自己跑以下看看程序执行过程
上述代码,子进程在进程在向管道中写入数据时,每间隔两秒写入一次,而父进程会阻塞等待,当子进程写入完成后父进程才会读取并返回。我们可以看到程序并没有执行cout<<"father read fail"<<endl;这也证明了父进程会进行阻塞等待,等待子进程写入。
个人感觉这已经能说明问题了,不知道你能不能get到

情况二:读写端正常,管道被写满,写端就要阻塞
接下来我们对上面代码进行一点小改动:

void Write(int wfd)
{char buff[NUM];string str="Hello ,I am child,pid:";pid_t self=getpid();int num=0;while(true){buff[0]=0;snprintf(buff,sizeof(buff),"%s%d---%d",str.c_str(),self,num++);//将要写入数据格式化到buffwrite(wfd,buff,strlen(buff));//将buff中的数据写入管道cout<<"已经写入了:"<<num<<"条信息"<<endl;//记录写入次数}}
void Read(int rfd)
{char buff[NUM];while(true){buff[0]=0;sleep(10);int n=read(rfd,buff,sizeof(buff));//从管道中读取数据if(n>0){cout<<"father get message:"<<buff<<endl;}else if(n==0){cout<<"father read fail"<<endl;}else break;}
}

我们让父进程等待10秒再读取,子进程一直写入。
在这里插入图片描述
此时你会看到当子进程入定数量的数据后,就会停止写入进入阻塞状态,等待父进程的读取,父进程读取成功后,子进程才能继续写入,其原因就直管道被写满了。

情况三:读端正常,写端关闭,读端就会读到0,表明读到了文件(pipe)结尾,不会阻塞

void Write(int wfd)
{char buff[NUM];string str="Hello ,I am child,pid:";pid_t self=getpid();int num=0;int cnt=5;//写入五次while(cnt--){sleep(1);buff[0]=0;snprintf(buff,sizeof(buff),"%s%d---%d",str.c_str(),self,num++);//将要写入数据格式化到buffwrite(wfd,buff,strlen(buff));//将buff中的数据写入管道}cout<<"我是写端,我关闭了"<<endl;
}void Read(int rfd)
{char buff[NUM];while(true){buff[0]=0;sleep(1);int n=read(rfd,buff,sizeof(buff));//从管道中读取数据if(n>0){cout<<"father get message:"<<buff<<endl;}else if(n==0){cout<<"father read fail"<<endl;}else break;}
}

在这里插入图片描述
这个图效果就很好了,当写端关闭时,读端读取到文件末尾返回0执行cout<<"father get message:"<<buff<<endl;,子进程退出变为僵尸。

情况四:写端正常,读端关闭,操作系统通过信号杀掉正在写入的进程。

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib> //stdlib.h
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>#define N 2
#define NUM 1024using namespace std;// child
void Writer(int wfd)
{string s = "hello, I am child";pid_t self = getpid();int number = 0;char buffer[NUM];while (true){sleep(1);// 构建发送字符串buffer[0] = 0; // 字符串清空, 只是为了提醒阅读代码的人,我把这个数组当做字符串了snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, number++);// cout << buffer << endl;// 发送/写入给父进程, system callwrite(wfd, buffer, strlen(buffer)); // strlen(buffer) + 1???}
}// father
void Reader(int rfd)
{char buffer[NUM];int cnt = 0;while(true){buffer[0] = 0; // system callssize_t n = read(rfd, buffer, sizeof(buffer)); //sizeof != strlenif(n > 0){buffer[n] = 0; // 0 == '\0'cout << "father get a message[" << getpid() << "]# " << buffer << endl;}else if(n == 0) {printf("father read file done!\n");break;}else break;cnt++;if(cnt>5) break;// cout << "n: " << n << endl;}
}int main()
{int pipefd[N] = {0};int n = pipe(pipefd);if (n < 0)return 1;// child -> w, father->rpid_t id = fork();if (id < 0)return 2;if (id == 0){// childclose(pipefd[0]);// IPC codeWriter(pipefd[1]);close(pipefd[1]);exit(0);}// fatherclose(pipefd[1]);// IPC codeReader(pipefd[0]); // 读取5sclose(pipefd[0]);cout << "father close read fd: " << pipefd[0] << endl;sleep(5); //为了观察僵尸int status = 0;pid_t rid = waitpid(id, &status, 0);    if(rid < 0) return 3;cout << "wait child success: " << rid << " exit code: " << ((status>>8)&0xFF) << " exit signal: " << (status&0x7F) << endl;sleep(5);cout << "father quit" << endl;return 0;
}

在这里插入图片描述
在这里插入图片描述
可以看到当读端关闭后,操作系统就会将使用13号信号杀掉写端。

总结

1、具有血缘关系的进程才能使用匿名管道通讯
2、管道只能单向通信
3、父子进程是会进程协同的,同步与互斥的------保护管道文件的数据安全
4、管道是门面向字节流的
5、管道是基于文件的,而文件的生命周期是随进程的

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

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

相关文章

探秘Transformer系列之(26)--- KV Cache优化 之 PD分离or合并

探秘Transformer系列之&#xff08;26&#xff09;— KV Cache优化 之 PD分离or合并 文章目录 探秘Transformer系列之&#xff08;26&#xff09;--- KV Cache优化 之 PD分离or合并0x00 概述0x01 背景知识1.1 自回归&迭代1.2 KV Cache 0x02 静态批处理2.1 调度策略2.2 问题…

十大PDF解析工具在不同文档类别中的比较研究

PDF解析对于包括文档分类、信息提取和检索在内的多种自然语言处理任务至关重要&#xff0c;尤其是RAG的背景下。尽管存在各种PDF解析工具&#xff0c;但它们在不同文档类型中的有效性仍缺乏充分研究&#xff0c;尤其是超出学术文档范畴。通过使用DocLayNet数据集&#xff0c;比…

HarmonyOS-ArkUI 装饰器V2 @ObservedV2与@Trace装饰器

参考文档: 文档中心https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V14/arkts-new-observedv2-and-trace-V14#trace%E8%A3%85%E9%A5%B0%E5%AF%B9%E8%B1%A1%E6%95%B0%E7%BB%84由于V2的装饰器比V1的装饰器更加易用,尽管学习的过程中用到的都是V1的装饰器,但…

GPT - GPT(Generative Pre-trained Transformer)模型框架

本节代码主要为实现了一个简化版的 GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型。GPT 是一种基于 Transformer 架构的语言生成模型&#xff0c;主要用于生成自然语言文本。 1. 模型结构 初始化部分 class GPT(nn.Module):def __init__(self, vocab…

基于FPGA的六层电梯智能控制系统 矩阵键盘-数码管 上板仿真均验证通过

基于FPGA的六层电梯智能控制系统 前言一、整体方案二、软件设计总结 前言 本设计基于FPGA实现了一个完整的六层电梯智能控制系统&#xff0c;旨在解决传统电梯控制系统在别墅环境中存在的个性化控制不足、响应速度慢等问题。系统采用Verilog HDL语言编程&#xff0c;基于Cyclo…

车载通信系统中基于ISO26262的功能安全与抗辐照协同设计研究

摘要&#xff1a;随着智能网联汽车的快速发展&#xff0c;车载通信系统正面临着功能安全与抗辐照设计的双重挑战。在高可靠性要求的车载应用场景下&#xff0c;如何实现功能安全标准与抗辐照技术的协同优化&#xff0c;构建满足ISO26262安全完整性等级要求的可靠通信架构&#…

Node.js种cluster模块详解

Node.js 中 cluster 模块全部 API 详解 1. 模块属性 const cluster require(cluster);// 1. isMaster // 判断当前进程是否为主进程 console.log(是否为主进程:, cluster.isMaster);// 2. isWorker // 判断当前进程是否为工作进程 console.log(是否为工作进程:, cluster.isW…

融合动态权重与抗刷机制的网文评分系统——基于优书网、IMDB与Reddit的混合算法实践

✨ Yumuing 博客 &#x1f680; 探索技术的每一个角落&#xff0c;解码世界的每一种可能&#xff01; &#x1f48c; 如果你对 AI 充满好奇&#xff0c;欢迎关注博主&#xff0c;订阅专栏&#xff0c;让我们一起开启这段奇妙的旅程&#xff01; 以权威用户为核心&#xff0c;时…

使用Golang打包jar应用

文章目录 背景Go 的 go:embed 功能介绍与打包 JAR 文件示例1. go:embed 基础介绍基本特性基本语法 2. 嵌入 JAR 文件示例项目结构代码实现 3. 高级用法&#xff1a;嵌入多个文件或目录4. 使用注意事项5. 实际应用场景6. 完整示例&#xff1a;运行嵌入的JAR 背景 想把自己的一个…

前端大屏可视化项目 局部全屏(指定盒子全屏)

需求是这样的&#xff0c;我用的项目是vue admin 项目 现在需要在做大屏项目 不希望显示除了大屏的其他东西 于是想了这个办法 至于大屏适配问题 请看我文章 底部的代码直接复制就可以运行 vue2 px转rem 大屏适配方案 postcss-pxtorem-CSDN博客 <template><div …

《2025蓝桥杯C++B组:D:产值调整》

**作者的个人gitee**​​ 作者的算法讲解主页▶️ 每日一言&#xff1a;“泪眼问花花不语&#xff0c;乱红飞过秋千去&#x1f338;&#x1f338;” 题目 二.解题策略 本题比较简单&#xff0c;我的思路是写三个函数分别计算黄金白银铜一次新产值&#xff0c;通过k次循环即可获…

[VTK] 四元素实现旋转平移

VTK 实现旋转&#xff0c;有四元数的方案&#xff0c;也有 vtkTransform 的方案&#xff1b;主要示例代码如下&#xff1a; //构造旋转四元数vtkQuaterniond rotation;rotation.SetRotationAngleAndAxis(vtkMath::RadiansFromDegrees(90.0),0.0, 1.0, 0.0);//构造旋转点四元数v…

华为hcie证书的有效期怎么判断?

在ICT行业&#xff0c;华为HCIE证书堪称含金量极高的“敲门砖”&#xff0c;拥有它往往意味着在职场上更上一层楼。然而&#xff0c;很多人在辛苦考取HCIE证书后&#xff0c;却对其有效期相关事宜一知半解。今天&#xff0c;咱们就来好好唠唠华为HCIE证书的有效期怎么判断这个关…

【精品PPT】2025固态电池知识体系及最佳实践PPT合集(36份).zip

精品推荐&#xff0c;2025固态电池知识体系及最佳实践PPT合集&#xff0c;共36份。供大家学习参考。 1、中科院化学所郭玉国研究员&#xff1a;固态金属锂电池及其关键材料.pdf 2、中科院物理所-李泓固态电池.pdf 3、全固态电池技术研究进展.pdf 4、全固态电池生产工艺.pdf 5、…

MySQL 中为产品添加灵活的自定义属性(如 color/size)

方案 1&#xff1a;EAV 模型&#xff08;最灵活但较复杂&#xff09; 适合需要无限扩展自定义属性的场景 -- 产品表 CREATE TABLE products (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(100),price DECIMAL(10,2) );-- 属性名表 CREATE TABLE attributes (id INT PRIMA…

CSPM认证对项目论证的范式革新:从合规审查到价值创造的战略跃迁

引言 在数字化转型浪潮中&#xff0c;全球企业每年因项目论证缺陷导致的损失高达1.7万亿美元&#xff08;Gartner 2023&#xff09;。CSPM&#xff08;Certified Strategic Project Manager&#xff09;认证体系通过结构化方法论&#xff0c;将传统的项目可行性评估升级为战略…

CLIP中的Zero-Shot Learning原理

CLIP&#xff08;Contrastive Language-Image Pretraining&#xff09;是一种由OpenAI提出的多模态模型&#xff0c;它通过对比学习的方式同时学习图像和文本的表示&#xff0c;并且能在多种任务中进行零样本学习&#xff08;Zero-Shot Learning&#xff09;。CLIP模型的核心创…

spring mvc 中 RestTemplate 全面详解及示例

RestTemplate 全面详解及示例 1. RestTemplate 简介 定义&#xff1a;Spring 提供的同步 HTTP 客户端&#xff0c;支持多种 HTTP 方法&#xff08;GET/POST/PUT/DELETE 等&#xff09;&#xff0c;用于调用 RESTful API。核心特性&#xff1a; 支持请求头、请求体、URI 参数的…

北大:LLM在NL2SQL中任务分解

&#x1f4d6;标题&#xff1a;LearNAT: Learning NL2SQL with AST-guided Task Decomposition for Large Language Models &#x1f310;来源&#xff1a;arXiv, 2504.02327 &#x1f31f;摘要 &#x1f538;自然语言到SQL&#xff08;NL2SQL&#xff09;已成为实现与数据库…

STM32LL库编程系列第八讲——ADC模数转换

系列文章目录 往期文章 STM32LL库编程系列第一讲——Delay精准延时函数&#xff08;详细&#xff0c;适合新手&#xff09; STM32LL库编程系列第二讲——蓝牙USART串口通信&#xff08;步骤详细、原理清晰&#xff09; STM32LL库编程系列第三讲——USARTDMA通信 STM32LL库编程…