当前位置: 首页 > news >正文

Linux学习:实现简单的共享内存通信

目录

  • 1. 共享内存的接口封装
  • 2. 给共享内存设置同步机制
  • 3. 共享内存通信的主干调用代码

1. 共享内存的接口封装

在这里插入图片描述
将系统调用接口做封装:

  • 1. 为了更好的处理调用接口时的各种情况,对调用错误的情况做特殊处理
  • 2. 在调用成功之后打印提示信息,可以更好的观察程序运行中个具体细节
  • 3. 因为给共享内存的创建与控制接口传入不同的参数时,其实现的功能也是不同的,需要进一步封装区分调用

默认共享内存创建大小,生成key的所需参数与头文件:

#include <sys/types.h>
#include <sys/ipc.h>
#include <iostream>
using namespace std;
#include <string.h>
#include <errno.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>const char* filename = "/home/zyc/linux-learning";//路径要存在,绝对/相对路径皆可
int proj_id = 066;//八进制
const int default_size = 4096;//4KB

十进制int类型转为十六进制字符串形式: 用于更好的打印观察key值

string ToHex(key_t k)
{char ret[1024];snprintf(ret, sizeof(ret), "0x%x", k);//16进制格式化占位符:%xreturn ret;
}

创建共享内存key值:

key_t CreateShmKeyOrDie()
{key_t key = ftok(filename, proj_id);if(key == -1)//key可能是负数,只要不是-1就表示创建成功{cerr << "ftok error , errno : " << errno << " string errno : " << strerror(errno) << endl;exit(1);}return key;
}

对共享内存创建系统调用接口做封装:

int CreateShmOrDie(key_t key, size_t size, int shmflg)
{int shmid = shmget(key, size, shmflg);if(shmid < 0){cerr << "shmget error , errno : " << errno << " string errno : " << strerror(errno) << endl;exit(2);}return shmid;
}

创建共享内存:

int CreateShm(key_t key, size_t size)
{int shmid = CreateShmOrDie(key, size, IPC_CREAT | IPC_EXCL | 0666);//传入默认权限码return shmid;
}

获取已有的共享内存: 通信前提,不同进程看到同一份资源

int GetShm(key_t key, size_t size)
{int shmid = CreateShmOrDie(key, size, IPC_CREAT);//不存在就创建,存在就返回return shmid;
}

共享内存挂载:

void* ShmAttach(int shmid)
{void* addr = shmat(shmid, nullptr, 0);if((long)addr < 0)//64位系统强转为long{cerr << "shmat error , errno : " << errno << " string errno : " << strerror(errno) << endl;}return addr;
}

共享内存解绑:

void ShmDettach(const void* shmaddr)
{int ret = shmdt(shmaddr);if(ret < 0){cerr << "shmdt error , errno : " << errno << " string errno : " << strerror(errno) << endl;}
}

共享内存删除:

void DeleteShm(int shmid)
{int ret = shmctl(shmid, IPC_RMID, nullptr);if(ret < 0){cerr << "delete shm error , errno : " << errno << " string errno : " << strerror(errno) << endl;}else//删除成功打印提示信息{cout << "delete shm success!!!" << endl;}
}

打印共享内存属性信息:

void ShmDebug(int shmid)
{struct shmid_ds ds;int ret = shmctl(shmid, IPC_STAT, &ds);if(ret < 0){cerr << "shm debug error , errno : " << errno << " string errno : " << strerror(errno) << endl;} cout << "segsz : " << ds.shm_segsz << endl;     //大小cout << "nattch : " << ds.shm_nattch << endl;   //关联进程数cout << "ctime : " << ds.shm_ctime << endl;     //创建时间cout << "key : " << ds.shm_perm.__key << endl;  //唯一标识符key值
}

2. 给共享内存设置同步机制

  共享内存虽然是所有通信手段中速度最快的,可是,其不具备同步机制。这就可能导致数据不一致问题,也会让读端进行很多无效读取。而管道通信的方式自带同步机制,所以,在这里的具体实现时,可以借助管道的同步机制来让共享内存同步。
在这里插入图片描述
定义为宏的所需参数,需要包含的头文件:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
using namespace std;
#include <string.h>
#include <cerrno>
#include <fcntl.h>
#include <cassert>#define MODE 0666//创建的管道文件的权限
#define PATH "./fifo"

创建一个命名管道类: 负责命名管道的创建与销毁

//控制管道的创建与销毁
class Fifo
{
public:Fifo(const char* path):_path(path){umask(0);//将权限掩码设置为0int n = mkfifo(_path.c_str(), MODE);if(n == 0){cout << "mkfifo success!!!" << endl;}else{cout << "mkfifo error , errno : " << errno << " string errno : " << strerror(errno) << endl;}}~Fifo(){int n = unlink(_path.c_str());if(n == 0){cout << "unlink success!!!" << endl;}else{cout << "unlink error , errno : " << errno << " string errno : " << strerror(errno) << endl;}}private:string _path;
};

创建一个用于提供同步机制的类:

//共享内存同步机制实现
//只用起来控制同步,不需要将文件fd暴露给外界
class Sync
{
public:Sync()//读写端文件描述符,通过接口打开并赋值:_rfd(-1),_wfd(-1){}void OpenReadOrDie(){int rfd = open(PATH, O_RDONLY);if(rfd < 0){cout << "read open error , errno : " << errno << " string errno : " << strerror(errno) << endl;exit(1);//打开失败直接退出}_rfd = rfd;}void OpenWriteOrDie(){int wfd = open(PATH, O_WRONLY);if(wfd < 0){cout << "write open error , errno : " << errno << " string errno : " << strerror(errno) << endl;exit(1);}_wfd = wfd;}bool Wait(){int signal = 0;int n = read(_rfd, &signal, sizeof(signal));if(n == sizeof(signal)){cout << "read success!!!" << endl;}else if(n == 0)//读端关闭{cout << "write quit , me too!!!" << endl;return false;}else{cout << "read error , errno : " << errno << " string error : " << strerror(errno) << endl;return false;}return true;//控制是否共享内存是否还进行通信}//bool Wakerupvoid Wakeup(){int signal = 1;int n = write(_wfd, &signal, sizeof(signal));assert(n == sizeof(signal));//断言为真,程序正常运行,否则报错}~Sync(){}
private:int _rfd;int _wfd;
};

3. 共享内存通信的主干调用代码

Server端:负责创建删除共享内存、创建管道文件、通信双方中的读端

#include "Comm.hpp"
#include "Fifo.hpp"//Server端:创建删除共享内存、读数据
int main()
{//创建管道文件Fifo fifo(PATH);//创建key值key_t key = CreateShmKeyOrDie();cout << "Server key : " << ToHex(key) << endl;//根据key值创建共享内存int shmid = CreateShm(key, default_size);cout << "Server shmid : " << shmid << endl;//将共享内存进行挂载,获取共享内存的地址char* addr = (char*)ShmAttach(shmid);//获取地址时必须确定指针类型//打印一下共享内存的属性信息//ShmDebug(shmid);//以读方式打开管道文件Sync sync;sync.OpenReadOrDie();//读取操作,默认传递的数据是字符串while(1){if(!sync.Wait())//等待被唤醒,写端关闭停止读取break;cout << "Content : " << addr << endl;}//解绑共享内存ShmDettach(addr);sleep(5);//删除共享内存DeleteShm(shmid);return 0;
}

Client端:通信双方中的写端

#include "Comm.hpp"
#include "Fifo.hpp"//Client端:获取共享内存、写入数据
int main()
{//创建通信key值key_t key = CreateShmKeyOrDie();cout << "Client key : " << ToHex(key) << endl;//获取共享内存int shmid = GetShm(key, default_size);cout << "Client shmid : " << shmid << endl;//挂载共享内存//获取地址时必须确定指针类型,指针类型之间不会发生隐式类型转换需要进行强转char* addr = (char*)ShmAttach(shmid);//以写方式打开管道文件Sync sync;sync.OpenWriteOrDie();//写入数据for(char i = 'A'; i <= 'Z'; i++){addr[i - 'A'] = i;sleep(1);sync.Wakeup();//唤醒读端}//解绑共享内存ShmDettach(addr);return 0;
}

自动化构建工具makefile:

.PHONY:all
all:shm_client shm_servershm_client:ShmClient.ccg++ -o $@ $^ -std=c++11shm_server:ShmServer.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -rf shm_client shm_server

  vim编辑器中,可以通过:%s/原字符串/替代字符串,快速批量修改字符串。

http://www.lryc.cn/news/624729.html

相关文章:

  • 06多段代码复杂度合成规则
  • 学习日志37 python
  • [优选算法专题二滑动窗口——水果成篮]
  • PyTorch数据处理工具箱(数据处理工具箱概述)
  • 【JavaEE】(16) Spring Boot 日志
  • C语言关于函数传参和返回值的一些想法
  • 《亚矩阵云手机重构出租接单:KVM 虚拟化与边缘计算驱动的设备替代技术路径》
  • Highcharts for Flutter 正式发布
  • SQL语法大全指南
  • 【Day 29 】Linux-数据库
  • 设计模式(四)——责任链模式
  • 福彩双色球第2025095期篮球号码分析
  • 19.8 《3步实现OPT-6.7B无损量化:用自定义数据集省70%显存,精度仅跌2.3%》
  • 终极方案!lightRag/graphRag离线使用tiktoken持续报错SSLError,不改源码,彻底解决!
  • 海洋牧场邂逅海洋旅游:碰撞出新业态的璀璨火花
  • 北斗安心联车辆管理系统优势分析
  • 飞机起落架轮轴深孔中间段电解扩孔内轮廓检测 - 激光频率梳 3D 轮廓检测
  • Conda技巧:修改Conda环境目录,节省系统盘空间
  • 【每天学点‘音视频’】前向纠错 和 漏包重传
  • vue从入门到精通:搭建第一个vue项目
  • 表格内容对比及标记
  • PLC无线组网实现多台RGV搬运机器人输送系统通讯案例
  • SSM从入门到实战:1.4 Spring Bean的生命周期管理
  • 【STM32】STM32H750 CubeMX 配置 USB CDC 虚拟串口笔记
  • ThinkPHP的安装运行和调试
  • MCP协议演进:从SSE到Streamable HTTP的技术革命
  • SAP ABAP IS SUPPLIED
  • 【语法糖】什么是语法糖
  • Java+Vue构建资产设备管理系统,适配移动端与后台管理,实现全生命周期管理,涵盖采购、入库、使用、维护、报废等环节,提供完整源码,便于二次开发
  • 快速搭建项目(若依)