【linux】进程间通信(匿名管道)

news2024/11/24 0:59:58

对于本篇文章我们采取三段论:是什么 为什么 怎么办。

目录

  • 进程间为什么要通信?
  • 进程间如何通信?
  • 进程间怎么通信?
    • 匿名管道:
      • 匿名管道原理:
      • 代码示例:
      • 匿名管道的情况与特征:

进程间为什么要通信?

进程间也是需要某种协同的。
协同应该怎么理解呢?
在这里插入图片描述
那么协同的前提必然是进行通信
但是通信的信息也是有类别的,比如说单纯的数据信息,或者控制信息…

了解了这个话题我们还要在阐述一个事实,进程间具有独立性:比如关闭你的QQ但是不影响微信。
进程 = 内核数据结构 + 数据与代码
那么这两份东西每个进程都是有独一份的,数据与代码会写时拷贝,所以不影响我们这个结论。

进程间如何通信?

这里我们要明确两个共识:
a. 进程间通信是有成本的(这个会在后边的代码中有验证)
b. 进程间通信的前提:让两个进程看到同一块内存资源

我们画图进行理解一下:
在这里插入图片描述

进程间怎么通信?

开始这个话题之前我们需要先知道一个东西:
互联网有大量标准的存在,否则为什么不同品牌的手机,不同大陆的手机厂商,用着不一样的硬件设备最后却能进行通信?
就是因为标准的存在!

所以我们进程间通信也有标准,这里我们只谈System V
这个标准提供了一些通信方式:

  1. 消息队列
  2. 共享内存
  3. 信号量

但是,人们刚接触通信时肯定是先想到尽量复用源代码进行通信的,故我们暂且不谈以上的方式(未来会谈)。
所以我们利用源代码设计出了两种方式

  1. 匿名管道
  2. 有名管道

这里我们先说匿名管道,因为他简单。

匿名管道:

匿名管道原理:

下图是一幅进程,文件与磁盘的简略图:
在这里插入图片描述
当对此文件以另一种方式打开时,变化如下
在这里插入图片描述
为什么答案是否定的呢?
在这里插入图片描述

那么当前进程fork()(创建子进程)会发生什么变化?
在这里插入图片描述
所以,现在我们就可以以普通文件为切入点理解匿名管道了

首先父进程打开同一个文件以“w”“r”方式,然后在进行创建子进程,
子进程会继承父进程的文件系统那一套数据结构,
这样他们就有了公共的一块内存,也就是那段内核缓冲区,
我们此时就可以一个进程向其中写入,另一个读取
在这里插入图片描述

但是我们匿名管道并不是普通文件,

  • 第一:我们的匿名管道不需要与磁盘进行交互了,因此,设计者要重新设计出一个新的通信接口
  • 第二:我们的管道只允许单向通信,因为他简单!所以我们fork之后要关闭一个进程的w和另一个进程的r

因此我们现在要理解一种现象以及两个问题:
现象:为什么我们的父子进程会向同一块显示屏打印数据

问题1:既然未来要关闭不需要的fd,那我们可不可以直接不打开呢?
问题2:可以不关闭吗?

对于这种现象是因为我们的bash父进程就已经打开了fd为0 1 2的文件描述符,所以bash的子进程们都继承了,所以他们会向同一块fd为1指向的内存进行疯狂输出并刷新到显示屏外设

对于问题1:不可以,因为这样子进程就不会继承了。
对于问题2:可以,单位了防止我们有意外操作建议关闭!

代码示例:

话说了这么多我们总得有理论实践,我们会使用pipe接口+fork来创建管道。

下段这80行的代码可以帮助我们理解匿名管道的情况与特征:
代码中会有注释:

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

const int SIZE = 1024;

std::string GetOtherMessage()
{
    static int cnt = 1;
    int id = getpid();
    std::string MessageCnt = std::to_string(cnt++);
    std::string ChildId = std::to_string(id);

    return "MessageCnt: " + MessageCnt + " child id: " + ChildId +  + " ";
}

子进程进行写入,但是我另外写了一段程序进行获取一段动态的信息。也就是如上的函数
void SubProcessWrite(int wfd)
{
    std::string message = "I am child,";
    while (true)
    {
        std::string info = message + GetOtherMessage();
        write(wfd, info.c_str(), info.size());
        sleep(1);
    }
}

父进程进行读
void FatherProcessRead(int rfd)
{
    char buffer[SIZE] = {0};
    while (true)
    {
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "father get message:" << buffer << std::endl;
        }
    }
}

int main()
{
    int pipefd[2];
    int ret = pipe(pipefd);
    if (ret == -1)
    {
        std::cerr << "errno: " << errno << " errstring: " << std::strerror(errno) << std::endl;
        return 1;
    }
    pid_t id = fork();
    if (id == 0)
    {
        std::cout << "我是子进程,已经关闭了0了" << std::endl;
        // 子进程,进行写,关闭r
        close(pipefd[0]);

        SubProcessWrite(pipefd[1]);
        close(pipefd[1]);
        exit(0);
    }
    std::cout << "我是父进程,已经关闭了1了" << std::endl;

    // 父进程,进行读,关闭w
    close(pipefd[1]);

    FatherProcessRead(pipefd[0]);
    close(pipefd[0]);

    // 父进程进行等待
    int rid = waitpid(id, nullptr, 0);
    if (rid > 0)
    {
        std::cout << "wait sucess" << std::endl;
    }

    return 0;
}

匿名管道的情况与特征:

对于下边情况的验证我们都可以通过对上边的程序进行修改得到结果。

四种情况:
在这里插入图片描述

在这里插入图片描述
对于2,我们可以将上面的程序进行一下修改在这里插入图片描述
同时让父进程sleep更长的时间,得到管道的大小为64kb(ubuntu22.04)

在这里插入图片描述
在这里插入图片描述
至于为什么不会阻塞是因为管道已经失效了。
在这里插入图片描述
首先我们要对这种情况进行一个探究,读端已经被关闭,说明写再多也没有用,OS不会允许这种浪费时空的东西存在,会直接杀掉写进程。
用什么杀死呢?
OS发送13号信号进行终止。

此时的这个管道也叫做broken pipe。


五种特征:

  1. 匿名管道:只能用于有血缘关系的进程之间,常用于父子进程。

  2. 管道之间自带进程的同步机制。
    (同步理解为多执行流代码的时候,具有顺序性)
    在这里插入图片描述

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

  4. 管道文件再通信的时候是面向字节流的(可以认为write的次数与read的次数不是一一匹配的,就像你用的水是从自然界收集来的,自然界用了10小时收集的水,你1分钟用完了)

  5. 管道的通信模式是一种特殊的半双工模式
    我们先来说一下全双工模式->类似于我们吵架时,我们可以同时说话
    半双工就是一个人说,一个人不说,或者相反过来
    而我们这个特殊的半双工就特殊在只能单向,只能一段说话,另一端不说话。

最后我们在输出最后一个结论:
我们在上文提到过:我们具有同步机制,但其实这不是一定的,当我们每次写入数据小于PIPE_BUF时才会具有这种机制,也叫作原子态(atomic)
在这里插入图片描述
匿名管道就结束啦!

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

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

相关文章

Debian 12 tomcat 9 catalina 日志信息 中文显示乱码

目录 问题现象 解决办法&#xff1a; 1、设定Debian locale 2、设定catalina.sh utf8字符集 问题现象 Debian 12 linux操作系统中&#xff0c;tomcat 9 catalina 启动日志输出 中文乱码 解决办法&#xff1a; 1、设定Debian locale 先确保系统本身就支持中文的 Debian …

【数据结构】算法的效率(时间复杂度和空间复杂度)

目录 一.算法的效率 二.时间复杂度 1.概念 2.大O的渐进表示法 3.常见时间复杂度计算举例 三.空间复杂度 四.常见复杂度对比 五. 复杂度的oj练习 1.消失的数字 2.轮转数字&#xff1a; 一.算法的效率 算法在编写成可执行程序后&#xff0c;运行时需要耗费时间资源和空…

uniapp 异步加载级联选择器(Cascader,data-picke)

目录 Props 事件方法 inputChange事件回调参数说明&#xff1a; completeChange事件回调参数说明&#xff1a; temList 属性Object参数说明 defaultItemList 属性Object参数说明 在template中使用 由于uniapp uni-ui的data-picke 不支持异步作者自己写了一个 插件市场下…

Deep Learning Part Seven基于RNN生成文本--24.5.2

不存在什么完美的文章&#xff0c;就好像没有完美的绝望。 ——村上春树《且听风吟》 本章所学的内容 0.引子 本章主要利用LSTM实现几个有趣的应用&#xff1a; 先剧透一下&#xff1a;是AI聊天软件&#xff08;现在做的ChatGPT&#xff08;聊天神器&#xff0c;水论文高手…

Latex小技巧:将图索引、表索引加到目录;调整公式段前段后间距

LaTex将图索引、表索引加到目录中 LaTex中通过\listoffigures命令生成图索引&#xff1b;通过listoftables命令生成表索引。但是图索引和表索引默认不出现在目录中。 为了将图索引、表索引加到目录中&#xff0c;使用\addcontentsline{}{}{}命令&#xff1a; \tableofconten…

【STM32+HAL】SDIO+DMA模式读写SD卡

一、准备工作 有关CUBEMX的初始化配置&#xff0c;参见我的另一篇blog&#xff1a;【STM32HAL】CUBEMX初始化配置 二、所用工具 1、芯片&#xff1a; STM32F407ZGT6 2、IDE&#xff1a; MDK-Keil软件 3、库文件&#xff1a;STM32F4xxHAL库 三、实现功能 实现用SDIODMA读写S…

实现优先队列——C++

目录 1.优先队列的类模板 2.仿函数的讲解 3.成员变量 4.构造函数 5。判空&#xff0c;返回size&#xff0c;返回队头 6.插入 7.删除 1.优先队列的类模板 我们先通过模板来进行初步了解 由上图可知&#xff0c;我们的模板里有三个参数&#xff0c;第一个参数自然就是你要存储的数…

WPF之可翻转面板

1&#xff0c;创建翻转面板的资源字典&#xff1a;FlippPanel.xaml。 无外观控件同样必须给样式指定类型&#xff08; <ControlTemplate TargetType"ss:FlipPanel">&#xff09;&#xff0c;相关详情参考&#xff1a;WPF之创建无外观控件-CSDN博客&#xff09…

Codeforces Round 943 (Div. 3) (A-G1) C++题解

目录 比赛链接 : A. Maximize? B. Prefiquence C. Assembly via Remainders D. Permutation Game E. Cells Arrangement F. Equal XOR Segments G1. Division LCP (easy version) G2. Division LCP (hard version) 比赛链接 : Dashboard - Codeforces Round 943 (…

【Spring】Spring中AOP的简介和基本使用,SpringBoot使用AOP

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 一、AOP简介 二、AOP个人浅谈 三、AOP中几个核心的方法注解 四、AOP中几个核心的属性 1.切入点&#xff08;PointCut&#xff09; 五、代码演示 1.SpringBoot引入依赖 2.定义一个AOP&#xff0c;也就是切面…

【数据库主从架构】

【数据库主从架构】 1. 什么是数据库的主从架构1.1 主从复制1.1.1 MySQL的主从主从复制技术三级目录 1. 什么是数据库的主从架构 随着公司业务线的增多&#xff0c;各种数据都在迅速增加&#xff0c;并且数据的读取流量也大大增加&#xff0c;就面临着数据安全问题&#xff0c;…

ICode国际青少年编程竞赛- Python-1级训练场-基础训练1

ICode国际青少年编程竞赛- Python-1级训练场-基础训练1 1、 Dev.step(4)2、 Dev.step(-4) Dev.step(8)3、 Dev.turnLeft() Dev.step(4)4、 Dev.step(3) Dev.turnLeft() Dev.step(-1) Dev.step(4)5、 Dev.step(-1) Dev.step(3) Dev.step(-2) Dev.turnLeft() Dev.step(…

Servlet(一些实战小示例)

文章目录 一、实操注意点1.1 代码修改重启问题1.2 Smart Tomcat的日志1.3 如何处理错误 一. 抓自己的包二、构造一个重定向的响应&#xff0c;让页面重定向到百度主页三、让服务器返回一个html数据四、表白墙4.1 约定前后端数据4.2 前端代码4.3 后端代码4.4 保存在数据库的版本…

超强动画制作软件blender

blender中文手册&#xff1a;Blender 4.1 Manual Blender 是一款集3D建模、渲染、动画、视频编辑、音频处理、游戏设计等多功能于一体的软件。由于其开源性质&#xff0c;它拥有庞大的用户群体和活跃的开发者社区&#xff0c;这使得Blender的功能和性能得到了不断的提升和优化…

Linux CentOS7部署ASP.NET Core应用程序,并配置Nginx反向代理服务器和Supervisor守护服务

前言&#xff1a; 本篇文章主要讲解的是如何在Linux CentOS7操作系统搭建.NET Core运行环境并发布ASP.NET Core应用程序&#xff0c;以及配置Nginx反向代理服务器。因为公司的项目一直都是托管在Window服务器IIS上&#xff0c;对于Linux服务器上托管.NET Core项目十分好奇。因为…

hot100 -- 二叉树(上)

目录 &#x1f382;二叉树的中序遍历 AC 递归 AC 迭代 &#x1f33c;二叉树的最大深度 AC DFS递归 AC BFS &#x1f6a9;翻转二叉树 AC 后序&#xff08;递归&#xff09; AC 中序 &#x1f6a9;对称二叉树 AC 递归 AC 迭代 &#x1f33c;二叉树的直径 A…

C语言之位操作符:<<、>>、、|、^、~,以及原码反码补码和例题详解

目录 前言 一、原码、反码、补码 二、移位操作符 三、位操作符&#xff1a;&、|、^、~ 四、经典例题分析&#xff1a; 总结 前言 本文将详细介绍C语言中左移操作符<<&#xff0c;右移操作符>>&#xff0c;按位与&&#xff0c;按位或|&#xff0c;按位异或^…

VS(Visual Studio)中查找项目里的中文字符

目录 正则表达式查找中文字符 正则表达式查找中文字符 在Visual Studio (VS) 中查找所有的中文字符&#xff0c;你可以使用其强大的查找和替换功能。不过&#xff0c;由于中文字符的范围非常广泛&#xff08;包括简体中文、繁体中文、日本汉字、韩国汉字等&#xff09;&#xf…

C语言——小知识和小细节17

一、未能给指针成功赋值 #include <stdio.h> #include <stdlib.h> #include <string.h>void GetMemory(char* p) {p (char*)malloc(20 * sizeof(char)); }void Test() {char* str NULL;GetMemory(str);strcpy(str, "Hello World!");printf(&quo…

计算机网络chapter2——应用层

文章目录 第2章 应用层章节引出—— 2.1应用层协议原理2.1.1 网络应用程序体系结构&#xff08;1&#xff09;客户-服务器体系结构&#xff08;2&#xff09;对等(P2P)体系结构2.1.2 进程通信1.客户和服务器进程2.进程与计算机网络之间的接口3. 进程寻址 2.1.3 可供应用程序使用…