alin的学习之路:共享内存

1. 概念

共享内存是进程间通信中效率最高的一种方式。

  • 共享内存: 可以被多个进程同时使用的一块内核的内存
  • 有血缘关系的进程
  • 没有血缘关系的进程
  • 这块内存不属于任何的进程, 属于内核 -> 内核对应的在物理内存上的存储空间
  • 共享内存的作用:
  • 进程间通信
  • 进程间通信的方式:
  • 管道(匿名, 有名)
  • 内存映射区 -> 这内存位于进程地址空间用户区的动态库加载区
  • 内存映射区中的内存是属于某个进程的
  • 信号 -> 不推荐使用
  • 套接字
  • 本地套接字(本地的进程间通信)
  • 网络套接字(本地/非本地的进程间通信)
  • 共享内存是迄今为止接触的进程间通信中效率最高的一种

2. 共享内存的使用步骤

  • 共享内存不存在时
  1. 基于一个key来创建共享内存,在Linux下 key是一个整数,在Windows下 key是一个字符串。创建出来的共享内存不属于任何进程,属于内核
  2. 如果某个进程想要使用这块内存,需要与这块内存进行关联
  • 调用相关函数
  1. 如果关联成功,则会获得共享的起始地址,根据这个地址读/写数据
  2. 读写数据前需要加锁,结束后需要解锁
  3. 如果不需要再使用这块共享内存了,可以和共享内存解除关联
  • 调用相关函数即可
  • 解除关联之后,共享内存依然存在,只是这个进程和该共享内存没有关系了
  • 如果进程没有解除关联就退出了,那么会自动解除关联
  • 如果说当前有n个进程和共享内存进行了关联, 这时候删除共享内存, 共享内存依然存在
  • 共享内存的内部维护了一个引用计数 (和共享内存关联的进程的个数)
  • 当引用计数>0的时候, 对共享内存做了删除操作, 共享内存不会被销毁, 当应用计数==0的时候就自动被销毁了(之前的删除操作现在才起作用)
  • 当引用计数==0的时候删除共享内存, 共享内存马上被释放
  • 当共享内存已经存在了
  1. 通过已经创建好了的共享内存的key来打开这块内存
  2. 和共享内存进行关联
  3. 基于起始地址读写内存
  4. 和共享内存解除关联

3. Qt中的共享内存:QShareMemory

下边的是Qt将系统的共享内存API封装之后的得到的类

  • 在Qt框架中为了避免内存泄露, 在Qt中使用共享内存的时候, 如果创建共享内存的对象被释放了, 这块共享内存也就没有了
  • 如果在程序中对共享内存进行解除关联的操作, 共享内存也就被释放了
// 构造函数
QSharedMemory::QSharedMemory(const QString &key, QObject *parent = Q_NULLPTR);
参数:
	- key: 共享内存的唯一标识, 通过这个key来创建/查找共享内存
    - parent: 指定父对象
QSharedMemory::QSharedMemory(QObject *parent = Q_NULLPTR);
// 给共享内存对象指定 key 值
void QSharedMemory::setKey(const QString &key);



// 创建共享内存, 创建成功之后, 进程就自动和共享内存关联到一起了
// 如果基于某个key的共享内存已经存在了, 再调用create函数, 返回false
bool QSharedMemory::create(int size, AccessMode mode = ReadWrite);
参数:
	- size: 共享内存的大小, 单位: 字节
    - mode: 对共享内存的访问权限, 默认是读写
        - QSharedMemory::ReadOnly
		- QSharedMemory::ReadWrite
返回值:
	- 成功: true
    - 失败: false
        
// 和共享内存进行关联    
// 关联成功之后, 默认的访问权限也是读写
bool QSharedMemory::attach(AccessMode mode = ReadWrite);
参数:
    - mode: 对共享内存的访问权限, 默认是读写
        - QSharedMemory::ReadOnly
		- QSharedMemory::ReadWrite
返回值:
	- 成功: true
    - 失败: false

// 判断当前进程是否和共享内存关联到一起了
bool QSharedMemory::isAttached() const;
返回值:
	- 已经关联了: true
    - 没有关联: false
        
// 得到共享的起始地址, 必须要关联成功
// 如果进程没有和共享内存进行关联, 调用这个函数返回NULL
void *QSharedMemory::data();	// -- 用的比较多, 默认情况下对共享内存的权限是: 读写
const void *QSharedMemory::data() const;
const void *QSharedMemory::constData() const;

// 对共享内存加锁
bool QSharedMemory::lock();

// 对共享内存解锁
bool QSharedMemory::unlock();

// 和共享内存解除关联
bool QSharedMemory::detach();

// 打印共享内存操作失败的原因
QString QSharedMemory::errorString() const;

// 得到共享内存对象关联 key 字符串
QString QSharedMemory::key() const;

// 得到关联的共享内存的大小
int QSharedMemory::size() const;
返回值:
	- 如果成功关联了共享内存: 共享内存的大小
    - 没有关联共享内存: 0

4. 读写内存在Qt中的demo

struct Person
{
    int num;
    char name[24];
};

ReadShm

QSharedMemory* shm = new QSharedMemory(this);
shm->setKey("123456");
shm->attach();

shm->lock();
Person* pAddr = (Person*)shm->data();
qDebug() << "num = " << pAddr->num
    << "name = " << pAddr->name;
shm->unlock();

shm->detach();

WriteShm

QSharedMemory* shm = new QSharedMemory("123456");
shm->create(1024);
bool bl = shm->create(1024);
if(!bl)
{
    qDebug() << "shm->create(1024) error: " << shm->errorString();
    return;
}

Person* pAddr = (Person*)shm->data();
shm->lock();
pAddr->num = 100;
strcpy(pAddr->name, "alin");
shm->unlock();