C++\QT|qt共享内存

一、说明 本文托管代码地址:https://gitee.com/jiangtao008/shareMemory.git
C++\QT|qt共享内存
文章图片

查看此文讲述共享内存,相信大家都是知道进程间通信的,即IPC(Inter-Process Communication)。而共享内存是实现进程通信的方式之一,其他通信方式及优缺点可访问链接:进程间通讯的7种方式
应用场景分析
首先讨论一下为什么需要进程间通信?因为产品经理说项目需要,因为业务需要。
从技术角度来说,不同的进程有自己的内存空间,正常情况下A进程是不能访问B进程的内存空间的。
可是一些大型项目,因为项目业务比较复杂,于是可以将业务拆分成几个相对独立的进程完成,然而各业务进程间又不能完全独立,有时候需要相互之间传输一些数据,于是进程通信方式派上用场了!
放开脑洞
这时候,有人会说我使用文件方式传输信息不行吗。。。。。。比如使用json、xml文件以及配置文件,A进程将数据写入文件中,然后B进程再读取。
首先上面说的的方式肯定是没有问题的,很多软件的配置或者设置系统应该就是这样实现的,但是对于有速度、读写频率要求的,磁盘读写速度肯定是没有内存快的。
有人又会提,不同电脑上的进程通信呢?正好进程通信中有一种方式是TCP/IP,我们使用这个实现进程通信一般说的是本地回环即使用的IP为127.0.0.1,如果不使用本地回环,那不就是普通的TCP/IP通信吗,貌似的确是这样,想一下不严格定义,我们上网或者聊天不就是进程间的通信吗!
二、原理 【C++\QT|qt共享内存】如下图所示,进程A需要和B通信,他们之间有相互独立的内存空间,虽然都会映射到物理内存上面,但是A进程也不知道B进程的空间会被映射到哪里啊,就是通过逆向工程(做外挂及软件破解经常使用的手段)知道了映射的内存地址,可万一该地址B进程释放不用了呢?所以解决此方法就是,在物理内存上创建一个两个进程都知道的共享内存段,所以初始化的时候必须指定一个唯一的标识Key!这样A和B进程就可以通过Key标识访问该内存段通信了。
这样一想,有必要的话,我们可以创建多个内存段操作也不是不行。
C++\QT|qt共享内存
文章图片

------------------------------------------------小涛画的简图-------------------------------------------
有兴趣的可以看看文章:内存原理
共享内存的平台差异性分析

1、Windows平台下:
QSharedMemory对象并不“拥有”共享内存段(共享内存段仍旧归操作系统所有)。当连接到某个共享内存段对象的所有线程或进程都与该内存段断开连接时,操作系统会自动释放该内存段。此处,连接到某个共享内存段的线程或进程,或者主动释放共享内存对象,或者线程或进程退出时,操作系统释放该内存段。
2、linux平台下:
QSharedMemory对象并“拥有”共享内存段。最后一个连接到该共享内存对象的线程或进程通过释放共享内存对象,从而断开与共享内存对象的连接,此时Unix内核释放该共享内存段。但是,如果最后一个连接到该共享对象的线程或进程崩溃,且在崩溃之前没有释放(也可以叫析构)该共享内存对象,那么该共享内存段就不会被释放,造成内存段泄漏(无法被申请使用)。
三、qt实现 数据写入方(进程A):
  1. 实例化QSharedMemory类,并通过函数setKey()设置标志名;
  2. 如果共享内存与主进程当前是关联状态,使用函数 detach()分离,然后再创建;
  3. 使用函数create()创建共享内存段;
  4. 对共享内存进行数据写入;
代码实现(进程A)
QSharedMemory shareMem; //1-实例化QSharedMemory类 shareMem.setKey("TestMem"); //1-通过函数setKey()设置标志名; if(shareMem.isAttached())//2-使用函数detach()将共享内存与主进程分离 shareMem.detach(); char str[100] = "hello,my share memory!"; if(!shareMem.create(100))//3-使用函数create()创建共享内存段 { qDebug() << shareMem.errorString(); return ; } //--------以下代码在需要的时候调用 --------- shareMem.lock(); //4- 调用lock()将共享内存上锁 char *dest = reinterpret_cast(shareMem.data()); const char *source = str; memcpy(dest, source, 100); //5-对共享内存进行数据写入 shareMem.unlock(); //6- 调用unlock()函数将共享内存解锁

读取数据方(进程B):
  1. 实例化QSharedMemory类,并通过函数setKey()设置标志名;
  2. 使用函数 attach()将共享内存与主进程关联;(业务上如果关联不上,也可以主动创建共享内存)
  3. 从共享内存中取数据
代码实现(进程B)
QSharedMemory shareMem; //1-实例化QSharedMemory类 shareMem.setKey("TestMem"); //1-通过函数setKey()设置标志名; if(!shareMem.attach())// { qDebug() << "cann't attach shareMem!"; return ; } char str[100]; //--------以下代码在需要的时候调用 --------- shareMem.lock(); //2- 调用lock()将共享内存上锁 const char *source = (char*)shareMem.constData(); char *dest = str; memcpy(dest, source, 100); //3-对共享内存进行数据写入 shareMem.unlock(); //4- 调用unlock()函数将共享内存解锁

代码编写注意点
代码中 QSharedMemory shareMem; 的实例化为直接创建而非使用new的方式(使用堆内存),如果是在函数内部实例化则是使用的栈内存,跳出函数后将无效!
这个地方需要重点注意,如果无其他进程attach共享内存,跳出函数后QSharedMemory 对象内存被释放,windows下共享内存段内存也会被释放!从共享内存的平台差异性分析中,linux平台下将不会出现此问题,但未测试验证,有一起学习的同学可以验证后底下评论评论(感谢感谢)。
托管代码地址:https://gitee.com/jiangtao008/shareMemory.git

    推荐阅读