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
转载请注明出处,如有不当欢迎指正