Linux系统基础-进程间通信(5)_模拟实现命名管道和共享内存

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

Linux系统基础-进程间通信(5)_模拟实现命名管道和共享内存

收录于专栏[Linux学习]
本专栏旨在分享学习Linux的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1. 命名管道的实现

1.1 命名管道类

定义常量与宏

类定义

成员变量 

构造函数

私有方法

公共方法

析构函数

1.2 客户端

1.3 服务端

1.4 效果展示: 

2. 共享内存的实现

2.1 共享内存类

常量定义

成员变量

私有方法

公共方法

2.2 命名管道类

2.3 客户端

2.4 服务端

2.5 效果展示:


1. 命名管道的实现

1.1 命名管道类

这个类 NamePiped 封装了命名管道的基本操作,提供了创建、打开、读取和写入的接口 

#pragma#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string comm_path = "./myfifo";
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096class NamePiped
{
private:bool OpenNamePipe(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(_fifo_path.c_str(), 0666);if(res != 0){perror("mkfifo");}std::cout << "create create named pipe" << std::endl;}}bool OpenForRead(){return OpenNamePipe(Read);}bool OpenForWrite(){return OpenNamePipe(Write);}int ReadNamePipe(std::string *out){char buffer[BaseSize];int n = read(_fd, buffer, sizeof(buffer));if(n > 0){buffer[n] = 0;*out = buffer;}return n;}int WriteNamePipe(const std::string &in){return write(_fd, in.c_str(), in.size());}~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;
};

定义常量与宏

const std::string comm_path = "./myfifo";: 定义了一个常量字符串,表示命名管道的路径。

#define DefaultFd -1: 定义一个默认的文件描述符值,表示未打开状态。

#define Creater 1: 定义服务端标识,用于区分管道的服务端和客户端

#define User 2: 定义客服端标识,用于标识读取或写入管道的用户。

#define Read O_RDONLY 和 #define Write O_WRONLY: 分别定义读取和写入的打开模式。

#define BaseSize 4096: 定义缓冲区的大小,用于读取数据。

类定义

成员变量 

const std::string _fifo_path;: 保存命名管道的路径。

int _id;: 标识是服务端还是用户。

int _fd;: 文件描述符,用于读写操作。

构造函数

初始化成员变量: 使用初始化列表设置路径、身份和默认文件描述符。

创建管道: 如果 _id 是 Creater(服务端),则调用 mkfifo 创建命名管道。如果创建失败,使用 perror 输出错误信息。

私有方法

OpenNamePipe(int mode) : 功能: 封装打开命名管道的逻辑。

调用 open 函数尝试打开管道。

如果打开失败,返回 false,成功则返回 true。

公共方法

OpenForRead() 功能: 调用 OpenNamePipe 以只读模式打开管道。

OpenForWrite() 功能: 调用 OpenNamePipe 以只写模式打开管道。

ReadNamePipe(std::string *out) 功能: 从管道读取数据。

定义一个缓冲区,调用 read 从文件描述符读取数据。

如果读取成功,将数据存入输出参数,并返回读取的字节数。

WriteNamePipe(const std::string &in) 功能: 向管道写入字符串数据。

析构函数

~NamePiped() 功能: 释放资源和清理工作。

如果 _id 是 Creater,则使用 unlink 删除命名管道。

关闭文件描述符(如果已打开)。

1.2 客户端

#include "namedPipe.hpp"//write
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.WriteNamePipe(message);}}return 0;
}

1.3 服务端

#include "namedPipe.hpp"
//server read : 管理命名管道的整个生命周期int main()
{NamePiped fifo(comm_path, Creater);//对于进程而言, 如果我们打开文件, 但是写还没来, 进程会阻塞在open调用中, 直到对方打开//进程同步if(fifo.OpenForRead()){std::cout << "server open named pipe done" << std::endl;sleep(3);while(true){std::string message;int n = fifo.ReadNamePipe(&message);if(n > 0){std::cout << "Client Say > " << message << std::endl;}else if(n == 0){std::cout << "Client quit, Server Too!" << std::endl;}else{std::cout << "fifo.ReadNamedPipe Error" << std::endl;break;}}}return 0;
}

1.4 效果展示: 

通过命名管道, 我们完成了客户端与服务器端的通信! (因为命名管道不需要两个进程具有血缘关系) 

2. 共享内存的实现

完成上述客户端与服务器端的通信, 除了使用命名管道, 共享内存同样可以!

2.1 共享内存类

#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>#include <string.h>const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/home/cjb/linux-operating-system-network/linux22/shm";
const int gproj_id = 0x66;
const int gShmSize = 4097; // 4096*nclass Shm
{
private:key_t GetCommKey(){key_t k = ftok(_pathname.c_str(), _proj_id);if (k < 0){perror("ftok");}return k;}int GetShmHelper(key_t key, int size, int flag){int shmid = shmget(key, size, flag);if (shmid < 0){perror("shmget");}return shmid;}std::string RoleToString(int who){if (who == gCreater)return "Creater";else if (who == gUser)return "gUser";elsereturn "None";}void *AttachShm(){if (_addrshm != nullptr)DetachShm(_addrshm);void *shmaddr = shmat(_shmid, nullptr, 0);if (shmaddr == nullptr){perror("shmat");}std::cout << "who: " << RoleToString(_who) << "attach shm..." << std::endl;return shmaddr;}void DetachShm(void *shmadder){if (shmadder == nullptr)return;shmdt(shmadder);std::cout << "who: " << RoleToString(_who) << "detach shm..." << std::endl;}public:Shm(const std::string &pathname, int proj_id, int who): _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr){_key = GetCommKey();if (_who == gCreater)GetShmUseCreate();else if (_who == gUser)GetShmForUse();_addrshm = AttachShm();std::cout << "shmid: " << _shmid << std::endl;std::cout << "_key: " << ToHex(_key) << std::endl;}~Shm(){if (_who == gCreater){int res = shmctl(_shmid, IPC_RMID, nullptr);}std::cout << "shm remove done..." << std::endl;}std::string ToHex(key_t key){char buffer[128];snprintf(buffer, sizeof(buffer), "0x%x", key);return buffer;}bool GetShmUseCreate(){if (_who == gCreater){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);if (_shmid >= 0)return true;std::cout << "shm create done..." << std::endl;}return false;}bool GetShmForUse(){if (_who == gUser){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);if (_shmid >= 0)return true;std::cout << "shm get done..." << std::endl;}return false;}void Zero(){if (_addrshm){memset(_addrshm, 0, gShmSize);}}void *Addr(){return _addrshm;}void DebugShm(){struct shmid_ds ds;int n = shmctl(_shmid, IPC_STAT, &ds);if (n < 0)return;std::cout << "ds.shm_prem.__key : " << ToHex(ds.shm_perm.__key) << std::endl;std::cout << "ds.shm_nattch" << ds.shm_nattch << std::endl;}private:key_t _key;int _shmid;std::string _pathname;int _proj_id;int _who;void *_addrshm;
};

常量定义

gCreater gUser:分别表示创建者和用户角色的常量,用于区分不同的操作角色。

gpathname:共享内存文件路径,ftok 使用此路径生成唯一的键。

gproj_id:用于生成唯一键的项目 ID。

gShmSize:共享内存的大小,设置为 4097 字节(必须是 4096 的倍数)。

成员变量

_key:存储生成的共享内存键。

_shmid:共享内存 ID。

_pathname:存储共享内存路径。

_proj_id:存储项目 ID。

_who:当前对象的角色(创建者或用户)。

_addrshm:指向当前附加的共享内存的指针。

私有方法

GetCommKey():

使用 ftok 函数生成共享内存的键。如果生成失败,打印错误信息。

GetShmHelper():

封装对 shmget 的调用,尝试获取共享内存 ID。如果失败,打印错误信息。

RoleToString():

将角色 ID 转换为字符串,方便调试输出。

AttachShm():

尝试附加共享内存。如果 _addrshm 不为空,先调用 DetachShm() 分离它。使用 shmat 函数附加共享内存,并打印调试信息。

DetachShm():

封装对 shmdt 的调用,用于分离共享内存。如果传入的指针为空,直接返回。

公共方法

构造函数:

接受路径、项目 ID 和角色,并初始化相关变量。

调用 GetCommKey() 获取键。

根据角色调用 GetShmUseCreate() 或 GetShmForUse() 来获取共享内存。

调用 AttachShm() 附加共享内存,打印共享内存 ID 和键。

析构函数:

如果角色是创建者,调用 shmctl 删除共享内存(IPC_RMID)。

ToHex():

将共享内存键转换为十六进制字符串,方便输出调试信息。

GetShmUseCreate():

仅在创建者角色时调用,尝试创建共享内存段(IPC_CREAT | IPC_EXCL),成功返回 true。

GetShmForUse():

仅在用户角色时调用,尝试获取共享内存段(IPC_CREAT),成功返回 true。

Zero():

清零共享内存内容,使用 memset。

Addr():

返回当前附加的共享内存地址。

DebugShm():

获取共享内存的状态信息(如键和连接数),使用 shmctl 和 IPC_STAT。

2.2 命名管道类

和上面一样, 不需要改变

#pragma#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string comm_path = "./myfifo";
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096// const std::string comm_path = "./myfifo";: 定义了一个常量字符串,表示命名管道的路径。
// #define DefaultFd -1: 定义一个默认的文件描述符值,表示未打开状态。
// #define Creater 1: 定义创建者标识,用于区分管道的创建者和用户。
// #define User 2: 定义用户标识,用于标识读取或写入管道的用户。
// #define Read O_RDONLY 和 #define Write O_WRONLY: 分别定义读取和写入的打开模式。
// #define BaseSize 4096: 定义缓冲区的大小,用于读取数据。// 这个类 NamePiped 封装了命名管道的基本操作,提供了创建、打开、读取和写入的接口
class NamePiped
{
private:bool OpenNamePipe(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(_fifo_path.c_str(), 0666);if(res != 0){perror("mkfifo");}std::cout << "create create named pipe" << std::endl;}}bool OpenForRead(){return OpenNamePipe(Read);}bool OpenForWrite(){return OpenNamePipe(Write);}int ReadNamePipe(std::string *out){char buffer[BaseSize];int n = read(_fd, buffer, sizeof(buffer));if(n > 0){buffer[n] = 0;*out = buffer;}return n;}int WriteNamedPipe(const std::string &in){return write(_fd, in.c_str(), in.size());}~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;
};

2.3 客户端

#include "Shm.hpp"
#include "namedPipe.hpp"int main()
{// 1. 创建共享内存Shm shm(gpathname, gproj_id, gUser);shm.Zero();char *shmaddr = (char *)shm.Addr();sleep(3);// 2. 打开管道NamePiped fifo(comm_path, User);fifo.OpenForWrite();// 当成stringchar ch = 'A';while (ch <= 'Z'){shmaddr[ch - 'A'] = ch;std::string temp = "wakeup";std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;fifo.WriteNamedPipe(temp);sleep(2);ch++;}return 0;
}

2.4 服务端

#include "Shm.hpp"
#include "namedPipe.hpp"int main()
{// 1. 创建共享内存Shm shm(gpathname, gproj_id, gCreater);char *shmaddr = (char*)shm.Addr();shm.DebugShm();sleep(5);return 0;
}

2.5 效果展示:

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

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

相关文章

Mac 使用 zsh 终端提示 zsh: killed 的问题

我的脚本的内容为&#xff1a; #!/bin/bashset -epids$(ps -ef | grep consul | grep -v grep | awk {print $2})for pid in $pids; doecho "kill process: $pid"kill -9 $pid donecd $(dirname $0)nohup ./consul agent -dev > nohup.log &可以看到这是一个…

阿里云项目启动OOM问题解决

问题描述 随着项目业务的增长&#xff0c;系统启动时内存紧张&#xff0c;每次第一次启动的时候就会出现oom第二次或者第n的时候&#xff0c;就启动成功了。 带着这个疑问&#xff0c;我就在阿里云上提交了工单&#xff0c;咨询为什么第一次提交失败但是后面却能提交成功尼&a…

Matlab学习01-矩阵

目录 一&#xff0c;矩阵的创建 1&#xff0c;直接输入法创建矩阵 2&#xff0c;利用M文件创建矩阵 3&#xff0c;利用其它文本编辑器创建矩阵 二&#xff0c;矩阵的拼接 1&#xff0c;基本拼接 1&#xff09; 水平方向的拼接 2&#xff09;垂直方向的拼接 3&#xf…

shell脚本-函数

文章目录 一、函数介绍什么是函数、为什么使用函数、如何使用函数 二、shell脚本中如何定义函数Way1Way2Way3 三、shell脚本中如何调用函数四、shell脚本中使用内置变量(1、#、?、2等等)五、函数的返回值、shell脚本中函数的返回值函数的返回值概念shell脚本中函数的返回值ret…

梦金园三闯港交所上市:年营收200亿元,靠加盟模式取胜

近日&#xff0c;梦金园黄金珠宝集团股份有限公司&#xff08;以下简称“梦金园”&#xff09;向港交所递交IPO申请&#xff0c;中信证券为其独家保荐人。贝多财经了解到&#xff0c;这已经是梦金园第三次向港股发起冲击&#xff0c;此前曾于2023年9月、2024年4月两度递表。 继…

刷题 - 图论

1 | bfs/dfs | 网格染色 200. 岛屿数量 访问到马上就染色&#xff08;将visited标为 true)auto [cur_x, cur_y] que.front(); 结构化绑定&#xff08;C17&#xff09;也可以不使用 visited数组&#xff0c;直接修改原始数组时间复杂度: O(n * m)&#xff0c;最多将 visited 数…

Deepinteraction 深度交互:通过模态交互的3D对象检测

一.前提 为什么要采用跨模态的信息融合? 点云在低分辨率下提供必要的定位和几何信息&#xff0c;而图像在高分辨率下提供丰富的外观信息。 -->因此必须采用跨模态的信息融合 提出的原因? 传统的融合办法可能会由于信息融合到统一表示中的不太完美而丢失很大一部分特定…

磁珠的工作原理:【图文讲解】

1&#xff1a;什么是磁珠 磁珠是一种被动组件&#xff0c;用来抑制电路中的高频噪声。磁珠是一种特别的扼流圈&#xff0c;其成分多半为铁氧体&#xff0c;利用其高频电流产生的热耗散来抑制高频噪声。磁珠有时也称为磁环、EMI滤波器、铁芯等。 磁珠是滤波常用的器件&#xf…

SpringMVC常用注解

RequestMapping接口的映射&#xff0c;可以将HTTP请求映射到控制器方法上&#xff0c;通过这个注解使用不同的映射&#xff0c;就可以区分不同的控制器&#xff0c;其中RequestMapping中还有不同的属性&#xff0c;比如method&#xff0c;params&#xff0c;produces等在这里我…

快速搭建SpringBoot3+Prometheus+Grafana

快速搭建SpringBoot3PrometheusGrafana 一、搭建SpringBoot项目 1.1 创建SpringBoot项目 1.2 修改pom文件配置 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://…

25年山东高考报名时间为10月23日-29日

今日&#xff0c;山东省招生考试院发布关于《山东省2025年普通高等学校招生考试报名工作的通知》 其中高考报名时间定为&#xff1a;2024年10月23日29日&#xff08;每天9&#xff1a;0018&#xff1a;00&#xff09; 资格审查时间为&#xff1a;10月30日~11月11日 网上缴费时间…

Android问题记录 - 适配Android Studio Ladybug/Java 21/AGP 8.0

文章目录 前言开发环境问题描述问题分析1. 适配Java 212. 适配AGP 8.0 解决方案补充内容最后 前言 Android Studio版本从Koala Feature Drop升级到Ladybug&#xff0c;出现了一系列报错。 开发环境 Flutter: 3.24.3Android Studio: 2024.2.1 Patch 1Java: 21.0.3Gradle: 7.4…

FPGA实现PCIE采集电脑端视频转SFP光口万兆UDP输出,基于XDMA+GTX架构,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的PCIE方案10G Ethernet Subsystem实现万兆以太网物理层方案 3、PCIE基础知识扫描4、工程详细设计方案工程设计原理框图电脑端视频PCIE视频采集QT上位机XDMA配置及使用XDMA中断模块FDMA图像缓存UDP视频组包发送UDP协议栈MAC…

高效改进!防止DataX从HDFS导入关系型数据库丢数据

高效改进&#xff01;防止DataX从HDFS导入关系型数据库丢数据 针对DataX在从HDFS导入数据到关系型数据库过程中的数据丢失问题&#xff0c;优化了分片处理代码。改动包括将之前单一分片处理逻辑重构为循环处理所有分片&#xff0c;确保了每个分片数据都得到全面读取和传输&…

Git文件操作指令和文件状态

一、Git 文件操作指令 1、查看指定文件的状态 git status [filename] 我们在新创建且初始化过后的 git 仓库中新建一个 文件&#xff0c;然后在 git 的命令行中输入此指令后&#xff0c;就可以看到 的状态&#xff1a; 在此显示的是 Untracked 的状态&#xff0c;也就是未…

visual studio设置修改文件字符集方法

该方法来自网文&#xff0c;特此记录备忘。 添加两个组件&#xff0c;分别是Force UTF-8,FileEncoding。 截图如下&#xff1a; 方法如下&#xff1a;vs中点击“扩展”->“管理扩展”&#xff0c;输入utf搜索&#xff0c;安装如下两个插件&#xff0c;然后重启vs&#xf…

【pytorch DistributedDataParallel 及amp 使用过程遇到的问题记录 】

目录 环境问题单机多卡时&#xff1a;超时错误部分报错内容:解决方法: 存在没有使用梯度的参数报错内容:解决方法:方法1 找到不参与梯度计算的层**且**没有用处的层&#xff0c;删除方法2 DistributedDataParallel 增加参数:find_unused_parameters True DDP 训练时第一个batc…

2 两数相加

解题思路&#xff1a; \qquad 这道题可以用模拟很直观的解决&#xff0c;模式加法的计算过程&#xff0c;只不过套了一层链表的外衣。题目给出的数字在链表中是按照逆序排列的&#xff0c;即链表头节点的值代表相加数字的个位&#xff0c;这样只需要从链表头开始计算加法即可得…

系统登录接口文档Demo

接口描述 该接口用于用户登录验证。通过用户名和密码进行身份验证&#xff0c;成功后返回一个用于后续请求的认证 token。这个 token 是访问受保护资源的凭证。 时序图&#xff1a; 登录请求&#xff1a; 登录查询接口: POST {url}/api/user/login 请求体: {"username…

简单的 curl HTTP的POSTGET请求以及ip port连通性测试

简单的 curl HTTP的POST&GET请求以及ip port连通性测试 1. 需求 我们公司有一个演示项目&#xff0c;需要到客户那边进行项目部署&#xff0c;项目部署完成后我们需要进行项目后端接口的测试功能&#xff0c;但是由于客户那边么有条件安装类似于postman这种的测试工具&am…