【Linux进程通信】二、深度解析匿名管道
时间:2025-04-20 17:13:56 491浏览 收藏
本文详细介绍了Linux进程间通信中的匿名管道。首先阐述了管道的概念,即基于文件系统、单向连接的数据流,其本质是内核缓冲区,并非磁盘文件,从而保证高效的数据传输。随后,文章深入探讨了匿名管道的原理和创建方法,重点讲解了利用`pipe()`系统调用创建匿名管道,以及父子进程通过继承文件描述符实现共享同一缓冲区的方法,并强调了关闭相应读写端以确保单向通信的必要性。最后,文章简述了匿名管道的读写特征,为读者理解Linux进程间通信提供了清晰的思路。
Ⅰ. 管道一、管道的概念 管道是 Unix
中最古老的进程间基于文件系统通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个 “管道”。注意管道是单向连通的,不存在说双向管道,就像生活中水往低处流而不会往高处流一样!

进程 A
通过管道将数据写入到 “公共内存” 中,并且进程 B
可以从该段 “公共内存” 中读取这些数据,这样子的话就达到了两个进程之间的交互!
那么有人可能会有问题:既然这段 “公共内存” 是共享的并且都是基于文件系统的,那这个管道文件是不是在磁盘上面呢,然后进程A通过写入到磁盘中的管道文件,进程B再去读取这样子的方式 ❓❓❓
其实不是的,因为我们都知道,文件 IO
的效率是相当的低的,所以操作系统肯定不笨,操作系统会将这个管道文件 load
到内存中,也就是内存级别的文件,而我们两个进程之间只需要和这个内存级文件打交道即可,这样子大大的提高了效率!
任何一个文件包括两套资源:
struct file
的操作方法有属于自己的内核缓冲区,所以父进程和子进程有一份公共的资源:文件系统提供的内核缓冲区,父进程可以向对应的文件的文件缓冲区写入,子进程可以通过文件缓冲区读取,此时就完成了进程间通信,这种方式提供的文件称为管道文件。管道文件本质就是内存级文件,不需要 IO
。 管道的本质是内核中的缓冲区,通过内核缓冲区实现通信,管道文件只是一个标识符,用于让多个进程能够访问同一块缓冲区,并非通信介质。
并且通过这个内存级别文件的不同形式,我们分为匿名管道和命名管道,后面我们来一一介绍!
二、管道通信原理 通过我们上面所说的,管道是个 内存级别的文件(struct file
中特有的,就像内核缓冲区一样),我们要知道为什么它会叫做 “管道” 呢,是因为它一开始就叫做 “管道” 吗,那肯定不是,是因为它的原理和 “管道” 是类似的,人们后期才会将其命名为 “管道”,而管道是单通向的,不存在双向管道!
并且我们生活中常见的管道比如说水管、油等等它们的作用就是来传输水和油,那么我们计算机中的管道就是传输数据,那么这个流向肯定要是一条单向的,我们就不能想象出其原理结构:

除此之外,我们可以再说说普通文件,我们父进程创建子进程,子进程拷贝父进程的文件描述符表,所以都指向 log
文件,而每次我们写入完毕之后,可能会存在写满等情况就会刷新,那么会访问磁盘,每次读取时候又将 log
加载进内存,不断的来回,这样子 IO
次数非常的多,效率也就非常的低,所以说为什么存在管道文件,其实就是为了避开普通文件通信的效率底下问题!

Ⅱ. 匿名管道一、匿名管道的原理与创建方法 在讲匿名管道之前呢,我们必须知道管道它的通信方法,而对于匿名管道来说,其实就是 通过 fork
创建子进程!(而命名管道的方法不需要创建子进程,后面会讲)
为什么对于匿名管道来说需要创建子进程呢 ❓❓❓
匿名管道的名称由来也是因为这个原因,我们需要通过 fork
创建子进程,我们都知道,子进程会继承父进程的大部分属性和内容包括文件描述符(若发生写时拷贝则会改变),也就是说父进程指向的文件 file
,子进程也会指向同一个文件 file
(父子进程文件描述符表是独立的,但是指向的文件是同一个),而就像我们下图,父子进程可以都指向该管道文件,这样子的话我们就无需说让子进程和父进程去专门创建一个带名称的管道并且指向它,也就是说我们可以 利用父子进程的继承性让子进程继承这个管道文件达到共同指向它的目的,所以这个管道文件没必要带名称,所以叫做匿名管道!
所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了 “Linux
一切皆文件思想“!

那么我们如何创建这个匿名管道文件呢 ❓❓❓
下面就得调用我们的系统函数 pipe()
:
#include#include #include #include #include #include #include #include #include #include using namespace std;#define MakeSeed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x171237 ^ rand() % 1234)///中间部分为任务功能部分/typedef void(* func_t)(); // 函数指针void IOTask(){ cout * funcMap){ assert(funcMap); funcMap->push_back(IOTask); funcMap->push_back(DownloadTask); funcMap->push_back(flushTask);}///以下为管道池的管理const int PROCESS_NUM = 5; // 子进程池的个数// 描述每对子进程和管道文件的结构体class childProcess{public: childProcess(pid_t pid, int writeFd) :_pid(pid), _writeFd(writeFd) { char buffer[1024]; snprintf(buffer, sizeof(buffer), "process-%d[pid(%d)|writeFd(%d)]", num++, _pid, _writeFd); _name = buffer; }public: string _name; // 以统一的规则命名的名称 pid_t _pid; // 子进程pid int _writeFd; // 管道文件的写入端 static int num; // 当前子进程为第几个进程编号};int childProcess::num = 0;void SendTask(const childProcess& cp, int index_func){ cout " * pipePool, vector & funcMap){ vector deleteFd; // 记录每次要关闭的前面的子进程的写端 for(int i = 0; i = 0); // 断言一下是否fork成功 // 子进程的执行部分 if(id == 0) { // 每次删掉子进程拷贝父进程的前n个写入端指向 for(int i = 0; i = 0 && commandCode push_back(move(cp)); // 将该对象调用move移动构造到管道池 deleteFd.push_back(pipefd[1]); // 记录每个子进程的前n个写入端fd }}void loadBlanceContrl(vector & cp, vector & fmp, int taskCnt){ int pipe_size = cp.size(); // 管道池个数 int func_size = fmp.size(); // 任务个数 bool forever = (taskCnt == 0 ? true : false); // 判断是否为永远 while(true) { // 1、选择一个子进程 int index_process = rand() % pipe_size; // 2、选择一个任务 int index_func = rand() % func_size; // 3、任务发送给选择的进程 SendTask(cp[index_process], index_func); sleep(1); if(!forever) { taskCnt--; if(taskCnt == 0) break; } } // 关闭写入端 for(int i = 0; i & cp){ for(int i = 0; i " funcMap; LoadFunction(&funcMap); vector pipePool; CreateProcessPool(&pipePool, funcMap); // 2、父进程控制子进程完成任务,负载均衡的向子进程发送命令码,若父进程退出则关闭子进程 int taskCnt = 3; // 0: 永远进行,其它则表示计数 loadBlanceContrl(pipePool, funcMap, taskCnt); // 3、回收子进程信息 waitProcess(pipePool); return 0;}
今天关于《【Linux进程通信】二、深度解析匿名管道》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
452 收藏
-
278 收藏
-
376 收藏
-
115 收藏
-
160 收藏
-
455 收藏
-
111 收藏
-
251 收藏
-
449 收藏
-
470 收藏
-
448 收藏
-
427 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习