Linux 进程通信——管道

目录

一、什么是进程通信

二、为什么要进行进程通信 

三、如何进行通信

四、管道

1、什么是管道

 2、管道的原理

3、接口

4、编码实现

5、管道的特征 

6、管道的4种情况


一、什么是进程通信

进程通信是两个或多个进程实现数据层面的交互。

因为进程具有独立性,导致进程通信的成本比较高,因为要打破进程的独立性。

二、为什么要进行进程通信 

  1. 在两个或多个进程可能会有以下
  2. 发送基本数据
  3. 发送命令
  4. 实现某种协同
  5. 进行通知

三、如何进行通信

进程间通信的本质:必须让不同的进程看到同一份“资源”,也能叫做同一份内存空间。这个资源是特定形式的内存空间。

那么这份空间将由谁来提供呢?

如果存放在进程里,要进行进程通信的话,另一个进程要来访问该进程,对该进程的这份空间可能做增删查改,那这样就破坏了进程的独立性。

所以这份空间由操作系统提供,防止破坏两进程的独立性,开辟第三空间。

我们进程访问这个空间,进行通信,本质上就是访问操作系统。 

我们可以把进程理解成用户,内存的创建、使用、释放都由系统调用接口,从底层设计,接口设计,都要由操作系统独立设计。因为操作系统不相信任何用户

一般的操作系统,会有一个独立的通信模块——隶属于文件系统——IPC通信模块

进程间通信是有标准的——system V(消息队列,共享内存,信号量)&&posix

这是历史发展得出两个标准,后面我们会给大家详细介绍system V。

我们接下来给大家讲解基于文件级别的通信方式——管道。

四、管道

1、什么是管道

把一个进程连接到另一个进程的一个数据流称作为管道。

我们来看看这张图理解

如果一个文件被可以多个进程打开,那么这个文件就可以作为公共资源,一个读,一个写即可

所谓的管道就是类似的基于文件级别的通信方式

管道是Unix中最古老的进程间通信的形式。

 2、管道的原理

我们启动一个进程的时候,PCB里面有一个文件描述符表,键盘显示器文件对应0、1、2描述符,这里显示器只有一个文件,方便理解我这里画了两个,当我们新打开一个文件的时候,这个文件的地址会放入到文件描述符表中未存放最小的描述符中,这个新打开文件中inode用于寻找文件属性,有file_operators用于实现一切皆文件,还有一个缓冲区。

在磁盘中,也会在特定的分区特定的分组有对应的数据块和属性块,供我们读写,如果写的时候,一旦对应的位置为脏,那么就会往回刷新过去,从而进行写入。

(即打开文件的时候,因为struct file本身就会创建一个struct inode,这个inode里面就是文件的属性,供我们查看,这个inode里面的会直接从磁盘当中的inode里面加载。缓冲区则是磁盘当中的数据要先加载到这个缓冲区中,当我们利用file_operators的方法去修改缓冲区以后,就是数据为脏了,然后就会将这个数据刷新回磁盘)

当我们创建的文件不在磁盘上刷新,并且有对应的inode、file_operators、缓冲区,那么这个文件为内存级文件。 

当我们创建子进程的时候,会拷贝父进程的PCB,文件描述符也会拷贝一份,这两份文件描述符表的内容是一模一样的,左边的都是属于进程的,而右边的是属于文件的,不需要拷贝也不会拷贝。

但子进程的文件描述符表要指向父进程文件描述符表指向的内容。

 

而我们前面所说的进程间通信,本质前提是需要先让不同的进程,看到同一份资源!!

这样我们就可以利用这个文件实现进程间通信了!

这就是管道的一个比较朴素的原理

所以管道其实就是文件

而且在这里会由于有引用计数,即便父进程关闭了这个文件,也不会消失的。

在这里如果我们父进程只有只读方式打开,那么这个文件描述符表继承下来的时候也是只读方式,这就没法通信了。所以其实父进程在打开文件的时候,会把文件以读写方式都打开一遍。这样的话就是下面的原理了!

上图即是父进程关闭了读端,子进程关闭了写端,这样就可以构成父进程往里面写入数据,子进程从中读取数据。

如下图所示,当我们的task_struct要将同一个文件分别以读写的方式打开的时候,会分别创建对应的struct file,只不过他们里面的inode,文件缓冲区,等等都是一样的,只是权限不同。然后我们继续创建子进程

像上面的这种,我们只想用来实现单向通信的

假设现在,我们想让子进程进行写入,父进程进行读取

当我们想要子进程写入,父进程读取的时候,只需要关闭对应的读写端即可

此时,两个struct file的引用计数都会变为1,也不可能会再次产生影响

这就是管道的原理

正式因为这个只能进行单向通信,所以才将它称作管道

那么如果要双向通信呢?

我们可以创建多个管道,比如两个管道就可以了

那么这两个进程如果没有任何关系,可以用我们上面的原理进行通信吗?

不能。必须是父子关系,兄弟关系,爷孙关系…

总之必须是具有血缘关系的进程,只不过常用于父子

那么我们这个文件有名字,路径…吗?即下面这部分

答案是没有的,它根本不需要名字,更不需要怎么标定它,因为它是通过继承父进程的资源来得到的。

所以我们把这种管道的名字叫做匿名管道

当然至此我们还没有通信,我们前面所做的工作都是建立通信信道,那么为什么这么费劲呢?这是因为进程具有独立性,通信是有成本的

3、接口

 我们先来认识一下系统调用接口,它的作用是创建一个管道。

 如果成功返回0,失败返回-1,并且错误码errno被设置

 那这个参数pipefd[2]是什么意思?

调用这个pipe以后,父进程就会以读写方式打开一个内存级文件了。

打开以后,它的工作就完了。

所以这个参数的意思就是,创建好内存级文件以后,就会把对应的两个文件描述符给带出来,供用户使用!!!

其中,一般pipefd[0]是读下标,pipefd[1]是写下标。

4、编码实现

 我们先测试一下我们上面得出来的结论

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
int main()
{
    int pipefd[2]={0};
    int n=pipe(pipefd);
    if(n<0) return 1;
    std::cout<<"pipefd[1]:"<<pipefd[0]<<"   "<<"pipefd[2]:"<<pipefd[1]<<std::endl;
    return 0;
}

 结论如下:

我们来模拟实现一下管道

我们这次让子进程写入,父进程读取。

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>

#define N 2
#define NUM 1024

using namespace std;

void Writer(int wfd)
{

    string s = "hello I am child";
    pid_t self = getpid();
    int number = 0;
    char buffer[NUM];
    while(1)
    {
        //构建发送字符串
        buffer[0] = 0;
        snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, number++);
        //cout << buffer;
        
        //发送给父进程
        write(wfd, buffer, strlen(buffer));
        sleep(1);
    }
}
void Reader(int rfd)
{
    char buffer[NUM];
    while(1)
    {
        buffer[0] = 0;
        ssize_t n = read(rfd, buffer, sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;
            cout << "father get a message : ["<<buffer << "]" << endl;           
        }
    }
}

int main()
{
    int pipefd[N] = {0};
    int n = pipe(pipefd);
    if(n < 0) return 1;
    // std::cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << std::endl;
    
    pid_t id = fork();
    if(id < 0)
    {
        return 2;
    }
    else if(id == 0)
    {
        //child
        close(pipefd[0]);
    
        Writer(pipefd[1]);
    
        close(pipefd[1]);
        exit(0);
    }
    //father
    close(pipefd[1]);
    Reader(pipefd[0]);

    pid_t rid = waitpid(id, NULL, 0);
    if(rid < 0) return 3;

    close(pipefd[0]);
    return 0;
}

运行结果如下:

 

 

5、管道的特征 

  1. 管道之间是单向通信的。
  2. 具有血缘关系的进程进行进程通信
  3. 父子进程是会进程协同的,同步互斥的。

我们把写入休眠3秒。

我们能看到是子进程输入的时候休眠3秒,父进程在读取的时候,在等待子进程输入。再读 

 

这是为了防止父进程在每读完的时候,子进程又写入,把数据覆盖了,父进程前面的读原来的数据,后面又是新的数据,导致错误。

 4.管道是面向字节流的。

 5.管道是基于文件的,而文件的生命周期是随进程的,进程关闭,管道也关闭,操作系统会回收未关闭的文件描述符对应的文件。

6、管道的4种情况

  1. 读写端正常,管道如果为空,读端就要阻塞

这就是我们刚刚介绍的子进程输入休眠3秒,父进程读取的时候等待子进程输入。

     2.读写端正常,管道如果被写满,写端就要被阻塞

在父进程读之前休眠50秒。

这里我们会有一个疑问了,那管道的大小是多少呢?

我们可以先用下面这个命令观察一下,这个命令的功能是查看一些数据的最大限制

ulimit -a、

这里显示pipe size是512*8个字节 也就是4kb 

我们写一段代码看看是不是4kb呢?

每次往里面写1个字节,直到写满。

我们能看到总共写入了65535次,也就是65535个字节=65535/1024=64kb 

那我们前面的4KB是什么呢?

其实管道的大小在不同的内核中是不同的。

当前我们系统的管道大小是64KB

现在回答前面的4KB究竟是什么,这是因为我们写端在写入数据以后,读端要读数据,但是不能写了一半就读走了,这样可能导致数据出现问题。所以要么就不读,要么一次全读完, 也就是PIPE_BUF就是要保证是一个原子性的最大长度。不能被打断的,而这个PIPE_BUF就是4KB,也就是前面查到的4KB。

3.读端正常,写端关闭,读端就会读到0,读到文件结尾,并不会阻塞

结果如下,会不断往显示器上输出0

 所以我们应该更改代码

结果如下:

 

4.写端正常写入,读端关闭了,操作系统会杀死正在写入的进程 

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>

#define N 2
#define NUM 1024

using namespace std;

void Writer(int wfd)
{

    string s = "hello I am child";
    pid_t self = getpid();
    int number = 0;
    char buffer[NUM];
    while(1)
    {
        //构建发送字符串
        buffer[0] = 0;
        snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, number++);
        write(wfd,buffer,strlen(buffer));        //cout << buffer;
        sleep(1);
        
        //发送给父进程
        // char c='c';
        // write(wfd, &c, 1);
        // cout <<number++<<endl;
    }
}
void Reader(int rfd)
{
    char buffer[NUM];
    int cnt=5;
    while(cnt--)
    {
        buffer[0] = 0;
        ssize_t n = read(rfd, buffer, sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;
            cout << "father get a message : ["<<buffer << "]" << endl;           
        }
        else if(n==0)
        {
            cout<<"father read done!"<<endl;
            break;
        }
        else break;
        
    }
}

int main()
{
    int pipefd[N] = {0};
    int n = pipe(pipefd);
    if(n < 0) return 1;
    // std::cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << std::endl;
    
    pid_t id = fork();
    if(id < 0)
    {
        return 2;
    }
    else if(id == 0)
    {
        //child
        close(pipefd[0]);
    
        Writer(pipefd[1]);
    
        close(pipefd[1]);
        exit(0);
    }
    //father
    close(pipefd[1]);
    Reader(pipefd[0]);
    close(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(3);

    close(pipefd[0]);
    return 0;
}

在还没执行到等待的时候,子进程一直是僵尸的

 

我们最后看到终止信号是13。

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux中DataX使用第四期

简介 紧接着上期关于定义如何一个简单的插件&#xff0c;本期了解下关系型数据库的数据读取和数据写入。 环境 Windows10 (linux中命令相似&#xff0c;为了方面调试就用windows的)JDK(1.8以上&#xff0c;推荐1.8)Python(2或3都可以)Apache Maven (推荐3.x版本)IntelliJ IDEA…

Java计算机毕业设计基于SSM宠物美容信息管理系统数据库源代码+LW文档+开题报告+答辩稿+部署教程+代码讲解

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…

核货宝助力连锁门店订货数字化转型升级

在竞争激烈的连锁零售行业&#xff0c;传统订货模式弊端日益凸显&#xff0c;严重制约着企业的发展。核货宝订货系统以其卓越的数字化解决方案&#xff0c;为连锁门店订货带来了全方位的变革&#xff0c;助力企业实现数字化转型升级&#xff0c;在市场中抢占先机。 一、增强总部…

论文解读 | AAAI'25 Cobra:多模态扩展的大型语言模型,以实现高效推理

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 个人信息 作者&#xff1a;赵晗&#xff0c;浙江大学-西湖大学联合培养博士生 内容简介 近年来&#xff0c;在各个领域应用多模态大语言模型&#xff08;MLLMs&…

java中的Entry类,map接口

看Redisson源码时候发现有个Entry&#xff0c;眼熟&#xff0c;遂查资料 Map.Enty<KV> 是在Map接口中的一个内部接口Entry 作用&#xff1a;当Map集合一创建那么就会在Map集合中创建一个Enty对象&#xff0c;用来记录键与值&#xff08;键值对对象&#xff0c;键与值的…

HarmonyOS学习第4天: DevEco Studio初体验

初次邂逅&#xff1a;DevEco Studio 在数字化浪潮汹涌澎湃的当下&#xff0c;移动应用开发领域始终是创新与变革的前沿阵地。鸿蒙系统的横空出世&#xff0c;宛如一颗璀璨新星&#xff0c;照亮了这片充满无限可能的天空&#xff0c;为开发者们开启了一扇通往全新世界的大门。而…

ue5地面上出现preview字样

如图&#xff1a; 解决办法 将光源修改为moveable 参考博客&#xff08;UE光影有preview字样、输出也有_ue5阴影preview消除-CSDN博客

Unity 适用于单机游戏的红点系统(前缀树 | 数据结构 | 设计模式 | 算法 | 含源码)

文章目录 功能包括如何使用 功能包括 红点数据本地持久化 如果子节点有红点&#xff0c;父节点也要显示红点&#xff0c;父节点红点数为子节点红点数的和&#xff1b; 当子节点红点更新时&#xff0c;对应的父节点也要更新&#xff1b; 当所有子节点都没有红点时&#xff0c…

个人环境配置--安装记录

根据显卡下载对应的cuda和cudnn 我使用的是docker,首先拉取镜像,我用的是ubuntu20.04 加速&#xff1a;pull hub.1panel.dev/ devel是开发版本 sudo docker pull hub.1panel.dev/nvidia/cuda:11.6.1-devel-ubuntu20.04先测试一下cuda有没有安装好 nvcc -V更新&#xff0c;安装…

代码审计初探

学会了基础的代码审计后&#xff0c;就该提高一下了&#xff0c;学一下一些框架的php代码审计 先从一些小众的、已知存在漏洞的cms入手 phpems php的一款开源考试系统 源码下载 https://down.chinaz.com/soft/34597.htm 环境部署 windows审计&#xff0c;把相关文件放到phps…

记录Qt 虚拟键盘样式修改与使用

文章目录 概述 一、使用虚拟键盘 二、项目文件定义 1.VirtualKeyboard.qml 2.main.qml 3.main.cpp 三、编译运行 1.编译 2.运行 3.运行效果 总结 概述 在 Qt 开发中&#xff0c;虚拟键盘是移动设备和嵌入式设备中常用的输入工具。本文将详细介绍如何通过 QML 修改虚拟键盘样式…

国产开源PDF解析工具MinerU

前言 PDF的数据解析是一件较困难的事情&#xff0c;几乎所有商家都把PDF转WORD功能做成付费产品。 PDF是基于PostScript子集渲染的&#xff0c;PostScript是一门图灵完备的语言。而WORD需要的渲染&#xff0c;本质上是PDF能力的子集。大模型领域&#xff0c;我们的目标文件格…

基于单片机的智能电表设计(论文+源码)

2.1 系统整体方案设计 本课题为基于单片机的电子式单项智能电表&#xff0c;在此设计如图2.1所示的系统总体架构&#xff0c;其采用STM32单片机作为主控制器&#xff0c;搭配外设HLW8032模块实现对电压&#xff0c;电流&#xff0c;功率因数&#xff0c;电能消耗等参数进行检…

Kafka在Windows系统使用delete命令删除Topic时出现的问题

在使用Windows的Kafka时&#xff0c;想要删除某一个主题&#xff0c;发现使用了delete之后会一直报警告。下面是我发现错误之后重新实测的Bug 先创建2个topic kafka-topics.bat --bootstrap-server localhost:9092 --topic test1 --createkafka-topics.bat --bootstrap-serve…

【部署优化篇十三】深度解析《DeepSeek API网关:Kong+Nginx配置指南》——从原理到实战的超详细手册

一、为什么需要API网关?从单体服务到微服务的必然选择 1.1 单体服务的痛点 想象一下早期的淘宝——所有功能(用户中心、商品管理、订单系统)都打包在一个巨型服务里。这样的架构存在三大致命问题: 单点故障:一旦服务崩溃,整个系统瘫痪扩展困难:每次发布都需要全量部署…

在一个集成的 SynMatrix-Ansys 设计工作流程中实现 3D 滤波器仿真

Synmatrix Technologies Inc.是Ansys 的技术合作伙伴&#xff0c;通过一体化 RF 滤波器设计和测试调整软件平台提供解决方案&#xff0c;该平台可与 Ansys HFSS 3D 高频电磁 (EM) 仿真软件配对&#xff0c;以减少开发时间、材料和运营成本、设计风险和产品故障。 典型的射频/微…

仿uni-segmented-control添加左右滑动效果

官网的选项卡没有左右滑动的效果&#xff0c;本身的样式也不太好看&#xff0c;所以封装了个简易的组件达到相同的效果&#xff0c;可自行修改css部分修改样式~ 封装组件效果如下 custom-segmented-control.vue <template><view class"container"><v…

LeetCode51

LeetCode51 目录 题目描述示例思路分析代码段代码逐行讲解复杂度分析总结的知识点整合总结 题目描述 N 皇后问题&#xff1a;将 n 个皇后放置在 n x n 的棋盘上&#xff0c;使得皇后彼此之间不能相互攻击&#xff08;即任何两个皇后不能在同一行、同一列或同一斜线上&#x…

Lineageos 22.1(Android 15)Launcer简单调整初始化配置

一、前言 Launcer的初始化配置主要在如下的xml文件夹下&#xff0c;默认读取的5x5 这里我们把device_profiles调整一下&#xff0c;然后新建一个default_workspace_my.xml作为我们自己的配置就行。 二、配置 注意Lineageos 的Launcer是在lineageos/packages/apps/Trebuchet…

2025.2.23机器学习笔记:PINN文献阅读

2025.2.23周报 一、文献阅读题目信息摘要Abstract创新点网络架构架构A架构B架构C 实验结论后续展望 一、文献阅读 题目信息 题目&#xff1a; Physics-Informed Neural Networks for Modeling Water Flows in a River Channel期刊&#xff1a; IEEE TRANSACTIONS ON ARTIFICI…