目录

一、命名管道定义

二、命名管道创建

1、指令

2、系统调用

3、删除

三、匿名管道和命名管道的区别

四、命名管道的打开规则

五、代码示例

1、comm.hpp

2、server.cc

3、client.cc


一、命名管道定义

# 匿名管道存在以下核心限制:

  • 仅限亲缘关系进程:只能用于父子进程等有血缘关系的进程间通信(如通过 fork() 创建的子进程)。
  • 单向通信:数据只能单向流动(一端写,另一端读),双向通信需创建两个管道。
  • 临时性:存在于内存中,进程结束后自动销毁。
  • 缓冲区有限:大小固定(通常为一个内存页,如4KB),易写满阻塞。

# 引入命名管道的原因

为解决匿名管道的局限性,命名管道允许任意进程(无论是否有亲缘关系)通过文件系统路径访问,实现跨进程通信。

# 由于匿名管道的局限性,如果我们想让两个毫不相关的进程间进行通信,就需要使用我们的命名管道。

# 命名管道与匿名管道都是只存在于内存中的文件,并不会向磁盘刷新,唯一不同的是匿名管道是通过父子进程看到同一份资源,而命名管道是通过路径与文件名的方式找到同一份文件资源,因为我们知道路径具有唯一性。

# 我们可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件。


二、命名管道创建

1、指令

mkfifo <路径名>   # 例如:mkfifo /tmp/my_pipe

# 生成一个具名管道文件,权限默认受 umask 影响。

# 并且我们可以直接通过其进行echo和cat两个进程间的通信:

2、系统调用

# 使用 mkfifo() 函数:

#include <sys/types.h>
#include <sys/stat.h>


int mkfifo(const char *pathname, mode_t mode);  // 成功返回0,失败返回-1

  • 参数
    • pathname:管道路径/文件名(如 /tmp/my_pipe)。若以路径的方式给出,则将命名管道文件创建在pathname路径下,若以文件名的方式给出,则将命名管道文件默认创建在当前路径下
    • mode:权限标志(如 0666 表示所有用户可读写),它受默认掩码umask的约束。
  • 后续操作
    • 需用 open() 打开管道(读模式 O_RDONLY 或写模式 O_WRONLY)。
    • 默认阻塞行为:读端打开时写端阻塞,反之亦然;可通过 O_NONBLOCK 设为非阻塞。

# 比如说我们可以通过该接口实现客户端client与服务端server间的通信。

3、删除

  • 命令行:rm <路径名> 或 unlink <路径名>
  • 程序内:unlink(pathname)


三、匿名管道和命名管道的区别

关键补充

  • 语义一致性:打开后两者操作方式相同(如 read()/write())。
  • 网络支持:命名管道可跨机器通信,匿名管道仅限本地。
  • 阻塞行为:两者均受缓冲区影响,但命名管道可通过 O_NONBLOCK 灵活控制阻塞。


四、命名管道的打开规则


五、代码示例

# 下面为了更好理解命名管道,我们直接来一段代码,使用命名管道让两个无血缘关系的进程进行通信——一个进程写一个进程读。

# 这里client.cc和server.cc代表两个没有血缘关系的进程,在前面学习进程时我们知道,.cc文件跑起来就是一个进程,所以这里不多赘述。而我们命名管道的创建,以及打开管道文件进行操作的代码则封装在comm.hpp中。Makefile则是我们配置的自动化工具。

1、comm.hpp

# 下面我们就来在comm.hpp中将代码封装起来,首先需要将命名管道创建,最后结束通信后还需要将管道回收,因为命名管道不会随进程的生命周期,所以需要我们手动回收。代码如下:

class NamedFifo
{
public:NamedFifo(const std::string &path, const std::string &name): _path(path), _name(name){_filename = _path + "/" + _name;// 创建命名管道int n = mkfifo(_filename.c_str(), 0666);if(n < 0){std::cerr << "mkfifo failed" << std::endl;}else{std::cout << "mkfifo success" << std::endl;}}~NamedFifo(){// 回收命名管道int n = unlink(_filename.c_str());if(n < 0){std::cerr << "remove fifo failed" << std::endl;}else{std::cout << "remove fifo success" << std::endl;}}private:std::string _path;std::string _name;std::string _filename;
};

# 由于我们要实现一个进程写,一个进程读的单向通信,所以我们先规定,让客户端client.cc进程来写,服务端server.cc进程来读,那么读写操作我们还需要再封装一个类,因为我们只要创建一个管道就行了。

# 如果都封装在一个类中,那么客户端和服务端都需要实例化出一个对象,才能对管道读写通信,但这样就会创建两个命名管道了,因为只要构造函数就会创建命名管道,而我们不需要两个命名管道,我们只需要创建一个命名管道,然后服务端和客户端分别以读写的方式打开这个管道文件就可以进行通信了,所以我们可以再封装一个类来实现对打开的命名管道进行操作。代码如下:

class Fileoper
{
public:Fileoper(const std::string &path, const std::string &name): _path(path), _name(name), _fd(-1){_filename = _path + "/" + _name;}void OpenForRead(){_fd = open(_filename.c_str(), O_RDONLY);if(_fd < 0){std::cerr << "open fifo failed" << std::endl;}else{std::cout << "open fifo success" << std::endl;}}void OpenForWrite(){_fd = open(_filename.c_str(), O_WRONLY);if(_fd < 0){std::cerr << "open fifo failed" << std::endl;}else{std::cout << "open fifo success" << std::endl;}}~Fileoper() {}private:std::string _path;std::string _name;std::string _filename;int _fd;
};

# 由于我们需要打开指定路径的管道文件,所以成员变量仍然需要和NamedFifo类一样,但是我们打开管道文件后,需要通过返回的文件描述符后续管理规管道文件,所以我们还需要一个成员变量_fd,来接收open返回的文件描述符。客户端需要从管道写入,服务端需要从管道读取,所以客户端以只写的方式打开管道文件,而服务端以只读的方式打开管道文件。但是打开之后我们客户端和服务端还需要对管道进行读写操作,所以我们还需要分别实现一个写函数和一个读函数。代码如下:

    void Write(){std::string message;while(true){std::cout << "Please Enter#";std::getline(std::cin, message);write(_fd, message.c_str(), message.size());}}void Read(){while(true){char buffer[1024];ssize_t n = read(_fd, buffer, sizeof(buffer)-1);if(n > 0){buffer[n] = 0;std::cout << "Client say#" << buffer << std::endl;}else if(n == 0){std::cout << "Client quit! me too!" << std::endl;break;}else{std::cerr << "read error" << std::endl;break;}}}

# 当然,通信结束之后我们需要关闭文件描述符。

    void Close(){close(_fd);}

# 我们定义两个宏,想要在当前路径下创建一个fifo的管道文件:

#define PATH "."
#define FILENAME "fifo"

# 再定义一个错误退出的宏:

// 在 C 语言中,\(反斜杠)在这里的作用是续行符,用于将一行代码延续到下一行。
#define ERR_EXIT(m) \
do \
{ \perror(m); \exit(EXIT_FAILURE); \
} while(0)

# 源码:

#pragma once#include <iostream>
#include <string>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define PATH "."
#define FILENAME "fifo"// 在 C 语言中,\(反斜杠)在这里的作用是续行符,用于将一行代码延续到下一行。
#define ERR_EXIT(m) \
do \
{ \perror(m); \exit(EXIT_FAILURE); \
} while(0)class NamedFifo
{
public:NamedFifo(const std::string &path, const std::string &name):_path(path),_name(name){_fifoname = _path + "/" + _name;// 将文件默认掩码设置为0umask(0);// 新建管道int n = mkfifo(_fifoname.c_str(), 0666);if (n < 0){// std::cerr << "mkfifo error" << std::endl;// perror("mkfifo");// exit(1);ERR_EXIT("mkfifo");}else{std::cout << "mkfifo success" << std::endl;}}~NamedFifo(){// 删除管道文件int n = unlink(_fifoname.c_str());if (n == 0){std::cout << "remove fifo success" << std::endl;}else{// std::cout << "remove fifo failed" << std::endl;ERR_EXIT("unlink");}}private:std::string _path;std::string _name;std::string _fifoname;
};class FileOper
{
public:FileOper(const std::string &path, const std::string &name): _path(path), _name(name),_fd(-1){_fifoname = _path + "/" + _name;}void OpenForRead(){// 打开// write方没有执行open的时候,read方就要在open内部进行阻塞,直到有人把管道文件打开了,open才会返回_fd = open(_fifoname.c_str(), O_RDONLY); // 以读方式打开命名管道文件if (_fd < 0){// std::cerr << "open fifo error" << std::endl;// return;ERR_EXIT("open");}std::cout << "open fifo success" << std::endl;}void OpenForWrite(){// write_fd = open(_fifoname.c_str(), O_WRONLY); // 以写方式打开命名管道文件if (_fd < 0){// std::cerr << "Open fifo error" << std::endl;// return;ERR_EXIT("open");}std::cerr << "Open fifo success" << std::endl;}void Write(){// 写入操作std::string message;int cnt = 1;pid_t id = getpid();while (true){std::cout << "Please Enter# ";std::getline(std::cin, message);message += ", message number: " + std::to_string(cnt++) + "[" + std::to_string(id) + "]";write(_fd, message.c_str(), message.size());}}void Read(){// 正常的readwhile (true){char buffer[1024];int number = read(_fd, buffer, sizeof(buffer) - 1);if (number > 0){// 读取成功buffer[number] = 0; // 字符串末尾置\0std::cout << "Client say# " << buffer << std::endl;}else if (number == 0){std::cout << "client quit! me too!" << std::endl;break;}else{std::cerr << "read error" << std::endl;break;}}}void Close(){close(_fd);}~FileOper(){}private:std::string _path;std::string _name;std::string _fifoname;int _fd;
};

2、server.cc

#include "comm.hpp"int main()
{// 创建管道NamedFifo f(PATH, FILENAME);// 文件操作Fileoper reader(PATH, FILENAME);reader.OpenForRead();reader.Read();reader.Close();return 0;
}

3、client.cc

#include "comm.hpp"int main()
{Fileoper Writer(PATH, FILENAME);Writer.OpenForWrite();Writer.Write();Writer.Close();   return 0;
}

运行测试:

# 可以看到成功实现了两个没有血缘关系的进程的单向通信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/97293.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/97293.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/97293.shtml

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

相关文章

LinuxC系统多线程程序设计

一.多线程程序设计1. 线程概述&#xff1a;1.1 什么是线程?线程是进程中的一个实体(组成单元),是系统进程调度的最小单元。一个进程至少具有一个线程&#xff0c;如果进程仅有一个线程&#xff0c;该线程就代表进程本身。把代表进程本身的线程称为主线程&#xff0c;一个进程…

Vue3 + TS + MapboxGL.js 三维地图开发项目

文章目录 1. 安装依赖 2. 新建 Map 组件(components/MapView.vue) 3. 在页面中使用(views/Home.vue) 4. 效果说明 1. 安装依赖 npm install mapbox-gl @types/mapbox-gl --save⚠️ 注意:需要去 Mapbox 官网,申请一个 access token。 package.json {"name":…

【编程语言】Rust 入门

目录 一、Rust 是什么&#xff1f;为什么选择它&#xff1f; 二、环境搭建&#xff0c;迈出第一步 2.1 Windows 系统安装步骤 2.2 macOS 系统安装步骤 2.3 Linux 系统安装步骤 2.4 安装过程中的常见问题及解决方案 三、基础语法&#xff0c;构建知识大厦的基石 3.1 变量…

Python 编码与加密全解析:从字符编码到 RSA 签名验证

在 Python 开发中&#xff0c;字符编码&#xff08;如 UTF-8、GBK&#xff09;和 数据加密&#xff08;如 Base64、MD5、RSA&#xff09;是处理数据传输、存储安全的核心技术。本文结合实战代码&#xff0c;从基础的字符编解码入手&#xff0c;逐步深入到加密算法的应用&#x…

关于shell命令的扩展

目录 一、逻辑运算符 1. &&&#xff08;AND&#xff09; 2. ||&#xff08;OR&#xff09; 3. 组合使用&#xff1a;A && B || C 二、输出与重定向 1. echo 输出 2. 标准文件描述符&#xff08;FD&#xff09; 3. 重定向操作符 4. 同时重定向 stdout 和…

MySQL EXPLAIN 查看执行计划详解

MySQL 的 EXPLAIN 命令。这是一个分析和优化 SQL 查询性能不可或缺的强大工具。它展示了 MySQL 如何执行一条 SQL 语句&#xff0c;包括如何使用索引、表连接顺序、估计的行数等关键信息。1. 如何使用 EXPLAIN在你要分析的 SELECT 语句前加上 EXPLAIN 或 EXPLAIN FORMATJSON&am…

TensorFlow 面试题及详细答案 120道(51-60)-- 模型保存、加载与部署

《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。 前后端面试题-专栏总目录 文章目录 一、本文面试题目录 51. TensorFlow中保存和加…

从零开始学Shell编程:从基础到实战案例

从零开始学Shell编程&#xff1a;从基础到实战案例 文章目录从零开始学Shell编程&#xff1a;从基础到实战案例一、认识Shell&#xff1a;是什么与为什么学1.1 Shell的定义1.2 常用Shell解释器二、Shell编程快速入门&#xff1a;编写第一个脚本2.1 步骤1&#xff1a;创建脚本文…

机器学习算法全景解析:从理论到实践

机器学习算法全景解析&#xff1a;从理论到实践引言机器学习作为人工智能的核心组成部分&#xff0c;正在深刻地改变我们的世界。从推荐系统到自动驾驶&#xff0c;从医疗诊断到金融风控&#xff0c;机器学习算法无处不在。本文将全面系统地介绍机器学习的主要算法类别及其核心…

week5-[二维数组]对角线

week5-[二维数组]对角线 题目描述 给定一个 nnn\times nnn 的正方形二维数组&#xff0c;输出它两条对角线上元素的和。 输入格式 输入共 n1n 1n1 行。 第 111 行 111 个正整数 nnn。 接下来 nnn 行&#xff0c;每行 nnn 个正整数 aija_{ij}aij​ 表示这个二维数组。 输出格式…

GoogLeNet:深度学习中的“卷积网络变形金刚“

大家好&#xff01;今天我们要聊一个在深度学习领域掀起革命的经典网络——GoogLeNet&#xff08;又称Inception v1&#xff09;。这个由Google团队在2014年提出的模型&#xff0c;不仅拿下了ImageNet竞赛冠军&#xff0c;更用"网络中的网络"设计理念彻底改变了卷积神…

笔记本电脑蓝牙搜索不到设备-已解决

方法1打开疑难解答&#xff0c;选择其他疑难解答&#xff0c;下划选择蓝牙&#xff0c;点击运行&#xff0c;电脑自行检测并修复蓝牙方法2右键此电脑&#xff0c;选择管理&#xff0c;找到自己的蓝牙设备。然后对箭头指向的这个点击右键&#xff0c;选择《更新驱动程序》&#…

WPF 程序用户权限模块利用MarkupExtension实现控制控件显示

工作记录 ------------------------------------------------------------------------------------------------------- MarkupExtension:XAML标记扩展 实现了什么作用&#xff1a;通过扩展标记将一种输入转化为另一种类型的输出 思路&#xff1a; 不直接设置控件的Visib…

SpringMVC相关梳理

SpringMVC 返回值类型&#xff08;一&#xff09;核心返回值类型分类视图渲染类&#xff1a;用于跳转并渲染页面&#xff0c;如String&#xff08;指定视图名&#xff09;、ModelAndView&#xff08;视图 数据&#xff09;。数据返回类&#xff1a;用于返回数据&#xff08;而…

Docker化性能监控平台搭建:JMeter+InfluxDB+Grafana全攻略

你作为一名DevOps工程师或测试专家&#xff0c;正在监控一个高并发微服务系统&#xff1a;突发流量峰值导致响应延迟&#xff0c;服务器CPU飙升&#xff0c;但你只能手动查看日志&#xff0c;优化起来像大海捞针。这时&#xff0c;DockerJMeterInfluxDBGrafana的“梦幻四重奏”…

Adobe Acrobat 中通过 JavaScript 调用 Web 服务

强大的JavaScript支持&#xff0c;允许用户通过脚本自动化处理PDF文档。本文将详细介绍如何在Adobe Acrobat环境中使用JavaScript调用Web服务&#xff0c;包括基础概念、实现方法、代码示例以及常见问题解决方案。 第一部分&#xff1a;基础概念与技术背景 1.1 Acrobat JavaScr…

SpringCloud OpenFeign 远程调用(RPC)(三)

目录 1 概念导入 2 添加依赖 3 在启动类上添加注解 4 编写对应的接口 5 注入并调用 6 日志 7 超时控制 8 超时重试 9 拦截器 10 Fallback兜底 1 概念导入 Spring Cloud OpenFeign Features :: Spring Cloud Openfeign 2 添加依赖 <!-- 远程调用 --><depen…

【Flask】测试平台开发,登陆重构

概述我们在开篇的时候实现了简单的登陆功能&#xff0c;也实现了一个前后端联调的登陆功能&#xff0c;但是你有没有发现&#xff0c;那个登陆只是一个简单的登陆&#xff0c;且密码在接口返回的过程中是铭文密码&#xff0c;在生产环境中使用肯定是不行的&#xff0c;一般密码…

【Bluedroid】A2DP Source设备音频数据读取机制分析(btif_a2dp_source_read_callback)

本文聚焦Android 蓝牙 A2DP Source设备的音频数据读取核心逻辑,深入解析关键回调函数btif_a2dp_source_read_callback的功能实现,包括从 HAL(硬件抽象层,支持 HIDL/AIDL 两种传输方式)或 UIPC(用户空间进程间通信)获取音频数据的路径选择机制,以及数据下溢(Underflow)…

多方调研赋能AI+智慧消防 豪越科技人工智能创新获认可

8月26日&#xff0c;中国职业安全健康协会城市及社区安全发展专业委员会秘书长汪卫国以及常务副秘书长黄强亮等诸位领导到访委员单位豪越科技&#xff0c;展开了实地的调研活动并给予相关指导。此次调研着重于了解豪越科技自主研发的“AI消防救援一体化安全管控平台”&#xff…