Linux进程间通信 - 匿名管道(1)

news2024/12/23 18:51:55

之间我们学习了基础IO中有关文件,动静态库等知识,后面我们将讲述进程间通信的内容,在本文中就将来展示匿名管道。

进程间通信

进程间通信的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变

进程间通信发展

  • 管道
  • System V进程间通信
  • POSIX进程间通信

我们知道进程是具有独立性的,因此想要进程间进行通信需要增加通信的成本。为了解决进程间通信的问题,首要的就是要让两个进程看到同一份资源。然后让一方进行读取,另一方进行写入,完成通信。

管道

什么是管道

管道是Unix中最古老的进程间通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。之前我们在学习Linux的基本指令的时候就使用过 | 这个指令,这被我们称为管道,例如这个指令 who | wc -l 就是将本来应该打印到显示器文件的信息传入到了wc文件中,再由wc文件进行处理输出。

匿名管道

#include <unistd.h>
功能:创建一无名管道
原型
        int pipe(int fd[2]); // int fd[2] 这是一个输出型参数用管道的时候需要有读端和写端的记录
参数
        fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码 

匿名管道原理

进程在内存中创建PCB,然后通过进程所对应的文件描述符表打开对应的文件,匿名管道就是OS提供的一个内存级文件,进程的文件描述附表以读写的方式打开这个文件。然后进行fork()创建子进程,子进程会复制父进程相关的数据结构包括文件描述符表,不会打开父进程曾经打开的文件,这样父子进行就都会指向同一个文件,目标文件就会被多个进程看到。未来如果父进程向文件写入信息,子进程就可以接收到相关的信息,另外这种管道只支持单向通信,因此需要确定数据的流向,关闭不需要的fd。

简单的小例子

创建管道

    // 让不同的进程看到同一份资源
    // 任何一种进程间通信,一定要 先 保证不同的进程看到同一份资源
    int pipefd[2] = {0};
    // 1、创建管道
    int n = pipe(pipefd);    
    if (n < 0)
    {
        std::cout << "pipe error," << errno << ":" << strerror(errno) << std::endl;
        return 1;
    }
    std::cout << "pipefd[0]:" << pipefd[0] << std::endl; // 读端
    std::cout << "pipefd[1]:" << pipefd[1] << std::endl; // 写端

 

可以看到创建管道之后文件描述符3和4确实连接到了匿名管道文件的读端与写端。

子进程写入

// 2、创建子进程
    pid_t id = fork();
    assert(id != -1); // 正常应该使用判断,这里使用断言:意料之外使用if,意料之中使用assert        
    if (id == 0)
    {
        // Scene1(pipefd); // 正常通信场景
        // Scene2(pipefd); // 让子进程不断地去写入,但是父进程每隔10秒读一次
        // Scene3(pipefd); // 让子进程写慢一点,父进程读取不受限制
        // Scene4(pipefd); // 关闭写端
        // Scene5(pipefd); // 关闭读端
    }
void Scene1(int pipefd[])
{
    // child
    // 3、关闭不需要的文件描述符,让父进程进行读取,子进程进行写入
    close(pipefd[0]);

    // 4、开始通信 -- 结合某种场景
    const std::string namestr = "hello, 我是子进程";
    int cnt = 1;
    char buffer[1024];
    while (true)
    {
        snprintf(buffer, sizeof(buffer), "%s, 计数器: %d, 我的PID: %d\n", namestr.c_str(), cnt++, getpid());
        write(pipefd[1], buffer, strlen(buffer));
        sleep(1);
    }

    close(pipefd[1]);
    exit(0);
}
void Scene2(int pipefd[])
{
    // child
    // 3、关闭不需要的文件描述符,让父进程进行读取,子进程进行写入
    close(pipefd[0]);

    // 4、开始通信 -- 结合某种场景
    const std::string namestr = "hello, 我是子进程";
    int cnt = 1;
    char buffer[1024];
    while (true)
    {
       char x = 'X';
       write(pipefd[1], &x, 1);
       std::cout << "Cnt:" << cnt++ << std::endl;
    }
    close(pipefd[1]);
    exit(0);
}

void Scene3(int pipefd[])
{
    // child
    // 3、关闭不需要的文件描述符,让父进程进行读取,子进程进行写入
    close(pipefd[0]);

    // 4、开始通信 -- 结合某种场景
    const std::string namestr = "hello, 我是子进程";
    int cnt = 1;
    char buffer[1024];
    while (true)
    {
        snprintf(buffer, sizeof(buffer), "%s, 计数器: %d, 我的PID: %d", namestr.c_str(), cnt++, getpid());
        write(pipefd[1], buffer, strlen(buffer));
        sleep(10);
    }

    close(pipefd[1]);
    exit(0);
}

void Scene4(int pipefd[])
{
    // child
    // 3、关闭不需要的文件描述符,让父进程进行读取,子进程进行写入
    close(pipefd[0]);

    // 4、开始通信 -- 结合某种场景
    int cnt = 0;
    while (true)
    {
        char x = 'X';
        write(pipefd[1], &x, 1);
        std::cout << "Cnt:" << cnt++ << std::endl;
        sleep(1);
        break;
    }

    close(pipefd[1]);
    exit(0);
}

void Scene5(int pipefd[])
{
    // child
    // 3、关闭不需要的文件描述符,让父进程进行读取,子进程进行写入
    close(pipefd[0]);

    // 4、开始通信 -- 结合某种场景
    int cnt = 0;
    while (true)
    {
        char x = 'X';
        write(pipefd[1], &x, 1);
        std::cout << "Cnt:" << cnt++ << std::endl;
        sleep(2);
    }

    close(pipefd[1]);
    exit(0);
}

父进程读取

// parent
    // 3、关闭不需要的文件描述符,让父进程进行读取,子进程进行写入
    close(pipefd[1]);

    // 4、开始通信 -- 结合某种场景
    char buffer[1024];
    int cnt = 0;
    while (true)
    {
        // sleep(10); // Scene2 父进程每隔10秒进行读取 65535 2^16 
        // sleep(1); // Scene5
        int n = read(pipefd[0], buffer, sizeof(buffer) - 1); // 最多读1023个字符,需要预留出一个空位来赋给'\0'
        if (n > 0)
        {
            buffer[n] = '\0';
            std::cout << "我是父进程: child give me message: " << buffer << std::endl;
        }
        else if (n == 0)
        {
            std::cout << "我是父进程, 读到了文件结尾"<< std::endl;
            break;
        }
        else
        {
            std::cout << "我是父进程, 读异常了" << std::endl;
            break;
        }
        // if (cnt++ > 3) break; // Scene5 读端读一次就退出
    }
    close(pipefd[0]);
    int status = 0;
    waitpid(id, &status, 0);
    std::cout << "sig:" << (status &  0x7F) << std::endl; // 获取信号编号

关闭写端场景 

关闭读端场景 

 

匿名管道的特点

1、单向通信 -- 半双工

2、管道的本质是文件,因为fd的生命周期随进程,管道的生命周期也是随进程的

3、管道通信,通常用来进行具有血缘关系的进程,进行进程间通信。通常用与父子通信 -- pipe打开管道,并不清楚管道的名字,匿名管道

4、在管道通信中写入的次数,和读取的次数,不是严格匹配的 读写次数多少没有强相关 --- 表现 --- 字节流

5、具有一定的协同能力,让reader和writer能够按照一定的步骤进行通信 --- 自带同步机制

匿名管道通信的4种场景:

1、如果我们read读取完毕了所有的管道数据,如果对方不发,我们就只能等待

2、如果我们writer将管道写满了,我们还能写吗?不能

3、如果我们关闭了写端,读取完毕管道数据,再读就会read返回0

4、写端一只写读端关闭,会发生什么? 没有意义。OS不会维护无意义,低效率,或者浪费资源的事情。OS会杀死一直在写入的进程。OS会通过信号来终止进程 13)SIGPIPE

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

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

相关文章

Java低代码开发工具:jvs-rules 2.1.8 新版本功能清单

规则引擎用于管理和执行业务规则。它提供了一个中央化的机制来定义、管理和执行业务规则&#xff0c;以便根据特定条件自动化决策和行为。规则引擎的核心概念是规则。规则由条件和动作组成。条件定义了规则适用的特定情况或规则触发的条件&#xff0c;而动作定义了规则满足时要…

载誉而归丨盐城卓晨中心在第十届“北大青鸟杯”IT精英挑战赛中斩获多项殊荣!

2023年6月13日&#xff0c;第十届“北大青鸟杯”全国IT精英挑战赛华东&华中区域评审在合肥圆满落幕&#xff0c;当天下午&#xff0c;喜讯传来&#xff0c;北大青鸟盐城卓晨中心在此次比赛中摘得多项荣誉&#xff0c;包揽一、二、三等奖及优秀指导老师奖项&#xff0c;荣耀…

【Vue】学习笔记-VueRouter replace 编程式导航 缓存路由组件

VueRouter replace 编程式导航 缓存路由组件 路由跳转的replace方法编程式路由导航(不用<router-link>)缓存路由组件 路由跳转的replace方法 作用&#xff1a;控制路由跳转时操作浏览器历史记录的模式浏览器的历史记录有两种写入方式:push和replace push 是追加历史记录…

IntelliJ IDEA 2023.1中新的UI增强,加强了IDE编码体验!

IntelliJ IDEA&#xff0c;是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具&#xff0c;尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。 去…

专家意见何处寻:AI扮演领域专家角色为你答疑解惑

当我们寻求意见或建议时&#xff0c;ChatGPT是一个非常有用的工具。 作为通用的语言模型&#xff0c;ChatGPT 可以提供关于各种话题的建议和意见&#xff0c;如日常生活、工作、学习、人际关系、心理健康、科技和互联网、旅行和休闲、财务和投资、健康和医疗&#xff0c;以及环…

同事用python搞副业,失业了也能月入1W

今年2月&#xff0c;我失业了。好在是被裁的&#xff0c;有些补偿。裁的是整个部门&#xff0c;刚开始拿到赔偿以后还欢呼雀跃&#xff0c;天天聚会&#xff0c;天天嗨。到现在过去几个月了&#xff0c;我们没一个找到工作。我已经感受到了一股鸡蛋被煎糊的焦虑感 一次前同事聚…

新方案登场!“软硬兼施”让光伏组件焊接检测更高效

焊接是光伏组件生产中重要的工艺流程&#xff0c;就拿光伏组件中常见的组成部分——接线盒来举例&#xff0c;作为完成组件电力传输的关键器件&#xff0c;在组件的自动化生产过程中&#xff0c;接线盒的电极焊接质量直接关乎组件的性能&#xff0c;焊接质量检测的重要性不言而…

Dubbo简介和配置

1.Dubbo和OpenFeign的简介 Dubbo一个高性能rpc框架&#xff0c;用于构建分布式微服务架构&#xff0c;它提供了服务注册与发现&#xff0c;负载均衡&#xff0c;容错机制等功能。Dubbo具有高性能和低延迟的特点&#xff0c;适合于大规模的分布式系统。OpenFeign一个基于Java的…

Vue中如何进行屏幕录制与直播推流

Vue中如何进行屏幕录制与直播推流 屏幕录制和直播推流是现代Web应用中常用的功能&#xff0c;例如在线教育、视频会议和游戏直播等。Vue作为一种流行的JavaScript框架&#xff0c;提供了一些工具和库&#xff0c;可以方便地实现屏幕录制和直播推流功能。本文将介绍如何在Vue中…

解锁高效word开发!Word控件Aspose.words for.NET介绍

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。 Aspose API 支持流行文件格式处理&#xff0c;…

hard fault on thread: mqtt0解决办法

rt thread版本4.1.0 使用paho mqtt软件包 运行一段时间后出现 psr: 0x21000000 r00: 0x5036fc8f r01: 0x5036fc88 r02: 0x00000000 r03: 0x5036fc8f r04: 0x00000007 r05: 0x00000063 r06: 0x00005f70 r07: 0x2001f1d8 r08: 0xdeadbeef r09: 0xdeadbeef r10: 0xdeadbeef r11…

如何知道mysql是否有设置密码

可以通过以下几种方式知道MySQL是否设置了 root 用户的密码: 1. 尝试使用root用户登录MySQL 打开终端窗口,运行以下命令尝试使用root用户登录MySQL: bash mysql -u root -p如果显示MySQL提示符mysql>,则说明没有为root用户设置密码。 如果提示输入密码,说明root用户已设置…

Aviator源码:Aviator表达式引擎执行过程源码分析

目录 1.if执行脚本示例 2.源码分析 2.1 compile执行过程 2.1.1 CodeGenerator 2.1.2 ExpressionParser 2.1.3 if脚本ASM反编译结果 2.2 compiledExpression.execute执行过程 3.总结概述 由于Aviator支持的脚本语法较多&#xff0c;下面通过项目中使用较多的if语句来对a…

tinyWebServer 学习笔记——六、注册登录

文章目录 一、基础知识二、代码解析1. 载入数据库表2. 提取用户名和密码3. 同步线程登录注册4. 页面跳转 参考文献 一、基础知识 流程图 [1] 二、代码解析 1. 载入数据库表 // 用户名和密码 map<string, string> users;// 初始化数据库 void http_conn::initmysql_resu…

关于 Spring 中事务的嵌套,你了解多少?

Spring事务的的详细理解&#xff0c;事务嵌套解析&#xff0c;以及事务失效的场景解惑 想要了解Spring的事务嵌套&#xff0c;我们先了解一下Spring的七种事务传播属性各自表示的意思 propagation_requierd&#xff1a;如果当前没有事务&#xff0c;就新建一个事务&#xff0c…

简单的TCP网络程序·单进程(后端服务器)

目录 文件1&#xff1a;tcpServer.cc 文件2&#xff1a;tcpServer.hpp 1.提出日志概念 -- 在后续完善 日志格式 -- 暂定简单的打印功能 2.创建套接字 SOCK_STREAM -- socket参数 3.bind自己的套接字 4.设置socket 为监听状态 * 新接口1&#xff1a;listen 函数1&…

Spring Boot进阶(46):集成Jackson之快速入门 | 超级详细,建议收藏

1. 前言&#x1f525; 在上一期《SpringBoot之Jackson配置全局时间日期格式》文中提到Jackson&#xff0c;了解到有很多小伙伴对它很感兴趣&#xff1b;顾这一期&#xff0c;我就重点带着大家以最基础的教学方式领大家入门&#xff0c;废话不多说&#xff0c;咱们这就开始。 这…

(字符串) 925. 长按键入 ——【Leetcode每日一题】

❓925. 长按键入 难度&#xff1a;简单 你的朋友正在使用键盘输入他的名字 name。偶尔&#xff0c;在键入字符 c 时&#xff0c;按键可能会被长按&#xff0c;而字符可能被输入 1 次或多次。 你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字&#xff08;…

【大学物理实验】表面张力

文章目录 选择题选择题 液体表面张力只存在与液体的: A. 内部 B. 底部 C. 表面 D. 表面和内部 正确答案: C 本实验中,下面哪一件测量仪器(工具)是不需要的: A. 力敏传感器 B. 数字电压表 C. 游标卡尺 D. 物理天平 正确答案: D 关于吊环从液体中拉脱力(即最大表面张力)…

距离和相似性度量

文章目录 1. 距离度量1.1 欧几里得距离(Euclidean Distance)1.2 明可夫斯基距离(Minkowski Distance)1.3 曼哈顿距离(Manhattan Distance)1.4 切比雪夫距离(Chebyshev Distance)1.5 马哈拉诺比斯距离(Mahalanobis Distance) 2. 相似性度量2.1 向量空间余弦相似度(Cosine Simila…