【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.在命令行上创建
- 使用
mkfifo
filename
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:…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...