简述
共享内存(Shared Memory)是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。共享内存实际就是内存映射的一种特殊情况。在《windows核心编程》写道:
在Windows中,在同一台机器上数据共享的最底层机制就是内存映射文件。 这种数据共享机制是通过让两个或多个进程映射同一个文件映射对象的视图来实现,意味着在进程间共享相同的物理存储页面。对多个进程共享同一个文件映射对象来说,所有进程使用的文件映射对象的名称必须完全相同。
共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,其它进程打开该文件映射对象就可以访问该内存块,这样每个进程都可以读取同一份数据,从而实现进程通信。因为是通过内存操作实现通信,因此是一种最高效的数据交换方法。由于共享内存是用文件映射实现的,所以它也有较好的安全性,也只能运行于同一计算机上的进程之间。
Qt提供了QSharedMemory类和QSystemSemaphore类,QSharedMemory可以访问共享内存区域,以及多线程和进程的共享内存区域。而QSystemSemaphore类用于访问系统共享资源,以实现独立进程间的通信。
效果
代码之路
共享端:
- 创建一个QSharedMemory,并设置一个key值
- 查看这个内存是不是被使用,如果被使用就断开
- 调用create进行创建
- 使用memcpy把要写入的数据放入QSharedMemory中(内部一般会使用互斥锁技术,锁住资源)
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QBuffer>
#include <QFileDialog>
#include <QDataStream>
#include <QSharedMemory>
#include <QPixmap>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QSharedMemory *m_sharedMemory;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//QSharedMemory类提供对共享内存段的访问,创建时设置key值,使用共享内存要有一个相同的key值
m_sharedMemory = new QSharedMemory("1314"); //可以创建时设置key值,也可以用setKey()设置
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
//判断该进程是否已经附加到的共享内存段,如果是,就先分离
if(m_sharedMemory->isAttached()) {
if(!m_sharedMemory->detach()) {
QMessageBox::information(this, tr("错误"), tr("共享内存失败"), QMessageBox::Yes);
return;
}
}
//选择要共享的文件
QString fileName = QFileDialog::getOpenFileName(this,"","","Images(*.png *.jpg)");
QImage image(fileName);
ui->label->setPixmap(QPixmap::fromImage(image).scaled(330, 330));
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QDataStream out(&buffer);
out << image;
//创建共享内存段,失败则显示错误原因
if(!m_sharedMemory->create(buffer.size())) {
QMessageBox::information(this, tr("创建共享内存失败"), tr(m_sharedMemory->errorString().toLatin1().data()), QMessageBox::Yes);
return;
}
m_sharedMemory->lock(); //把数据输入时锁定该共享内存段,其他进程将不能访问该共享内存
char *to = (char*)m_sharedMemory->data();
const char *from = buffer.data().data();
memcpy(to, from, qMin(m_sharedMemory->size(), (int)buffer.size())); //使用memcpy把要写入的数据拷贝入共享内存
m_sharedMemory->unlock(); //输入完数据解锁共享内存
}
读取端:
- 创建一个QSharedMemory,把key值设置为分享端相同的key值
- 使用attach连接上这个QSharedMemory
- 以读取字节的方式读取QSharedMemory中的数据(内部一般会使用互斥锁技术,锁住资源)
- 使用detach端口对这个QSharedMemory的连接
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QBuffer>
#include <QDataStream>
#include <QPixmap>
#include <QSharedMemory>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QSharedMemory *m_sharedMemory;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_sharedMemory = new QSharedMemory("1314");
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
//尝试将该进程附加到共享内存段,成功返回true
if(!m_sharedMemory->attach()){
QMessageBox::information(this, tr("错误"), tr("共享内存失败"), QMessageBox::Yes);
return;
}
//读取共享内存中的数据
QBuffer buffer;
QDataStream in(&buffer);
QImage image;
m_sharedMemory->lock(); //读取数据时锁定该共享内存段,其他进程将不能访问该共享内存
buffer.setData((char *)m_sharedMemory->constData(),m_sharedMemory->size());
buffer.open(QBuffer::ReadOnly);
in>>image;
//读取完解锁该共享内存段,并将该进程从共享内存段中分离出去
m_sharedMemory->unlock();
m_sharedMemory->detach();
ui->label->setPixmap(QPixmap::fromImage(image));
}
源代码百度云下载地址:https://pan.baidu.com/s/1nsmxAB_JPdPIxJ9w7Kh9Iw 提取码:dl2e