【Linux进程间通信】Linux匿名管道详解:构建进程间通信的隐形桥梁

news2024/12/22 11:52:23

📝个人主页🌹:Eternity._
⏩收录专栏⏪:Linux “ 登神长阶 ”
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

❀Linux进程间通信

  • 📒1. 进程间通信介绍
  • 📚2. 什么是管道
  • 📜3. 匿名管道
    • 🌞fork共享管道原理
    • 🌙结合文件描述符
    • ⭐站在内核角度
  • 📝4. 管道的读写情况与特点
    • 🎈管道的读写情况
    • 🎩管道的特性
  • 📖5. 总结


前言:当提及Linux系统中的进程间通信(IPC),管道(Pipes)无疑是最基础且广泛使用的一种机制。作为匿名通信的典范,管道为进程间数据交换提供了一个简单而有效的途径。在这个信息飞速传递的时代,掌握Linux管道的使用不仅是理解操作系统底层通信原理的关键一步,也是提升软件开发效率、构建复杂应用系统的必备技能

本篇文章将带您深入探索Linux进程间匿名通信的管道机制。我们将从管道的基本概念出发,逐步揭开其背后的工作原理,并通过实例演示如何在实际编程中创建、使用和维护管道。无论您是初学者,希望建立对Linux IPC的初步认识;还是经验丰富的开发者,渴望在现有基础上进一步精进;亦或是对系统编程充满好奇的学习者,渴望深入了解操作系统内部的奥秘,本文都将为您提供丰富的知识和实用的指导

我们将详细介绍管道的创建过程、数据读写操作、管道的生命周期管理以及常见的使用场景。 同时,我们还会探讨管道在并发编程中的表现,分析其在多进程环境下的行为特性,并提供相应的优化策略。通过理论与实践相结合的方式,相信您能够全面掌握Linux进程间匿名通信的管道技术,为您的软件开发之路增添一份坚实的力量

让我们一同踏上这段探索之旅,揭开Linux管道的神秘面纱,领略其在进程间通信中的独特魅力!


📒1. 进程间通信介绍

进程间通信(Interprocess communication,IPC)是指在不同的进程之间传播或交换信息。由于进程的用户空间是互相独立的,一般而言不能互相访问,但存在一些双方都可以访问的介质或系统空间来实现通信

  • 原理: 进程间通信主要依赖于双方都可以访问的介质或系统空间。这些介质包括共享内存区、系统空间以及双方都可以访问的外设(如磁盘上的文件、数据库中的表项等)。然而,广义上的通过这些方式进行的通信一般不算作“进程间通信”。进程间通信更常见的是通过一组编程接口来实现,这些接口允许程序员协调不同的进程,使它们能在一个操作系统里同时运行,并相互传递、交换信息

  • 必要性: 即使只有一个用户发出要求,也可能导致一个操作系统中多个进程的运行。这些进程之间必须互相通信,以协调它们的行为和共享资源。进程间通信使得一个程序能够在同一时间里处理许多用户的要求


📚2. 什么是管道

  • 管道是Unix中最古老的进程间通信的形式
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

在这里插入图片描述

管道分为:匿名管道和命名管道,本篇我们主要来了解一下匿名管道


📜3. 匿名管道

匿名管道是Linux中一种非常基础的进程间通信(IPC)方式,其本质上是一种内存级的文件,专门用于父子进程间或具有亲缘关系的进程间的通信

创建匿名管道

#include <unistd.h>

//功能:创建一无名管道
//原型
int pipe(int fd[2]);

//参数
//fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
//返回值:成功返回0,失败返回错误代码

在这里插入图片描述
实例代码:

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

#define MAX 1024

using namespace std;

int main()
{
    // 1. 建立管道
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    assert(n == 0);
    // 定义 n
    (void)n;
    // 查看文件描述符
    cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << endl;

    // 2. 创建子进程
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return 1;
    }

    // 子写,父读,
    // 3. 关闭父子不需要的fd,形成单向通信的管道
    if(id == 0)
    {
        // 子进程
        close(pipefd[0]);

        // 写入
        int cnt = 10;
        while(cnt)
        {
            char message[MAX];
            snprintf(message, sizeof(message), "hello father, I am child, pid: %d, cnt: %d", getpid(), cnt);
            cnt--;
            write(pipefd[1], message, strlen(message));
            cout << "writing cnt: " << cnt << endl;
        }

        exit(0);
    }

    // 父进程
    close(pipefd[1]);

    // 读取
    char buffer[MAX];
    while(true)
    {
        ssize_t n = read(pipefd[0], buffer, sizeof(buffer)-1);
        if(n == 0)
        {
            cout << "child qiut, read tail" << endl;
            break;
        }
        else if(n > 0)
        {
            buffer[n] = 0; // '\0', 当作字符串
            cout << getpid() << ": " << "child say: " << buffer << " to me!" << endl;
        }
    }

    pid_t rid = waitpid(id, nullptr, 0);
    if(rid == id)
    {
        cout << "wait seccess" << endl;
    }

    return 0;
}

🌞fork共享管道原理

在这里插入图片描述


🌙结合文件描述符

在这里插入图片描述


⭐站在内核角度

在这里插入图片描述


📝4. 管道的读写情况与特点

🎈管道的读写情况

  1. 正常情况,如果管道没有数据了,读端必须等待,直到有数据为止(写端写入数据)
  2. 正常情况,如果管道被写满了,写端必须等待,直到有空间为止(读端读走数据)

我们让读端一直读,而写端在写入部分文件后让它sleep一段时间,我们这是来观察一下读端的情况

代码示例:(C++):

if(id == 0)
{
    // 子进程
    close(pipefd[0]);

    // 写入
    int cnt = 10000;
    while(cnt)
	{
        char message[MAX];
    	snprintf(message, sizeof(message), "hello father, I am child, pid: %d, cnt: %d", getpid(), cnt);
        cnt--;
        write(pipefd[1], message, strlen(message));
        // 在正常写入一次后,sleep,父进程读取不做修改
        sleep(4);       
	}
    exit(0);
}

在这里插入图片描述


当我们的管道被写满了的时候,写端就不能在进行写入了,我们必须等待读端将数据读取走才能继续往管道里面写入,我们让读端休眠上几面,让写端一直写

代码示例:(C++):

if(id == 0)
{
    // 子进程
    close(pipefd[0]);

    // 写入
    int cnt = 0;
    while(true)
	{
        char message[MAX];
    	snprintf(message, sizeof(message), "hello father, I am child, pid: %d, cnt: %d", getpid(), cnt);
        cnt++;
        write(pipefd[1], message, strlen(message));
        // 在正常写入一次后,sleep,父进程读取不做修改
        cout << "writing cnt: " << cnt << endl; 	
	}
    exit(0);
}

在这里插入图片描述


  1. 写端关闭,读端一直读取,读端会读到read返回值为0,表示读到文件结尾
  2. 读端关闭,写端一直写入,0S会直接杀掉写端进程,通过想目标进程发送SIGPIPE(13)信号,终止目标进程

写端关闭代码示例:(C++):

if(id == 0)
{
    // 子进程
    close(pipefd[0]);

    // 写入
    int cnt = 0;
    while(true)
    {
        char message[MAX];
     	snprintf(message, sizeof(message), "hello father, I am child, pid: %d, cnt: %d", getpid(), cnt);
        cnt++;
        write(pipefd[1], message, strlen(message));
        //sleep(2);
        cout << "writing cnt: " << cnt << endl;
        // 在写入两次时,我们将子进程的写入关闭
        if(cnt == 2)
        {
        	close(pipefd[1]);
            break;
        }
    }

    exit(0);
}

// 父进程
close(pipefd[1]);

// 读取
char buffer[MAX];
while(true)
{
	sleep(4);
    ssize_t n = read(pipefd[0], buffer, sizeof(buffer)-1);
    // 当 n == 0 时,代表read已经读到文件结尾了
    if(n == 0)
    {
    	cout << "child qiut, read tail" << endl;
        break;
    }
    else if(n > 0)
    {
        buffer[n] = 0; // '\0', 当作字符串
        cout << getpid() << ": " << "child say: " << buffer << " to me!" << endl;
    }
}

我们这样设计代码,先让子进程写入之后,关闭掉pipefd[1],然后观察父进程是否会打印,我们需要的代码

在这里插入图片描述


读端关闭代码示例:(C++):

// 父进程
close(pipefd[1]);

// 读取
char buffer[MAX];
while(true)
{
	//sleep(4);
    ssize_t n = read(pipefd[0], buffer, sizeof(buffer)-1);
    if(n == 0)
    {
    	cout << "child qiut, read tail" << endl;
        break;
    }
    else if(n > 0)
    {
        buffer[n] = 0; // '\0', 当作字符串
        cout << getpid() << ": " << "child say: " << buffer << " to me!" << endl;
    }
    cout << "father return val(n)" << n << endl;
    sleep(1);
    // 打印一次后,我们退出循环    
    break;
}

// 关闭 pipefd[0],停止读取
cout << "close point read" << endl;
close(pipefd[0]);

sleep(3);

int status = 0;
pid_t rid = waitpid(id, &status, 0);
if(rid == id)
{
    cout << "wait seccess, exit sig: " << (status&0x7f) << endl;
}

注意:当前状态码 & 0x7f可以查看到最后的退出码

在这里插入图片描述


🎩管道的特性

管道的5种特性

  1. 匿名管道,可以允许具有血缘关系的进程之间进行进程间通信,常用与父子,仅限于此
  2. 匿名管道,默认给读写端要提供同步机制
  3. 面向字节流的入
  4. 管道的生命周期是随进程的
  5. 管道是单向通信的,半双工通信的一种特殊情况

在了解完管道的这些情况和特征后,我们可以利用管道来写一个简单的线程池

线程池代码链接


📖5. 总结

在探索Linux进程间匿名通信的管道机制这一旅程的尾声,我们不禁对Linux操作系统的精妙设计和强大功能有了更深一层的理解。管道,作为进程间通信的基础而又高效的工具,不仅简化了数据在不同进程间的流动过程,还极大地促进了多任务并发执行的灵活性

通过本文的学习,我们见证了管道从创建到使用的全过程,理解了其背后的工作原理,并掌握了如何在实际编程中利用管道来实现进程间的数据交换。从pipe()函数的调用,到文件描述符的分配,再到数据的读写操作,每一个步骤都蕴含着Linux系统设计的智慧与匠心

但Linux提供的进程间通信机制远不止于此。命名管道、消息队列、共享内存、信号量以及套接字等多种IPC方式,各自拥有独特的优势和适用场景。在未来的学习与实践中,我们可以继续深入探索这些机制,以更加灵活多样的方式实现进程间的协同工作

让我们以更加饱满的热情和坚定的信心,继续前行在Linux系统编程的学习之路上!

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

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

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

相关文章

22.1 k8s不同role级别的服务发现

本节重点介绍 : 服务发现的应用3种采集的k8s服务发现role 容器基础资源指标 role :nodek8s服务组件指标 role :endpoint部署在pod中业务埋点指标 role :pod 服务发现的应用 所有组件将自身指标暴露在各自的服务端口上&#xff0c;prometheus通过pull过来拉取指标但是promet…

期权卖方如何选择铁矿石行权价?期权策略盈亏分析计算方式详解

截止9月30日收盘&#xff0c;铁矿石2411合约收盘价825元/吨。日线级别处于上涨趋势中 假设以825元为最新价&#xff0c;假设后市铁矿石期货价格会下跌&#xff0c;期权卖方应该如何选择行权&#xff1f; 卖出行权价800的看涨期权&#xff0c;期权报价37.9&#xff0c;一手权利…

【环境配置】科研小白Windows下安装Git

2024年小白使用Win10安装Git 2.46.2教程&#xff1a; 1 下载安装包 访问下载地址 Git - Downloading Package (git-scm.com) 下载之后打开文件 2 安装过程 点击Next 2.1 选择安装路径 2.2 选择勾选必要组件 2.3 一路Next 这一步直接Next即可 继续点击Next 继续点击Ne…

Linux学习之路 -- 线程 -- 线程池

前面介绍了条件变量的生产消费模型&#xff0c;下面介绍一下条件变量的另一个用法&#xff0c;那就是线程池。线程池的用法其实就是先创建一批线程&#xff0c;然后让这些线程从任务队列中取数据。具体就是生产消费者模型&#xff0c;(我的代码中生产线程只有一个并且生产的任务…

自动微分-梯度!

前言背景知识&#xff1a; 梯度下降(Gradient descent,GD) 正文&#xff1a; 自动微分为机器学习、深度学习神经网络的核心知识之一&#xff0c;若想更深一步使用神经网络进行具体问题研究&#xff0c;那么自动微分不得不了解。 “工欲善其事&#xff0c;必先利其器”&…

数据结构 ——— 单链表oj题:合并两个升序链表

目录 题目要求 手搓两个简易链表 代码实现 题目要求 将两个升序链表合并为一个新的升序链表并返回&#xff0c;新链表是通过拼接给定的两个链表的所有节点组成的 手搓两个简易链表 代码演示&#xff1a; struct ListNode* n1 (struct ListNode*)malloc(sizeof(struct …

【Linux】第一个小程序——进度条实现

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…

【Python报错已解决】TypeError: ‘NoneType‘ object is not iterable

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

Android KMP 快速入门1 - 项目打包

这里写目录标题 KMP 运行与打包运行程序程序打包 KMP 运行与打包 运行程序 运行Android客户端&#xff0c;你首先需要把USB连接到物理机上&#xff0c;或者使用模拟器模拟一个手机&#xff1b; 然后选择运行配置的 composeApp &#xff0c;运行它即可 运行windows客户端&…

Qt/C++开源控件 自定义雷达控件

使用Qt框架创建一个简单的雷达图&#xff0c;包含动态扫描、目标点生成、刻度和方向标识。代码实现使用C编写&#xff0c;适合用作学习和扩展的基础。 1. 头文件与基本设置 #include "RadarWidget.h" #include <QPainter> #include <QPen> #include &…

解决银河麒麟操作系统V10软件包架构不符问题

TOC &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在银河麒麟桌面操作系统V10中安装软件包时&#xff0c;如果遇到“软件架构与本机架构不符”的提示&#xff0c;可以尝试以下步骤来解决问题&#xff1a; 1. 确认架构一致性 查看本机架构…

基于STM32的智能门禁系统

目录 引言项目背景环境准备 硬件准备软件安装与配置系统设计 系统架构关键技术代码示例 密码验证模块电机控制实现门禁状态监控应用场景结论 1. 引言 智能门禁系统能够通过密码或其他验证方式&#xff08;如指纹、刷卡等&#xff09;控制门的开关&#xff0c;结合电机控制与…

2024年7月大众点评武汉餐饮美食店铺基础信息

在做一些城市分析、学术研究分析、商业选址、商业布局分析等数据分析挖掘时&#xff0c;大众点评的数据参考价值非常大&#xff0c;截至2024年7月&#xff0c;大众点评美食店铺剔除了暂停营业、停止营业后的最新数据情况分析如下。 武汉餐饮美食店铺约9.6万家&#xff0c;有均…

MySQL高阶2051-商店中每个成员的级别

目录 题目 准备数据 分析数据 实现 总结 题目 一个商店想对其成员进行分类。有三个层次: "钻石": 如果转换率 大于或等于 80."黄金": 如果转换率 大于或等于 50 且小于 80."白银": 如果转化率 小于 50."青铜": 如果该成员从未访…

澳洲本科毕业论文的初稿撰写要点分析

临近毕业季的时候&#xff0c;如何更好地完成澳洲本科毕业论文成为了困扰大家的一大难题。澳洲毕业论文的质量高低关系到留学生能否顺利毕业。因此大家都会关心如何更好地完成毕业论文。我们在之前一些文章中介绍了如何确立论点&#xff0c;如何查找资料以及如何完成高质量的di…

HarmonyOS/OpenHarmony 如何将rawfile中文件复制到沙箱中

关键词&#xff1a;h5离线加载、HarmonyOS、OpenHarmony、文件操作、复制、解压 当下有一个场景&#xff0c;需要离线加载 h5离线资源zip包&#xff0c;并实现资源包的动态更新&#xff0c;那么仅靠 $rawfile并不能实现该功能&#xff0c;那么我们该如何实现&#xff1f; 我们…

面试题05.08绘制直线问题详解(考察点为位运算符)

目录 一题目&#xff1a; 二详细思路汇总&#xff1a; 三代码解答&#xff08;带注释版&#xff09;&#xff1a; 一题目&#xff1a; leetcode原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 二详细思路汇总&#xff1a; 这里先剧透一下简单版思路哦&…

Azure DevOps Server:不能指派新增的用户

Contents 1. 概述2. 解决方案 1. 概述 近期和微软Azure DevOps项目组解决了一个“无法指派开发人员”的问题&#xff0c;在此分享给大家。问题描述&#xff1a; 在一个数据量比较大的Azure DevOps Server的部署环境中&#xff0c;用户发现将新用户的AD域账户添加到Azure DevOps…

睢宁自闭症寄宿学校:培养特殊孩子的未来

在自闭症儿童的教育与康复领域&#xff0c;每一所学校的努力都是对孩子们未来无限可能的一次深刻诠释。从江苏睢宁到广东广州&#xff0c;自闭症寄宿学校正以不同的方式&#xff0c;为这些特殊的孩子铺设一条通往未来的希望之路。其中&#xff0c;广州的星贝育园自闭症儿童寄宿…

数据分析-29-基于pandas的窗口操作和对JSON格式数据的处理

文章目录 1 窗口操作1.1 滑动窗口思想1.2 函数df.rolling2 JSON格式数据2.1 处理简单JSON对象和JSON列表2.1.1 处理简单的JSON结构2.1.2 处理空字段2.1.3 获取部分字段2.2 处理多级json2.2.1 展开所有级别(默认)2.2.2 自定义展开层级2.3 处理嵌套列表JSON3 参考附录1 窗口操作 …