【Linux】深入解析Linux命名管道(FIFO):原理、实现与实战应用
本文承接上文匿名管道:【Linux】深度解析Linux进程间通信:匿名管道原理、实战与高频问题排查-CSDN博客
深入探讨Linux进程间通信(IPC),以匿名管道为核心,详细阐述其通信目的、实现前提及机制。涵盖数据传输、资源共享等核心目的,说明共享OS资源与系统调用支持的实现前提。重点解析匿名管道和命名管道的机制、特征,包括内核缓冲区管理、文件描述符作用及进程通信的四种关键情况和五大特征。实战应用部分展示进程池、命令行管道的实现及代码示例,同时剖析关键技术细节与常见问题解决方案,为开发者提供全面的技术指导。
目录
deepseek 总结全文:
命名管道
原理:
匿名管道与命名管道的区别
命名管道的打开规则
命名管道的底层实现:
1.创建,删除命名管道:namedPipe.hpp
2.打开管道
函数传参规则
3.读写管道
4.开始通信
让服务端与写入端进行调用:
写入端关闭,读取端也应该关闭
读取端关闭,写入端再写入时被关闭 -- 同匿名管道
deepseek 总结全文:
- 命名管道(FIFO)的核心概念
- 允许无亲缘关系的进程通过文件系统路径访问同一内核缓冲区进行通信。
- 本质是特殊文件,数据不落盘,仅在内核缓冲区中传输。
- 与匿名管道的区别:命名管道通过
mkfifo创建并需显式打开,匿名管道通过pipe函数创建。命名管道的操作规则
- 打开规则:
读端阻塞直到有写端打开(非阻塞模式直接返回)。
写端阻塞直到有读端打开(非阻塞模式返回
ENXIO错误)。读写规则:
读端关闭后,写端会收到
SIGPIPE信号(进程终止)。写端关闭后,读端
read返回0,触发退出逻辑。底层实现与代码实战
C++封装类:通过
NamePiped类管理命名管道的生命周期(创建、打开、读写、删除)。服务端与客户端设计:
服务端:创建管道、以读模式打开、循环读取数据。
客户端:以写模式打开管道、接收用户输入并发送。
同步机制:服务端需等待客户端打开管道后才能继续执行,实现隐式进程同步。
关键代码逻辑
创建与删除:
mkfifo和unlink系统调用。读写实现:基于
read和write系统函数,缓冲区大小为4KB。异常处理:对
SIGPIPE信号的容错(如服务端检测到写端关闭后主动退出)。实际运行效果
验证了命名管道在不同场景下的行为(如读写端关闭的响应)。
通过终端截图展示了通信过程及错误处理逻辑。
命名管道
管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件
原理:
既然是要支持两个毫无关系的进程能够进行通信,那么就需要保证他们打开同一个文件,那么他们就需要在同一个文件内核缓冲区即管道当中写入和读取内容,一个管道属于一个文件,想要找到一个文件就可以通过这个唯一性文件路径进行寻找。而通信时不需要将写入端写入的内容刷新到所打开的文件当中,因此这个文件属于一种特殊文件--->命名管道 ---> 与管道一样是内核级的通信。
文件系统的内容可以看这篇博客:深入理解Linux文件系统:从磁盘结构到inode与挂载-CSDN博客
如图:使用mkfifo指令创建文件,也可以使用mkfifo函数创建文件
如图:可以一直写入
while :;do sleep 1;echo "hello named pipe"; done >> myfifo可以一直打印
如果先关闭读端(右边),那么左边的写端就会直接崩掉,因为echo是一个内建命令,内建命令是属于是bash在写,操作系统就会直接干掉bash,就会导致这个终端直接退出:
如图:
无论在myfifo中写入多少内容,他的文件大小始终是0
匿名管道与命名管道的区别
- 匿名管道由pipe函数创建并打开。
- 命名管道由mkfifo函数创建,打开用open
- FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义
命名管道的打开规则
如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
O_NONBLOCK enable:立刻返回成功
如果当前打开操作是为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
命名管道的底层实现:
需要准备四个文件:
namedPipe.hpp --->用于管理命名管道 server.cc client.cc
server和client是两个常见的角色名称,这种命名方式是为了模拟一种典型的客户端-服务器(Client-Server)架构
Server(服务器):通常是指提供服务的一方,它等待客户端的请求,并根据请求提供相应的服务。
Client(客户端):是指请求服务的一方,它主动发起请求,等待服务器的响应。
Makefile:
.PHONY: all all: client serverclient: client.ccg++ -o $@ $^ -std=c++11server: server.ccg++ -o $@ $^ -std=c++11.PHONY: clean clean:rm -rf client server
1.创建,删除命名管道:namedPipe.hpp
#include<iostream> #include<cstdio> #include<string> #include<cerrno> //mkfifo #include <sys/types.h> #include <sys/stat.h>const std::string comm_path = "./myfifo";int CreateNamePipe(const std::string &path) {// int mkfifo(const char *pathname--->文件路径名,缺省权限:mode);int res = mkfifo(path.c_str(), 0666);if(res != 0){perror("mkfifo");}return res; }int RemovedNamePipe(const std::string &path) {int res = unlink(path.c_str());if(res != 0){perror("unlink");}return res; }在
server.cc中进行调用CreateNamePipe,运行结果:创建成功
主要用来删除特殊文件:unlink ---> 删除指定目录下的文件:成功时为0,错误错误码被设置#include <unistd.h>int unlink(const char *pathname);在
server.cc中进行调用CreateNamePipe、RemovedNamePipe,运行结果:删除成功
做好以上的准备工作:
现在我们需要让客户端client进行写入,服务端server进行读取,并且服务端server管理管道命名管道的整个生命周期,修改namedPipe.hpp对管道进行封装:
const std::string comm_path = "./myfifo";class NamePiped
{
public:NamePiped(const std::string path): _fifo_path(path){int res = mkfifo(path.c_str(), 0666);if (res != 0){perror("mkfifo");}}~NamePiped(){sleep(10);//十秒后关闭管道int res = unlink(_fifo_path.c_str());if (res != 0){perror("unlink");}}private:const std::string _fifo_path;
};
2.打开管道
定义使用者(客户端)和创建者(服务端)的id,文件的fd,只读只写的宏
#define DefaultFd -1 //文件的fd
#define Creater 1
#define User 2
#define Read O_RDONLY //只读
#define Write O_WRONLY //只写
private:const std::string _fifo_path;int _id;int _fd;
- 打开文件的方法不想被看到,给外界提供调用,告诉外界的服务端和客户端文件是否打开成功
- 创建者才需要创建管道,使用者只需要初始化管道
- 析构时服务端关闭管道和文件描述符 , 客户端只关闭文件描述符
class NamePiped
{private://打开文件的方法不想被看到,给外界提供调用,告诉外界的服务端()和客户端是否打开成功bool OpenNamedPipe(int mode){_fd = open(_fifo_path.c_str(), mode);if (_fd < 0)return false;return true;}public:NamePiped(const std::string path, int who): _fifo_path(path), _id(who), _fd(DefaultFd){// 是创建者才需要创建管道,使用者只需要初始化管道就可以if (_id == Creater){int res = mkfifo(path.c_str(), 0666);if (res != 0){perror("mkfifo");}std::cout << "creater create named pipe" << std::endl;}}bool OpenForRead(){return OpenNamedPipe(Read); }bool OpenForWrite(){return OpenNamedPipe(Write);}~NamePiped(){// 是创建者才可以删除管道,使用者不用管if (_id == Creater){int res = unlink(_fifo_path.c_str());if (res != 0){perror("unlink");}std::cout << "creater free named pipe" << std::endl;}if(_fd != DefaultFd) close(_fd);}private:const std::string _fifo_path;int _id;int _fd;
};
服务端以读的方式打开文件 ,客户端以写的方式打开文件
//服务端
int main()
{NamePiped fifo(comm_path, Creater);// 服务端以读的方式打开文件fifo.OpenForRead();return 0;
}// 客户端
int main()
{NamePiped fifo(comm_path, User);// 客户端以写的方式打开文件fifo.OpenForWrite();return 0;
}
函数传参规则
const &: const std::string &XXX 纯输入
* : std::string * 纯输出
& : std::string & 输入输出
3.读写管道
通信基本大小4KB
#define BaseSize 4096
int ReadNamedPipe(std::string *out){char buffer[BaseSize];//将_fd文件的内容读取到bufferint n = read(_fd, buffer, sizeof(buffer));if(n > 0){buffer[n] = 0;*out = buffer;}return n;}//写入管道int WriteNamedPipe(const std::string &in){// 向_fd文件写入内容,写in, 写入in的大小return write(_fd, in.c_str(), in.size());}
4.开始通信
让服务端与写入端进行调用:
//服务端int main() {NamePiped fifo(comm_path, Creater);// 服务端以读的方式打开文件if (fifo.OpenForRead()){std::cout << "server open named pipe done" << std::endl;sleep(3);while (true){std::string message;int n = fifo.ReadNamedPipe(&message);if (n > 0){std::cout << "Client Say> " << message << std::endl;}}}return 0; }//客户端 int main() {NamePiped fifo(comm_path, User);// 客户端以写的方式打开文件if (fifo.OpenForWrite()){std::cout << "client open named pipe done" << std::endl;while (true){std::cout << "Please Enter> ";std::string message;std::getline(std::cin, message);fifo.WriteNamedPipe(message);}}return 0; }运行结果:
对于读取端服务端而言,已经将文件打开,但在写入端客户端未打开写入端时,是处于等待状态的,被阻塞在OpenForRead()调用中。直到客户端编译,打开了管道的写入端,服务端再继续执行,打开读入端。这是一种变相的进程同步。(与匿名管道的区别)
在
由上:客户端和服务端没有任何关系,实现了通信
写入端关闭,读取端也应该关闭
当写入端关闭后,读取端因为read的返回值为0,应该会退出;因此还需要修改服务端代码
int main() {NamePiped fifo(comm_path, Creater);if (fifo.OpenForRead()){std::cout << "server open named pipe done" << std::endl;sleep(3);while (true){std::string message;int n = fifo.ReadNamedPipe(&message);if (n > 0){std::cout << "Client Say> " << message << std::endl;}else if(n == 0){std::cout << "Client quit, Server Too!" << std::endl;break;}else{std::cout << "fifo.ReadNamedPipe Error" << std::endl;break;}}}return 0; }运行结果:写入端关闭,读取端也被关闭
读取端关闭,写入端再写入时被关闭 -- 同匿名管道
这是因为在写入端再次要写入内容的时候,OS判断出读取端已关闭检测出了异常,对写入端发送了13号SIGPIPE的信号,杀掉了写入端。
结语:
随着这篇关于题目解析的博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。
在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。
你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容,让我们在知识的道路上共同前行。
相关文章:
【Linux】深入解析Linux命名管道(FIFO):原理、实现与实战应用
本文承接上文匿名管道:【Linux】深度解析Linux进程间通信:匿名管道原理、实战与高频问题排查-CSDN博客 深入探讨Linux进程间通信(IPC),以匿名管道为核心,详细阐述其通信目的、实现前提及机制。涵盖数据传输…...
第十四届蓝桥杯省赛电子类单片机学习记录(客观题)
01.一个8位的DAC转换器,供电电压为3.3V,参考电压2.4V,其ILSB产生的输出电压增量是(D)V。 A. 0.0129 B. 0.0047 C. 0.0064 D. 0.0094 解析: ILSB(最低有效位)的电压增量计算公式…...
Python高效编程技巧与AI专用库:NumPy/Pandas数据处理与Matplotlib/Seaborn可视化实战
Python高效编程技巧与AI专用库:NumPy/Pandas数据处理与Matplotlib/Seaborn可视化实战 引言:Python高效AI开发的核心技能栈 在AI项目中,数据处理和可视化占据了70%以上的工作量。低效的代码会导致模型训练缓慢,而糟糕的数据可视化…...
vim的一般操作(分屏操作) 和 Makefile 和 gdb
目录 一. vim的基本概念 二. vim基础操作 2.1 插入模式 aio 2.2 [插入模式]切换至[正常模式] Esc 2.3[正常模式]切换至[末行模式] shift ; 2.4 替换模式 Shift R 2.5 视图(可视)模式 (可以快速 删除//注释 或者 增加//注释) ctrl v 三&…...
生成器的应用 async与await实现
生成器配合使用函数 yield 将暂停执行代码,同时把函数返回值传递出去 function s(){console.log(ss); } function * f(){/*当 next() 调用时从头开始执行直到yield 开始检查后面的表达式现在是一个函数,那么首先执行函数当函数执行完毕,有返回值下面相当于 yield undefined*/…...
Apache Shiro 统一化实现多端登录(PC端移动端)
Apache Shiro 是一个强大且易用的Java安全框架,提供了身份验证、授权、密码学和会话管理等功能。它被广泛用于保护各种类型的应用程序,包括Web应用、桌面应用、RESTful服务、移动端应用和大型企业级应用。 需求背景 在当今数字化浪潮的推动下ÿ…...
NAT—地址转换(实战篇)
一、实验拓扑: 二、实验需求: 1.实现内网主机访问外网 2.实现外网客户端能够访问内网服务器 三、实验思路 1.配置NAT地址池实现内网地址转换成公网地址,实现内网主机能够访问外网。 2.配置NAT Sever实现公网地址映射内网服务器地址&…...
用HTML和CSS生成炫光动画卡片
这个效果结合了渐变、旋转和悬浮效果的炫酷动画示例,使用HTML和CSS实现。 一、效果 二、实现 代码如下: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport&quo…...
Vue 3 + Composition API + Vite + Pinia + Element Plus 构建项目的完整指南
以下是使用 Vue 3 + Composition API + Vite + Pinia + Element Plus 构建项目的完整指南,包含 TypeScript 支持配置: 1. 创建项目 使用 Vite 初始化项目 npm create vite@latest my-vue-app -- --template vue-ts如果不需要 TypeScript,使用 --template vue 进入项目目录…...
FPGA_YOLO(三)
上一篇讲的是完全映射,也就是block中的所包含的所有的卷积以及归一,池化卷积 举例总共6个等都在pl侧进行处理(写一个top 顶层 里面conv 1 bn1 relu1 pool1 conv1*1 conv 2 bn2 relu2 pool2 conv1*1 ....总共6个 ),…...
使用 SQL CTE(公共表表达式)优化数据查询的实践
目录 一、背景 二、什么是 CTE? 三、CTE 的基本结构 四、示例分析 五、CTE 的作用 六、优势分析 一、背景 在数据分析和数据库管理中,SQL 查询的效率和可读性是至关重要的。随着数据量的不断增加,复杂的查询变得越来越难以管理和理解。…...
旅游CMS选型:WordPress、Joomla与Drupal对比
内容概要 在旅游行业数字化转型进程中,内容管理系统(CMS)的选择直接影响网站运营效率与用户体验。WordPress、Joomla和Drupal作为全球主流的开源CMS平台,其功能特性与行业适配性存在显著差异。本文将从旅游企业核心需求出发&…...
全面适配iOS 18.4!通付盾加固产品全面升级,护航App安全上架
引言: 苹果官方新规落地! 自2025年4月24日起,所有提交至App Store Connect的应用必须使用Xcode 16或更高版本构建,否则将面临审核驳回风险!Beta版iOS 18.4、iPadOS 18.4现已推出,通付盾iOS加固产品率先完成…...
bash 和 pip 是两种完全不同用途的命令,分别用于[系统终端操作]和[Python 包管理]
bash 和 pip 是两种完全不同用途的命令,分别用于 系统终端操作 和 Python 包管理。以下是它们的核心区别、用法及常见场景对比: 1. 本质区别 特性bashpip类型Shell 命令解释器(一种脚本语言)Python 包管理工具作用执行系统命令、…...
SQL 通用表表达式(CTE )
目录 概念:CTE: Common table Expression CTE 语法 CTE Demo 概念:CTE: Common table Expression 通用表表达式(CTE)是SQL中用于简化复杂查询的工具,第一次上线于SQL Server 2005。 CTE提供…...
一台电脑最多能接几个硬盘?
在使用电脑时,硬盘空间不够是许多用户都会遇到的问题。无论是摄影师、剪辑师等需要大量存储空间的专业人士,还是游戏玩家、数据备份爱好者,都可能希望通过增加硬盘来扩展存储容量。然而,一台电脑究竟最多能接多少个硬盘࿱…...
MATLAB中iscell函数用法
目录 语法 说明 示例 确定数组是否为元胞数组 iscell函数的功能是确定输入是否为元胞数组。 语法 tf iscell(A) 说明 如果 A 是元胞数组,则 tf iscell(A) 返回 1 (true)。否则,将返回 0 (false)。 示例 确定数组是否为元胞数组 创建一个元胞数…...
【玩转全栈】---- Django 基于 Websocket 实现群聊(解决channel连接不了)
学习视频: 14-11 群聊(一)_哔哩哔哩_bilibili 目录 Websocket 连接不了? 收发数据 断开连接 完整代码 聊天室的实现 聊天室一 聊天室二 settings 配置 consumer 配置 多聊天室 Websocket 连接不了? 基于这篇博客&…...
如何快速解决django报错:cx_Oracle.DatabaseError: ORA-00942: table or view does not exist
我们在使用django连接oracle进行编程时,使用model进行表映射对接oracle数据时,默认表名组成结构为:应用名_类名(如:OracleModel_test),故即使我们库中存在表test,运行查询时候&#…...
Selenium之简介
Selenium简介 首先,让我们看看官网是怎么定义的 Selenium是一个支持web浏览器自动化的一系列工具和库的综合项目,提供了扩展来模拟用户和浏览器的交互,用于扩展浏览器分配的分发服务器;用于W3C WebDriver规范的基础架构 其实&a…...
pip 安装某个包之后,Jupyter Lab仍旧显示包冲突;例如:Numba needs NumPy 2.1 or less. Got NumPy 2.2.
异常提示 Numba needs NumPy 2.1 or less. Got NumPy 2.2. --------------------------------------------------------------------------- ImportError Traceback (most recent call last) Cell In[8], line 53 import pywt4 import matplot…...
本地安装git
下载git 通过官网 下载 :Git - Downloading Package 若此页面无法直达,请删掉download/win尝试 2.双击运行安装 选择安装目录: 选择配置,默认不动 git安装目录名 默认即可 Git 的默认编辑器,建议使用默认的 Vim 编辑器…...
小程序内表格合并功能实现—行合并
功能介绍:支付宝小程序手写表格实现行内合并,依据动态数据自动计算每次需求合并的值,本次记录行内合并,如果列内合并,同理即可实现 前端技术:grid布局 display:grid 先看实现效果: axml&…...
SSE协议介绍和python实现
概述: SSE(Server-Sent Events)协议是一种允许服务器向客户端实时推送更新的技术,基于HTTP协议,常用于实时数据推送特点: 单向通信:服务器向客户端推送数据,客户端无法发送数据。基…...
甘肃旅游服务平台+论文源码视频演示
4 系统设计 4.1系统概要设计 甘肃旅游服务平台并没有使用C/S结构,而是基于网络浏览器的方式去访问服务器,进而获取需要的数据信息,这种依靠浏览器进行数据访问的模式就是现在用得比较广泛的适用于广域网并且没有网速限制要求的小程序结构&am…...
Spring Boot 3虚拟线程的使用
在Spring Boot非Web应用中,使用虚拟线程时程序提前终止的问题及解决方案,可以通过以下步骤深入理解和验证: 问题根源分析 JVM退出机制 Java中,当所有非守护线程结束时,JVM会立即退出。即使存在正在运行的守护线程&…...
3、pytest实现参数化
在 pytest 中,参数化(parametrization)是一种强大的功能,可以让你用不同的输入数据重复执行同一个测试函数。这种功能非常有用,可以帮助你显著减少重复代码并提高测试覆盖率。 参数化的主要作用是: 测试多…...
【解决】Linux命令报错:Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64
报错命令 yum install zabbix-web-mysql-scl zabbix-apache-conf-scl centos使用scl切换软件版本时提示Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64 报错原因 CentOS7的SCL源在2024年6月30日停止维护了。 当scl源里面默认使用了centos官方的地址&#x…...
WebRTC中音视频服务质量QoS之FEC+NACK调用流程
WebRTC中音视频服务质量QoS之FECNACK调用流程 WebRTC中音视频服务质量QoS之FECNACK调用流程 WebRTC中音视频服务质量QoS之FECNACK调用流程前言一、WebRTC中FEC基础原理1. FEC基础操作 异或操作XOR2、 FEC中 行向和纵向 计算3、 WebRTC中 媒体包分组和生成FEC的包数① kFecRateT…...
神经网络知识点整理
目录 一、深度学习基础与流程 二、神经网络基础组件 三、卷积神经网络(CNN)编辑 四、循环神经网络(RNN)与LSTM 五、优化技巧与调参 六、应用场景与前沿编辑 七、总结与展望编辑 一、深度学习基础与流程 机器学习流…...









