【Linux操作系统】进程间通信之匿名管道与命名管道
目录
- 一、进程间通信的目的:
- 二、进程间通信的种类
- 三、什么是管道
- 四、匿名管道(共同祖先的进程之间)
- 1.匿名管道的使用
- 2.匿名管道举例
- 3.匿名管道的原理
- 4.管道特点
- 5.管道的读写规则
- 1. 当管道内没有数据可读时
- 2.当管道满的时候
- 3.管道端被关闭
- 4.数据写入的原子性
- 五、命名管道(让两个不相干的进程通信)
- 1.命名管道的概念与原理
- 2.命名管道的创建与删除
- 3.匿名管道与命名管道的区别
- 4.命名管道的打开规则
- 5.命名管道举例(附完整代码)
- 1.makefile文件
- 2.wFifo.cpp
- 3.rFifo.cpp
- 六、关于匿名管道与命名管道的细节问题
- 匿名管道
- 命名管道
一、进程间通信的目的:
在学习通信间进程前我们要明白进程间通信的目的意义是是什么,从而方便我们在学习的过程中对我们所学的知识有一个清晰的认知
1.传输数据
将一个进程的数据发送给另一个进程
2.资源共享
让多个进程共享所需要的资源
3.通知事件
一个进程向另一个进程发送消息,通知它发生了某种事件(如进程终止时要通知父进程)
4.进程控制
有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
二、进程间通信的种类
1.管道
- 匿名管道
- 命名管道
2.System V
- System V 消息队列
- System V 共享内存
- System V 信号量
3.POSIX
- 消息队列
- 共享内存
- 信号量
- 互斥锁
- 条件变量
- 读写锁
在本章中主要讲解管道通信
三、什么是管道
- 管道是操作系统中最古老的进程间通信的方式
- 我们把一个进程连接到另一个进程的一个数据流称为一个“管道”

四、匿名管道(共同祖先的进程之间)
1.匿名管道的使用
#include <unistd.h>
int pipe(int fd[2]);
匿名管道的功能是创建一个无名管道
- fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
- 返回值:成功返回0,失败返回错误代码

如果上述看的不太明白的话可以去看看这篇文章,里面有对文件描述符及数组的详细讲解
https://blog.csdn.net/liuty0125/article/details/140828056?spm=1001.2014.3001.5502
2.匿名管道举例
1.先创建管道, 进而创建子进程, 父子进程使用管道进行通信
父进程向管道当中写“i am father”,
子进程从管道当中读出内容, 并且打印到标准输出
#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
using namespace std;
int main()
{int fd[2];int f=pipe(fd);char buf[100];const char* fa="I am father";int len=strlen(fa);if(f==-1){perror("make pipe");exit(1);}int pid = fork();if(pid==-1){perror("fork failed");return 1;}if(pid>0)//父进程{write(fd[1],fa,len);}if(pid==0){read(fd[0],buf,len);printf("%s",buf);}return 0;
}
2.从键盘读取数据,写入管道,读取管道,写到屏幕
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include<sys/types.h>
#include<string>
int main(void)
{int fds[2];char buf[100];int len;if (pipe(fds) == -1)perror("make pipe"), exit(1);// read from stdinwhile (fgets(buf, 100, stdin)) {len = strlen(buf);// write into pipeif (write(fds[1], buf, len) != len) {perror("write to pipe");break;}memset(buf, 0x00, sizeof(buf));// read from pipeif ((len = read(fds[0], buf, 100)) == -1) {perror("read from pipe");break;}// write to stdoutif (write(1, buf, len) != len) {perror("write to stdout");break;}}
}
3.匿名管道的原理
我们知道,进程将一个文件以读方式打开一次会产生一个FILE*,再以写方式打开一次,也会产生一个FILE*,但是这两个文件流指针指向的是同一个缓存区
内容只有一份基于文件的,让不同进程看到同一份资源的通信方式,叫做管道,管道只能被设计成为单向通信
如:ajx | ps | … 这中间的管道是兄弟关系
我们可以让父子进程都打开这个文件,再让父进程关闭读端,子进程关闭写端,这样就形成了一条由父进程通向子进程的管道,反之则形成由子进程通向父进程的管道

在这个地方其实有个bug:
通过父进程创建多个子进程,用管道文件连接父进程与各个子进程,产生的管道文件有多个读端,因为每个子进程的文件描述符表都会和创建他的父进程的文件描述符表一样,父进程的读端没有关闭,所以与接连创建的各个子进程的管道会带有上个父进程的读端
解决方案1:最后一个管道一定只有一个读写端,可以从后往前删
解决方案2:每次创建管道文件时,记录父进程的fd,然后每次创建时通过记录的上个父进程的文件描述符关闭不需要的fd
4.管道特点
- 匿名管道只能用于共同祖先的进程之间进行通信,一般都是一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间就可以应用改管道
- 一般而言,匿名管道的生命周期随进程,进程退出,管道释放
- 管道是半双工的,数据只能向一个方向流动,需要双方通信时,则需要建立起两个管道

5.管道的读写规则
管道读写操作的行为会根据是否设置了非阻塞标志(O_NONBLOCK)以及管道的状态(是否有数据可读、是否已满等)有所不同
1. 当管道内没有数据可读时
- O_NONBLOCK 未设置(阻塞模式):
read调用阻塞,即进程暂停执行,一直等到有数据来到为止
- O_NONBLOCK 已设置(非阻塞模式):
read调用返回-1,errno值为EAGAIN
2.当管道满的时候
- O_NONBLOCK 未设置(阻塞模式):
write调用阻塞,直到有进程读走数据
- O_NONBLOCK 已设置(非阻塞模式):
调用返回-1,errno值为EAGAIN
3.管道端被关闭
- 所有管道的写端对应的文件描述符被关闭:
read 调用会返回 0,表示已经到达文件末尾(EOF),没有更多的数据可以读取
- 所有管道的读端对应的文件描述符被关闭:
write 调用会产生 SIGPIPE 信号,默认情况下,这会导致进程终止。可以通过捕获 SIGPIPE 信号来避免进程终止
4.数据写入的原子性
- 当要写入的数据量不大于 PIPE_BUF 时:
Linux 保证写入的原子性,即要么全部写入成功,要么完全不写入。这有助于避免数据在管道中被分割成多个部分,从而保证了数据的完整性。
- 当要写入的数据量大于 PIPE_BUF 时:
Linux 不再保证写入的原子性,数据可能会被分割成多个部分写入管道。这可能导致读端在读取数据时,需要多次调用 read 才能获取完整的数据。
五、命名管道(让两个不相干的进程通信)
1.命名管道的概念与原理
学习了上面的匿名管道,我们知道匿名管道其实是有缺陷的,它只能在具有共同祖先(具有亲缘关系)的进程间通信,那如果我们想在不相关的进程之间交换数据呢?
想要在两个不相干进程之间进行通信,我们的目的是让不同进程看到同一份资源
比如进程A通过文件描述符表打开一个文件,会为文件创建一个struct FILE,并通过文件缓冲区与磁盘进行交互
进程B也通过文件描述符打开同一个文件,也会为文件再创建一个struct FILE,但会使用同一个缓冲区,指向同一个内存空间,因此我们就可以通过这段内存空间进行通信
不过我们只是需要两个进程之间进行通信,并不需要把打开的文件内容写到缓冲区中,因此这里的文件是一个特殊的文件,也就是我们下面说的命名管道
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道
- 怎么保证两个进程打开的是同一个文件?
只需要保证文件的路径与文件名一样即可,因为路径具有唯一性且同一路径下不能有同名文件
2.命名管道的创建与删除
1.在命令行上创建
- 使用
mkfifofilename

p开头为管道文件
2.在程序中创建
int mkfifo(const char *filename,mode_t mode);
int main(int argc, char *argv[])
{mkfifo("p2", 0644);return 0;
}
3.删除文件
使用unlink()删除文件,可以用于析构函数
unlink(filename);
也就是说,想让两个不相干的进程通信,只需通过mkfifo创建一个管道文件,一个进程写,一个进程读即可
在程序中,管道文件只能创建一个,一个进程创建后,另一个进程只需直接操作就行,不需要再创建管道文件
管道中的通信都是基于文件的,可以说管道通信是基于文件的二次创新
如果写端没打开,而去先打开读端,读端open时会阻塞,直到把写端打开,读端才会返回
3.匿名管道与命名管道的区别
- 匿名管道由pipe函数创建并打开
- 命名管道由mkfifo函数创建,打开用open
- FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义
4.命名管道的打开规则
- 为读而打开FIFO
O_NONBLOCK 未设置(阻塞模式):
如果当前没有进程为写而打开该FIFO,则打开操作会阻塞,直到有进程以写模式打开它。这意味着,如果FIFO是空的,并且没有写端打开,那么尝试以读模式打开FIFO的进程将被挂起,直到另一个进程以写模式打开它
O_NONBLOCK 已设置(非阻塞模式):
如果当前没有进程为写而打开该FIFO,则打开操作会立即成功,但是后续的read操作可能会失败,并返回-1,同时设置errno为EAGAIN(表示资源暂时不可用,可以在以后重试)。
- 为写而打开FIFO
O_NONBLOCK 未设置(阻塞模式):
如果当前没有进程为读而打开该FIFO,则打开操作会阻塞,直到有进程以读模式打开它。这意味着,如果FIFO没有读端,那么尝试以写模式打开FIFO的进程将被挂起,直到另一个进程以读模式打开它
O_NONBLOCK 已设置(非阻塞模式):
如果当前没有进程为读而打开该FIFO,则打开操作会立即失败,并返回-1,同时设置errno为ENXIO(表示设备不存在或没有这样的设备文件)。
在非阻塞模式下,如果没有读端可用,写端不能立即打开FIFO。
5.命名管道举例(附完整代码)
创建出来的命名管道可以供两个进程通信
进程A 向管道当中写 “i am process A”
进程B 从管道当中读 并且打印到标准输出
1.makefile文件
.PHONY:all
all:w rw:wFifo.cppg++ -o $@ $^ -std=c++11r:rFifo.cppg++ -o $@ $^ -std=c++11
PHONY:clean
clean:rm -f r w
2.wFifo.cpp
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string>
#include <string.h>
#include <errno.h>
#include <strings.h>
#include <stdio.h>
#include<unistd.h>void errS(const char* s)
{perror(s);exit(EXIT_FAILURE);
}int main()
{const char* s="/home/xiaoliu/code/Fifo/__fifo";char a[1024]="i am process A";mkfifo(s,0666);int wfd=open(s,O_WRONLY);if(wfd < 0){errS("open");}int w=write(wfd,a,sizeof(a-1));if(w<0){errS("write");}return 0;
}
3.rFifo.cpp
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string>
#include <string.h>
#include <errno.h>
#include <strings.h>
#include <stdio.h>
#include<unistd.h>
#include <iostream>void errS(const char* s)
{perror(s);exit(EXIT_FAILURE);
}int main()
{const char* s="/home/xiaoliu/code/Fifo/__fifo";char buf[1024];int rfd=open(s,O_RDONLY);if(rfd<0){errS("open");}int n = read(rfd,buf,sizeof(buf)-1);if(n>0){buf[n]='\0';std::cout<<"A said: "<<buf;}else if(n==0){std::cout<<"A didn't speak";}else{std::cerr << "read failed, errno: " << errno << ", errstring: " << strerror(errno) << std::endl;}return 0;
}
六、关于匿名管道与命名管道的细节问题
匿名管道
- 进程之间可以通过地址访问进行相互通行吗?
错误,进程之间具有独立性,拥有自己的虚拟地址空间,因此无法通过各自的虚拟地址进行通信(A的地址经过B的页表映射不一定映射在什么位置)
- 所有的进程间通信都是通过内核中的缓冲区实现的吗?
错误,除了内核中的缓冲区之外还有文件以及网络通信的方式可以实现
- 管道的容量仅受磁盘容量大小限制吗?
错误,管道的本质是内核中的缓冲区,通过内核缓冲区实现通信,命名管道的文件虽然可见于文件系统,但是只是标识符,并非通信介质
- 一个管道只能有一个读进程或写进程对其操作吗?
错误,多个进程只要能够访问同一管道就可以实现通信,不限于读写个数
- 程对管道进行读操作和写操作都可能被阻塞吗?
正确,管道自带同步(没有数据读阻塞,缓冲区写满写阻塞)与互斥
- 管道的本质是内核中的一块缓冲区?
正确,管道本质是内核中的一块缓冲区,多个进程通过访问同一块缓冲区实现通信
命名管道
- .命名管道可以用于同一主机上的任意进程间通信?
正确,命名管道可用于同一主机上的任意进程间通信
- 向命名管道中写入的数据越多,则管道文件越大?
错误,管道的通信本质是通过内核中一块缓冲区(内存)时间数据传输,而命名管道的管道文件只是一个标识符,用于让多个进程能够访问同一块缓冲区,不是磁盘文件
- 多个进程在通过管道通信时,删除管道文件则无法继续通信?
错误,管道的生命周期随进程,本质是内核中的缓冲区,命名管道文件只是标识,用于让多个进程找到同一块缓冲区,删除后,之前已经打开管道的进程依然可以通信
- 命名管道的本质和匿名管道的本质相同都是内核中的一块缓冲区?
正确
- .命名管道在磁盘空间足够的情况下可以持续写入数据?
错误,管道在缓冲区写满后会写阻塞,跟磁盘空间并无关系
- 若以只读或只写的方式打开命名管道时,则会阻塞?
正确,当一个进程尝试以只读方式打开命名管道时,如果该管道尚未被任何进程以写方式打开,则打开操作会阻塞,阻塞将持续到另一个进程以写方式打开该管道为止。此时,读进程可以继续进行,并尝试从管道中读取数据。反之亦然
相关文章:
【Linux操作系统】进程间通信之匿名管道与命名管道
目录 一、进程间通信的目的:二、进程间通信的种类三、什么是管道四、匿名管道(共同祖先的进程之间)1.匿名管道的使用2.匿名管道举例3.匿名管道的原理4.管道特点5.管道的读写规则1. 当管道内没有数据可读时2.当管道满的时候3.管道端被关闭4.数…...
慢sql优化和Explain解析
要想程序跑的快,sql优化不可懈怠!今日来总结一下常用的慢sql的分析和优化的方法。 1、慢sql的执行分析: 大家都知道分析一个sql语句执行效率的方法是用explain关键词: 举例:sql:select * from test where bussiness_…...
ALIGN_ Tuning Multi-mode Token-level Prompt Alignment across Modalities
文章汇总 当前的问题 目前的工作集中于单模提示发现,即一种模态只有一个提示,这可能不足以代表一个类[17]。这个问题在多模态提示学习中更为严重,因为视觉和文本概念及其对齐都需要推断。此外,仅用全局特征来表示图像和标记是不…...
【Java SE】代码注释
代码注释 注释(comment)是用于说明解释程序的文字,注释的作用在于提高代码的阅读性(可读性)。Java中的注释类型包括3种,分别是: 单行注释多行注释文档注释 ❤️ 单行注释 基本格式ÿ…...
如何在算家云搭建Llama3-Factory(智能对话)
一、Llama3-Factory 简介 当地时间 4 月 18 日,Meta 在官网上宣布公布了旗下最新大模型 Llama 3。目前,Llama 3 已经开放了 80 亿(8B)和 700 亿(70B)两个小参数版本,上下文窗口为 8k。Llama3 是…...
操作数据表
创建表 创建表语法: CREATE TABLE table_name ( field1 datatype [COMMENT 注释内容], field2 datatype [COMMENT 注释内容], field3 datatype ); 注意: 1. 蓝色字体为关键字 2. CREATE TABLE 是创建数据表的固定关键字,表…...
C# 实现进程间通信的几种方式(完善)
目录 引言 一、基本概念 二、常见的IPC方法 1. 管道(Pipes) 2. 共享内存(Shared Memory) 3. 消息队列(Message Queues) 4. 套接字(Sockets) 5. 信号量(Semaphore…...
MySQL Workbench Data Import Wizard:list index out of range
MySQL Workbench的Data Import Wizard功能是用python实现的,MySQL Workbench自带了一个python,数据导入的时候出现错误提示 22:55:51 [ERR][ pymforms]: Unhandled exception in Python code: Traceback (most recent call last): File "D…...
微信支付宝小程序SEO优化的四大策略
在竞争激烈的小程序市场中,高搜索排名意味着更多的曝光机会和潜在用户。SEO即搜索引擎优化,对于小程序而言,主要指的是在微信小程序商店中提高搜索排名,从而增加曝光度和用户访问量。有助于小程序脱颖而出,提升品牌知名…...
AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion论文阅读笔记
AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion 论文阅读笔记 这是ECCV2024的论文,作者单位是是港中文和上海AI Lab 文章提出了一个叫AutoDIR的方法,包括两个关键阶段,一个是BIQA,基于vision-language…...
SQLite 数据库设计最佳实践
SQLite特点 SQLite是一款功能强大的 轻量级嵌入式数据库 ,具有以下显著特点: 体积小 :最低配置仅需几百KB内存,适用于资源受限环境。 高性能 :访问速度快,运行效率高于许多开源数据库。 高度可移植 :兼容多种硬件和软件平台。 零配置 :无需复杂设置,开箱即用。 自给自…...
【论文精读】ID-like Prompt Learning for Few-Shot Out-of-Distribution Detection
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀论文精读_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 注:下文…...
Android 10.0 根据包名禁用某个app的home事件
1.前言 在10.0的系统rom定制化开发中,在某些app中,需要禁用home事件,在普通的app中又无法 禁用home事件,所以就需要从系统中来根据包名禁用home事件了,接下来分析下 系统中处理home事件的相关流程 2.根据包名禁用某个app的home事件的核心类 frameworks/base/services/c…...
Rust 文档生成与发布
目录 第三节 文档生成与发布 1. 使用 RustDoc 生成项目文档 1.1 RustDoc 的基本使用 1.2 文档注释的格式与实践 1.3 生成文档的其他选项 1.4 在 CI/CD 中生成文档 2. 发布到 crates.io 的步骤与注意事项 2.1 创建 crates.io 账户 2.2 配置 Cargo.toml 2.3 生成发布版…...
【C++动态规划】有效括号的嵌套深度
本文涉及知识点 C动态规划 LeetCode1111. 有效括号的嵌套深度 有效括号字符串 定义:对于每个左括号,都能找到与之对应的右括号,反之亦然。详情参见题末「有效括号字符串」部分。 嵌套深度 depth 定义:即有效括号字符串嵌套的层…...
2024年优秀的天气预测API
准确、可操作的天气预报对于许多组织的成功至关重要。 事实上,在整个行业中,天气条件会直接影响日常运营,包括航运、按需、能源和供应链(仅举几例)。 以公用事业为例。根据麦肯锡的数据,在 1.4 年的时间里…...
Android和iOS有什么区别?
Android 和 iOS 有以下区别: 开发者与所属公司: Android:由谷歌公司开发以及开放手机联盟维护。它是基于 Linux 内核和其他开源软件的修改版本,代码开源程度较高,许多厂商都可以基于 Android 源代码进行深度定制和开发…...
NVR小程序接入平台/设备EasyNVR多个NVR同时管理多平台级联与上下级对接的高效应用
政务数据共享平台的建设正致力于消除“信息孤岛”现象,打破“数据烟囱”,实现国家、省、市及区县数据的全面对接与共享。省市平台的“级联对接”工作由多级平台共同构成,旨在满足跨部门、跨层级及跨省数据共享的需求,推动数据流通…...
Spring Cloud Sleuth(Micrometer Tracing +Zipkin)
分布式链路追踪 分布式链路追踪技术要解决的问题,分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节…...
人工智能:机遇与挑战
人工智能(AI)作为当今世界科技发展的前沿领域,正在以前所未有的速度和规模影响着我们的生活和工作方式。AI技术的应用前景广阔,从医疗健康到金融服务,从教育到交通,再到娱乐和家庭生活,AI正在逐…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
