欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > Linux系统基础-进程间通信(5)_模拟实现命名管道和共享内存

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

2025/5/2 10:54:50 来源:https://blog.csdn.net/wer24_25/article/details/143191497  浏览:    关键词: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 效果展示:

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词