Qt提供通过访问共享内存实现进程间通信


原文链接:
https://blog.csdn.net/qq_18108083/article/details/78595475?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-4.pc_relevant_default&spm=1001.2101.3001.4242.3&utm_relevant_index=7

Qt提供了QSharedMemory类来访问共享内存,实现共享内存的操作。

一、QSharedMemory类常用的操作

1、QSharedMemory类对象的创建

利用QSharedMemory类创建实例对象时,必须为该共享内存指定关键字(即为该共享内存起一个名字)。只有当共享内存被设置了关键字之后,才可以执行创建create()、关联attach()等操作。为共享内存指定关键字有两种方法:

① 通过构造函数QSharedMemory::QSharedMemory ( const QString & key, QObject * parent =0 )为实例对象传入关键字;
例如:
SharedMemory* sharememory;
sharememory = newQSharedMemory(“QSharedMemoryExample”);
② 通过构造函数QSharedMemory::QSharedMemory (QObject * parent = 0 )构造实例对象,之后调用setKey()函数为该实例对象设置关键字。

例如:

QSharedMemory* sharememory;

sharememory = new QSharedMemory();

sharememory->setKey("QSharedMemoryExample ");

2、创建共享内存

bool QSharedMemory::create ( int size, AccessMode mode =ReadWrite )
为QSharedMemory类实例对象创建一个空间大小为size的共享内存,该内存空间默认的访问方式为可读可写。共享内存创建成功返回true,否则返回false。QSharedMemory类定义一个枚举类变量AccessMode,指定了两种共享内存的访问方式:

QSharedMemory::ReadOnly 只读方式访问共享内存

QSharedMemory::ReadWrite 读写方式访问共享内存

3、关联共享内存

bool QSharedMemory::attach ( AccessMode mode =ReadWrite )
将以关键字key命名的共享内存和当前程序进行关联,共享内存默认的访问方式为可读可写。如果程序和共享内存关联成功,返回true,否则返回false。

4、分离共享内存

bool QSharedMemory::detach ()
解除共享内存和程序的关联,即调用该函数后,程序不可以再访问共享内存。如果该共享内存被多个程序实例所关联,当最后一个程序实例和共享内存解除关联后,该共享内存将由操作系统自动释放掉。分离操作成功,返回true。如果返回false,通常意味着该共享内存和程序分离失败,或者其他程序当前正在访问该共享内存,分离操作执行失败。

5、判断共享内存的关联状态

bool QSharedMemory::isAttached ()const
该函数用来判断程序(调用该函数的程序)是否和共享内存进行关联,是返回true,否返回false。

6、设置/获取共享内存的关键字

QString QSharedMemory::key ()const //获取共享内存关键字
Qt应用程序通过关键字来辨识共享内存。key ()函数用来获取共享内存的关键字,如果没有指定实例对象的关键字,或者共享内存的关键字是由nativeKey ()函数指定的话,则返回空。

void QSharedMemory::setKey (const QString & key ) //设定共享内存关键字
setKey () 函数用来为共享内存段设定关键字 ( 为共享内存命名 ) ,如果参数 key 的值和构造函数或者之前指定的关键字相同的话,则该函数将不做任何操作,直接返回。

7、锁定/解锁共享内存

bool QSharedMemory::lock () //锁定共享内存
如果共享内存资源当前处于释放状态,进程调用该函数将共享内存中的资源锁定,并返回true。其他进程将不能访问该共享内存。如果共享内存被其他进程占用时,则该函数会一直处于阻塞状态,直到其他进程使用完毕,释放共享内存资源。
bool QSharedMemory::unlock () //解锁共享内存
如果共享内存资源被当前进程所占有,调用该函数将解锁该共享资源,并返回true。如果当前进程没有占用该资源,或者共享内存被其他进程访问,则不做任何操作并返回false。

为了保证共享内存中数据的完整性,当一个进程在读写共享内存的时候,其他进程不允许对该共享区域进行访问。QSharedMemory类提供了lock()函数和unlock()函数来实现这一共享内存访问机制。某一程序对共享内存进行读写操作之前,需要调用lock()函数锁定该共享内存,之后独享共享内存中的数据,并对数据进行读写等操作。共享内存访问完毕,调用unlock()函数,释放共享内存的使用权限。

8、错误原因

SharedMemoryError QSharedMemory::error ()const
当共享内存出错时,调用该函数显示相应的错误代码。
QString QSharedMemory::errorString ()const
当共享内存出错时,调用该函数,以文本形式显示错误原因。

9、获取共享内存的地址

const void *QSharedMemory::constData ()const
void * QSharedMemory::data ()

const void *QSharedMemory::data ()const //重载函数
程序关联共享内存的前提下,调用该函数返回共享内存中数据的起始地址。如果没有关联共享内存,则返回0。

10、获取共享内存的大小

int QSharedMemory::size ()const
调用该函数将返回程序所关联的共享内存的大小(字节)。如果没有关联的共享内存,则返回0。

二、示例代码

① main.cpp源文件

#include <QtGui/QApplication>
#include "dialog.h"
#include <QTextCodec>
int main(int argc, char *argv[])
{
    QApplication application(argc, argv);
    //Qt国际化显示
    QTextCodec::setCodecForTr(QTextCodec::codecForName("GB18030"));
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB18030"));
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB18030"));
    Dialog dialog;
    dialog.show();
    return application.exec();
}

② dialog.h头文件

#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
//调试用头文件
#include <QDebug>
#include <QMessageBox>
#include <QFileDialog>
#include <QDir>
#include <QPixmap>
#include <QImage>
#include <QDataStream>
#include <QBuffer>
#include <QSharedMemory>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
    Q_OBJECT
public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();
public slots:
    void loadFromFile();        //载入图片按钮 响应函数
    void loadFromMemory();      //显示图片按钮 响应函数
private:
    Ui::Dialog *ui;
    QSharedMemory *sharememory; //定义共享内存实例指针
    bool first_flag;            //判断是否是首次加载文件
};
#endif // DIALOG_H

③ dialog.cpp源文件

#include "dialog.h"
#include "ui_dialog.h"
#define DEBUG           //调试开关
Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    QObject::connect(ui->PBtn_Load,SIGNAL(clicked()),this,SLOT(loadFromFile()));
    QObject::connect(ui->PBtn_Display,SIGNAL(clicked()),this,SLOT(loadFromMemory()));
    sharememory = new QSharedMemory();              //构造实例对象
    sharememory->setKey("QSharedMemoryExample");    //为实例对象指定关键字(给共享内存命名)
    first_flag = true;
}
Dialog::~Dialog()
{
    delete ui;
}
//载入图片按钮响应函数
void Dialog::loadFromFile()
{
    if(sharememory->isAttached())       //检测程序当前是否关联共享内存
        sharememory->detach();          //解除关联
    ui->Label_Display->setText(tr("请选择一张图片"));
    QString filename = QFileDialog::getOpenFileName(
                this,"打开",QString(),tr("Image (*.png *.xpm *.jpg)"));
    QImage image;
    if(!image.load(filename))           //将打开的图片文件和QImage实例关联
    {
        ui->Label_Display->setText(tr("您选择的不是图片文件,请重新选择"));
        return;
    }
    ui->Label_Display->setPixmap(QPixmap::fromImage(image));
    QBuffer buffer;
    buffer.open(QBuffer::ReadWrite);    //构建并打开数据缓冲区,访问方式为读写
#ifdef DEBUG
    qDebug()<<"新建缓冲区的大小为:"<<buffer.size();  //测试缓冲区大小(一般为0)
#endif
    QDataStream in(&buffer);            //建立数据流对象,并和缓冲区相关联
    in << image;                        //向缓冲区写入数据
    int size = buffer.size();           //获取写入数据的大小(图片的大小)
#ifdef DEBUG                             //调试部分
    qDebug()<<"缓冲区的大小为:"<<size;
    qDebug()<<sharememory->key();
    qDebug()<<sharememory->nativeKey();
//    sharememory->setKey("共享内存");   //修改共享内存的关键字,将无法访问共享内存
    qDebug()<<sharememory->key();
    qDebug()<<sharememory->nativeKey();
    qDebug()<<sharememory->error();
    qDebug()<<sharememory->errorString();
#endif
    if(true == first_flag)
    {
        if (!sharememory->create(size)) //创建共享内存,大小为size
        {
            ui->Label_Display->setText(tr("无法创建共享内存段"));
            qDebug()<<sharememory->errorString();
            return;
        }
        first_flag = false;
        qDebug()<<sharememory->size();  //显示共享内存的大小
    }
    //对共享内存进行读写操作
    sharememory->lock();        //锁定共享内存
    char *to = (char*)sharememory->data();      //获取共享内存中的地址
    const char *from = buffer.data().data();    //获取有效数据在缓冲区中的地址
    memcpy(to, from, qMin(sharememory->size(), size));  //将缓冲区中的数据复制到共享内存
    sharememory->unlock();      //释放共享内存
}
//显示图片按钮响应函数
void Dialog::loadFromMemory()
{
    if (!sharememory->attach())     //关联共享内存
    {
         ui->Label_Display->setText("无法关联共享内存");
         return;
    }
     QBuffer buffer;                //构建缓冲区
     QDataStream out(&buffer);      //建立数据流对象,并和缓冲区关联
     QImage image;
     //对共享内存进行读写操作
     sharememory->lock();           //锁定共享内存
     //初始化缓冲区中的数据,setData函数用来初始化缓冲区。
     //该函数如果在open()函数之后被调用,则不起任何作用。
     //buffer.open(QBuffer::ReadOnly);  //解除注释,则setData函数不起作用,无法加载内存中数据
     buffer.setData((char*)sharememory->constData(), sharememory->size());
     buffer.open(QBuffer::ReadOnly);    //只读方式打开缓冲区
     out >> image;                      //将缓冲区的数据写入QImage对象
     sharememory->unlock();         //释放共享内存
     sharememory->detach();         //解除程序和共享内存的关联
     ui->Label_Display->setPixmap(QPixmap::fromImage(image));   //显示图片

Qt之进程间通信(共享内存)

https://blog.csdn.net/lmhuanying1012/article/details/78517610?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-4.pc_relevant_antiscanv2&spm=1001.2101.3001.4242.3&utm_relevant_index=7

简述

上一节中,我们分享下如何利用Windows消息机制来进行不同进程间的通信。但是有很多局限性,比如:不能跨平台,而且必须两个进程同时存在才可以,要么进程A发了消息谁接收呢?

下面我们来分享另外一种跨平台的进行间通信的方式-Shared Memory(共享内存)。

Qt提供的基于共享内存的IPC有QSharedMemory类和QSystemSemaphore类,QSharedMemory可以访问共享内存区域,以及多线程和进程的共享内存区域。而QSystemSemaphore类用于访问系统共享资源,以实现独立进程间的通信。

QSharedMemory

QSharedMemory读写内存时,可以使用lock()实现同步。因此,如果同步完成,必须使用unlock()为共享内存区域解锁。

QSharedMemory可以使用attach()访问共享内存。可以通过指定参数来设置共享内存的访问模式。如果使用的是QSharedMemory::ReadOnly模式,则只能通过只读模式访问共享内存。反之,使用QSharedMemory::ReadWrite模式则可以通过读写模式访问共享内存。

QSharedMemory拥有进程并提供可以返回共享内存区域指针的成员函数。在共享内存区域,成员函数constData()可以通过void类型返回进程正在使用的内存区域指针。创建共享时,QSharedMemory可以以字节为单位分配共享内存区域,还可以通过第二个参数设置函数attach()提供的模式。

QSharedMemory可以设置特定共享内存的固定键。函数setNativeKey()可以设置共享内存对象的键,该函数使用从属平台的共享内存的键进行相关设置。相反,使用函数setKey()可以设置与独立与平台的键。函数setKey()创建与平台本地键(Native Key)映射的键。

QSystemSemaphore

QSystemSemaphore可以提供普通系统的信号量。信号量使用互斥体,而互斥体只可以使用1次锁定(Block)。因此,QSemaphore类不能对有效资源使用多线程,而QSystemSemaphore类可以再多进程或多线程中实现。

QSystemSemaphore与QSemaphore类不同,可以访问多进程。这表示QSystemSemaphore是一个重量级的类。因此,使用单一线程或进程时,建议使用QSemaphore。获得资源前,成员函数acquire()始终阻塞。函数release()用于释放资源,且该函数可以设置参数。该函数的参数>1时,释放资源。

注意事项

初始化QSharedMemory时,必须指定一个唯一的标识Key,进程的Key必须保持一致。可以使用setKey来设置。

加载进内存

说明

进程A-写

分为下面几步:

检测该进程是否连接到共享内存段,如果连接,则将该进程与共享内存段分离。
从系统足够大的内存中得到一个新的共享内存段。
锁定该共享内存段,以阻止第二个对话框进程访问,将缓冲区中的图片复制进共享内存段。
将共享内存段解锁,然后第二个对话框进程就可以访问了。

实现

void Dialog::loadFromFile()
{
    if (sharedMemory.isAttached())
    {
        // 将该进程与共享内存段分离
        if (!sharedMemory.detach())
            qDebug() << "Unable to detach from shared memory.";
    }
 
    QString fileName = QFileDialog::getOpenFileName(0, QString(), QString(),
                                        tr("Images (*.png *.xpm *.jpg)"));
    QImage image;
    if (!image.load(fileName))
    {
        qDebug() << "Selected file is not an image, please select another.";
        return;
    }
 
    // 将数据加载到共享内存中
    QBuffer buffer;
    buffer.open(QBuffer::ReadWrite);
    QDataStream out(&buffer);
    out << image;
    int size = buffer.size();
 
    // 创建共享内存段
    if (!sharedMemory.create(size))
    {
        qDebug() << sharedMemory.errorString() << "\n Unable to create shared memory segment.";
        return;
    }
    sharedMemory.lock();
    char *to = (char*)sharedMemory.data();
    const char *from = buffer.data().data();
    memcpy(to, from, qMin(sharedMemory.size(), size));
    sharedMemory.unlock();
}

从内存中读取

说明
进程B-读

分为下面几步:

将该进程与进程A创建的共享内存段绑定
锁定共享内存段,复制数据到缓冲区,然后写入到QImage中。
将共享内存段解锁,然后将该进程与共享内存段分离。

实现

void MainWindow::loadFromMemory()
{
    // 将共享内存与该进程绑定
    if (!sharedMemory.attach())
    {
        qDebug() << "Unable to attach to shared memory segment.";
        return;
    }
 
    // 从共享内存中读取数据
    QBuffer buffer;
    QDataStream in(&buffer);
    QImage image;
 
    sharedMemory.lock();
    buffer.setData((char*)sharedMemory.constData(), sharedMemory.size());
    buffer.open(QBuffer::ReadOnly);
    in >> image;
    sharedMemory.unlock();
 
    sharedMemory.detach();
    m_pLabel->setPixmap(QPixmap::fromImage(image));
}

qt linux 共享内存,Qt共享内存实现进程间通信(QSharedMemory)

https://blog.csdn.net/weixin_30641571/article/details/116650103

Qt提供了一种安全的共享内存的实现QSharedMemory,以便在多线程和多进程编程中安全的使用。

先说下实现共享内存的步骤,然后用一具体的实例说明。

(一)向共享内存中提供数据的一方:

1,定义QSharedMemory shareMemory,并设置标志名shareMemory.setKey(),例如shareMemory.setKey(“shareimg”);

2,将共享内存与主进程分离 shareMemory.detach();

3,创建共享内存 shareMemory.create();

4,将共享内存上锁shareMemory.lock();

5,将进程中要共享的数据拷贝到共享内存中;

6,将共享内存解锁shareMemory.unlock();

(二)从共享内存中取数据的一方:

1,定义QSharedMemory shareMemory,并设置共享内存的标志名shareMemory.setKey()注意设置的要与提供内存共享的一方要一样,本例是shareMemory.setKey(“shareimg”)。

2,将共享内存上锁shareMemory.lock();

3,将共享内存与主进程绑定shareMemory.attach(),使该进程可以访问共享内存的数据;

4,从共享内存中取数据;

5,使用完后将共享内存解锁shareMemory.unlock(),最后将共享内存与该进程分离shareMemory.detach();

如程序运行图:
在这里插入图片描述
上面的程序(称为A)将图片从文件中读取并显示,同时将读取的图片数据复制到共享内存。下面的程序(称为B)直接从共享内存中获取A读取的图片数据并显示出来。

QT 进程间通信——共享内存

原文链接:https://blog.csdn.net/Sakuya__/article/details/89519412

简述

共享内存(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));
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值