Linux共享内存和Qt共享内存QSharedMemory的使用,今天你会了吗

一、Linux进程通信

Linux系统下进程通信的方式有很多:

  • 管道(pipe)
  • 命名管道(FIFO)
  • 内存映射(mapped memeory)
  • 消息队列(message queue)
  • 共享内存(shared memory)
  • 信号量(semaphore)
  • 信号(signal)
  • 套接字(Socket)

共享内存允许两个或更多进程访问同一块内存。内存共享是使用的比较简单方便的一种了,简单的一些数据通信和文件共享,用内存共享非常方便。说起内存共享就得提一提多线程、多进程间的内存共享。线程之间的内存都是共享的。严格的说,同一进程的线程使用的是同一个地址空间,而不是在不同地址空间之间进行内存共享;父子进程间的内存共享。父进程以MAP_SHARED|MAP_ANONYMOUS选项mmap一块匿名内存,fork之后,其子孙进程之间就能共享这块内存。

二、Linux共享内存使用

Linux内存共享有几种不同的实现方式

  • 基于传统SYS V的共享内存;
  • 基于POSIX mmap文件映射实现共享内存;
  • 通过memfd_create()和fd跨进程共享实现共享内存;
    这里我们着重了解一下SYS V的共享内存。
    首先来了解下共享内存原理,
    在这里插入图片描述
    当2个或者多个进程通过页表把虚拟内存地址映射到物理后,在物理地址就有一块共同能访问到的内存,就是内存共享区。A,B进程就可以操作这块区域进行数据的交换。

1.基本使用步骤

  • 生成键值
key_t ftok(const char *path ,int id);
  • 创建内存共享区
int shmget(key_t key, size_t size, int shmflg);
  • 映射内存共享区
void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 解除映射
int shmdt(const void *shmaddr);
  • 删除内存共享区
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

2.写共享数据

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>

int main()
{
    
    
  key_t key = ftok(".",0x7777);
  if(key < 0)
  {
    
    
    perror("ftok failed");
    return -1;
  }
  //创建共享内存
  int shmid = shmget(key,4096,IPC_CREAT|IPC_EXCL|0666);
  if(shmid<0)
  {
    
    
    perror("shmget failed");
  }

  //映射,将共享内存挂接到进程地址空间
  char* buf = shmat(shmid,NULL,0);

  int i=0;
  memset(buf,'\0',sizeof(buf));
  strcat(buf,"第六天魔王,");

  //分离去关联
  shmdt(buf);

  //等待输入退出
  while(getchar() == '\0');

  //删除共享内存
  shmctl(shmid,IPC_RMID,NULL);
  return 0;
}

3.读共享数据

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>

int main()
{
    
    
  key_t key = ftok(".",0x7777);
  if(key < 0)
  {
    
    
    perror("ftok failed");
    return 1;
  }
  //创建共享内存
  int shmid = shmget(key,4096,IPC_CREAT);
  if(shmid<0)
  {
    
    
    perror("shmget failed");
    return 2;
  }
  //映射共享内存区
  char* buf = shmat(shmid,NULL,0);
  //读取共享数据
  printf("read shm data: %s\n",buf);  
  //分离去关联
  shmdt(buf);

  return 0;
}

编译文件:gcc -o xxx xxx.c
先执行写,把数据写到共享区域
在这里插入图片描述
使用命令ipcs -m即可读出系统共享内存相关信息。执行读共享内存。
在这里插入图片描述
我们可以看到在主键0x7701a32创建了4096字节的共享区域,权限是666,当有进程访问时连接数就会变化。如果共享内存区域已存在,再次去创建就会报错,如果需要删除共享区域,则要执行函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
当然也可以用命令去手动删除:ipcrm -M 0x7701a32,这样就把上面创建的共享内存区域删除了。

1.键值key可以ftok生成,也可以指定固定值
2.如果共享区域被删除了,再去访问读数据会造成程序崩溃
3.内存共享一般要配合信号量进行数据同步

三、Qt共享内存SharedMemory使用

Qt的内存共享操作封装在SharedMemory类里,使用相比Linux原生内存共享也更简单一点。

1.SharedMemory类介绍

//默认构造函数
QSharedMemory(QObject *parent = Q_NULLPTR);
//构造函数,携带key参数
QSharedMemory(const QString &key, QObject *parent = Q_NULLPTR);
//析构
~QSharedMemory();
//设置key
void setKey(const QString &key);
//返回key
QString key() const;
//设置平台特点key
void setNativeKey(const QString &key);
//返回key
QString nativeKey() const;
//创建共享内存,指定大小和读写模式
bool create(int size, AccessMode mode = ReadWrite);
//返回共享内存大小
int size() const;
//关联共享内存
bool attach(AccessMode mode = ReadWrite);
//判断是否已关联共享内存
bool isAttached() const;
//分离共享内存
bool detach();
//返回共享内存数据
void *data();
//返回const指针共享内存const数据
const void* constData() const;
//返回const指针共享内存数据
const void *data() const;
#ifndef QT_NO_SYSTEMSEMAPHORE
//锁住共享内存
bool lock();
//解锁共享内存
bool unlock();
#endif
//返回共享内存错误
SharedMemoryError error() const;
//返回共享内存错误QString类型
QString errorString() const;

这里使用内置的lock进行数据同步,当然也可以使用信号量进行同步。
并且Qt会在最后一个进程或线程detach()之后直接销毁QSharedMemory。
下面展示一个通过内存共享一个进行把图片写道内存区域,另外一个进程读图片并显示出来。

2.写共享数据

void ShmWriter::WriteToShareMemory()
{
    
    
    //准备图片数据
    QImage image;
    if(!image.load(":/img/puzzle.jpg"))
    {
    
    
        qDebug()<<"load image failed!";
        return;
    }
    QBuffer buff;
    buff.open(QBuffer::WriteOnly);
    QDataStream out(&buff);
    out<<image;

    //创建共享内存对象并设置key为"ImageShm"
    mShmWriter = new QSharedMemory("ImageShm",this);
    if(mShmWriter->isAttached())
    {
    
    
        //还在连接状态则进行分离
        mShmWriter->detach();
    }
    //创建共享内存
    if(!mShmWriter->create(buff.size()))
    {
    
    
        qDebug()<<"create share memory failed!"<<mShmWriter->errorString();
        return;
    }
    //锁住共享区域
    mShmWriter->lock();
    //拷贝数据到共享内存
    char *shmAddr = (char*)mShmWriter->data();
    const QByteArray arry(buff.data());
    const char *i_data = arry.data();
    memcpy(shmAddr,i_data,buff.size());
    //解锁
    mShmWriter->unlock();
}

3.读共享数据

void ShmReader::readFromShareMemory()
{
    
    
    //锁定
    mShmReader->lock();
    //关联共享内存
    if(!mShmReader->attach())
    {
    
    
        qDebug()<<"attach failed!"<<mShmReader->errorString();
        return;
    }
    // 从共享内存中读取数据
    QBuffer buffer;
    QDataStream in(&buffer);
    QImage image;

    buffer.setData( (const char*)mShmReader->constData(),mShmReader->size());
    buffer.open(QBuffer::ReadOnly);
    in>>image;
    //解锁
    mShmReader.unlock();
    //分离共享内存
    mShmReader.detach();
    //显示
    ui->label->setPixmap(QPixmap::fromImage(image));

}

最终读出图片数据,显示如下。
在这里插入图片描述
Qt的共享内存key可以在实例化的时候把key带进去,也可以使用setKey函数进行key的设置。读写端key必须保持一致。
代码示例链接:https://download.csdn.net/download/haohaohaihuai/12566419

作者:费码程序猿
欢迎技术交流:QQ:255895056
转载请注明出处,如有不当欢迎指正

猜你喜欢

转载自blog.csdn.net/haohaohaihuai/article/details/106862138