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

📝个人主页🌹: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 1024using 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.rhkb.cn/news/438104.html

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

相关文章

unity 默认渲染管线材质球的材质通道,材质球的材质通道

标准渲染管线——材质球的材质通道 文档&#xff0c;与内容无关&#xff0c;是介绍材质球的属性的。 https://docs.unity3d.com/2022.1/Documentation/Manual/StandardShaderMaterialParameters.html游戏资源中常见的贴图类型 https://zhuanlan.zhihu.com/p/260973533 十大贴图…

最新版ChatGPT对话系统源码 Chat Nio系统源码

介绍&#xff1a; 最新版ChatGPT对话系统源码 Chat Nio系统源码 支持 Vision 模型, 同时支持 直接上传图片 和 输入图片直链或 Base64 图片 功能 (如 GPT-4 Vision Preview, Gemini Pro Vision 等模型) 支持 DALL-E 模型绘图 支持 Midjourney / Niji 模型的 Imagine / Upsc…

OpenSource - 开源WAF_SamWaf

文章目录 PreSafeLine VS SamWaf开发初衷软件介绍架构界面主要功能 使用说明下载最新版本快速启动WindowsLinuxDocker 启动访问升级指南自动升级手动升级 在线文档 代码相关代码托管介绍和编译已测试支持的平台测试效果 安全策略问题反馈许可证书贡献代码 Pre Nginx - 集成Mod…

单调队列应用介绍

单调队列应用介绍 定义应用场景实现模板具体示例滑动窗口最大值问题描述问题分析代码实现带限制的子序列和问题描述问题分析代码实现跳跃游戏问题描述问题分析代码实现定义 队列(Queue)是另一种操作受限的线性表,只允许元素从队列的一端进,另一端出,具有先进先出(FIFO)的特…

关于HTML 案例_个人简历展示01

案例效果展示 代码 <!DOCTYPE html> <lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>个人简历信息</title> </he…

MySQL 中的 LAST_INSERT_ID()函数详解

在 MySQL 数据库中&#xff0c;LAST_INSERT_ID()是一个非常有用的函数。它可以帮助我们获取最近一次插入操作所生成的自增 ID 值。本文将详细解释 MySQL 中的LAST_INSERT_ID()函数及其用途。 一、函数介绍 LAST_INSERT_ID()是 MySQL 中的一个内置函数&#xff0c;它返回最近一…

通过栈实现字符串中查找是否有指定字符串的存在

题目示例&#xff1a; 分析 由与没有给出字符串的长度&#xff0c;所以只能通过getline一次性处理&#xff0c;而在输入后恰好能倒序处理字符串&#xff0c;以标点符号为分界点&#xff0c;将数字当成字符放到栈里&#xff0c;遇到下一个标点符号时执行查找操作&#xff0c;…

关于Mybatis框架操作时注意的细节,常见的错误!(博主亲生体会的细节!)

目录 1.在对DB进行CRUD时&#xff0c;除了查&#xff0c;其余的操作都要进行事务的提交否则不成功。 2.用sqlSession原生方法时&#xff0c;第一个参数方法名&#xff0c;是xml文件中定义的id名&#xff0c;底层找的是你这个接口所定义的方法名。 3.以包为单位引入映射文件 …

Vue项目开发注意事项

事项一&#xff1a;项目代码放在本地怎么运行起来 1、首先确定项目对应的node和npm版本 node下载地址 Index of /dist/https://nodejs.org/dist/ node 与 npm版本对应关系 Node.js — Node.js Releases 2、node卸载的时候&#xff0c;会自动把对应的npm卸载掉 情况1&…

无环SLAM系统集成后端回环检测模块(loop):SC-A-LOAM以及FAST_LIO_SLAM

最近在研究SLAM目标检测相关知识&#xff0c;看到一篇论文&#xff0c;集成了SC-A-LOAM作为后端回环检测模块&#xff0c;在学习了论文相关内容后决定看一下代码知识&#xff0c;随后将其移植&#xff0c;学习过程中发现我找的论文已经集成了回环检测模块&#xff0c;但是我的另…

【重学 MySQL】四十九、阿里 MySQL 命名规范及 MySQL8 DDL 的原子化

【重学 MySQL】四十九、阿里 MySQL 命名规范及 MySQL8 DDL 的原子化 阿里 MySQL 命名规范MySQL8 DDL的原子化 阿里 MySQL 命名规范 【强制】表名、字段名必须使用小写字母或数字&#xff0c;禁止出现数字开头&#xff0c;禁止两个下划线中间只出现数字。数据库字段名的修改代价…

Java 计算器项目

更多有趣请关注公众号 计算器项目 代码仓库&#xff1a;https://gitee.com/wengxiulin/vs_code 项目图片 项目简介 这是一个用 Java 编写的简单计算器应用程序&#xff0c;具有基本的数学运算功能。该计算器支持加、减、乘、除等运算&#xff0c;并提供用户友好的图形界面…

【STM32】TCP/IP通信协议(2)--LwIP内存管理

五、LWIP内存管理 1.什么是内存管理&#xff1f; &#xff08;1&#xff09;内存管理&#xff0c;是指软件运行时对计算机内存资源的分配的使用的技术&#xff0c;其主要目的是如何高效、快速的分配&#xff0c;并且在适当的时候释放和回收内存资源&#xff08;就比如C语言当…

使用微服务Spring Cloud集成Kafka实现异步通信(消费者)

1、本文架构 本文目标是使用微服务Spring Cloud集成Kafka实现异步通信。其中Kafka Server部署在Ubuntu虚拟机上&#xff0c;微服务部署在Windows 11系统上&#xff0c;Kafka Producer微服务和Kafka Consumer微服务分别注册到Eureka注册中心。Kafka Producer和Kafka Consumer之…

Mybatis框架梳理

Mybatis框架梳理 前言1.ORM2.模块划分2.1 ORM的实现2.2 SQL的映射2.3 插件机制2.4 缓存机制2.5 其他 3. 愿景 前言 如果让我聊一聊mybatis&#xff0c;我该怎么说呢&#xff1f;开发中时时刻刻都在用它&#xff0c;此时此刻&#xff0c;脑海中却只浮现ORM框架这几个字&#xff…

[每周一更]-(第117期):硬盘分区表类型:MBR和GPT区别

文章目录 1. **支持的磁盘容量**2. **分区数量**3. **引导方式**4. **冗余和数据恢复**5. **兼容性**6. **安全性**7. **操作系统支持**8. 对比 国庆假期前补一篇 在一次扫描机械硬盘故障的问题&#xff0c;发现我本机SSD和机械硬盘的分类型不一样&#xff0c;分别是GPT和MBR&a…

Vue3轻松实现前端打印功能

文章目录 1.前言2.安装配置2.1 下载安装2.2 main.js 全局配置3.综合案例3.1 设置打印区域3.2 绑定打印事件3.3 完整代码4.避坑4.1 打印表格无边框4.2 单选框复选框打印不选中4.3 去除页脚页眉4.4 打印內容不自动换行1.前言 vue3 前端打印功能主要通过插件来实现。 市面上常用的…

C语言 | Leetcode C语言题解之第450题删除二叉搜索树中的节点

题目&#xff1a; 题解&#xff1a; struct TreeNode* deleteNode(struct TreeNode* root, int key){struct TreeNode *cur root, *curParent NULL;while (cur && cur->val ! key) {curParent cur;if (cur->val > key) {cur cur->left;} else {cur c…

telnet发送邮件教程:安全配置与操作指南?

telnet发送邮件的详细步骤&#xff1f;怎么用telnet命令发邮件&#xff1f; 尽管现代邮件客户端和服务器提供了丰富的功能和安全性保障&#xff0c;但在某些特定场景下&#xff0c;了解如何使用telnet发送邮件仍然是一项有价值的技能。AokSend将详细介绍如何安全配置和操作tel…

github/git密钥配置与使用

零、前言 因为要在ubuntu上做点东西&#xff0c;发现git clone 的时候必须输账户密码&#xff0c;后来发现密码是token&#xff0c;但是token一大串太烦了&#xff0c;忙了一天发现可以通过配置 公钥 来 替代 http 的 部署方式。 一、生成 ssh 密钥对 我们先测试下能不能 连接…