基于QSharedMemory的读写安全的共享内存

这篇具有很好参考价值的文章主要介绍了基于QSharedMemory的读写安全的共享内存。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

多进程交互中,其中共享内存是比较常用的一种交互方式,比较高效且易于调试。网上虽然也有很多基于QSharedMemory的实现,但是都是比较基础的,同时读写,读完后分离进程之类的都没有完全保证安全性。所以我花了一整天重新封装了一个基于QSharedMemory的读写安全的类,包含支持传入成员回调函数,以及一次性压入共享内存中好几包的分包处理。此类继承于QThread,遍历收取消息,当遍历到新数据后,处理分包后,回调成员函数直接执行成员函数代码段。

sharedmemory.h
#ifndef SHAREDMEMORY_H
#define SHAREDMEMORY_H

#include <QThread>
#include <QSharedMemory>
#include <stdio.h>
#include <QSystemSemaphore>
#include <QBuffer>
#include <QDataStream>
#include <QDebug>
//创建一个基类,因为子类是模板类,所以后面调用运行时直接用基类的纯虚函数指向子类运行函数运行
class CallBack
{
public:
    virtual ~CallBack(){}
    virtual void execute(QByteArray) = 0;
};
//实现基类,方便后面绑定类与函数
template <typename T>
class ConcretaCallBack: public CallBack
{
public:
    typedef void (T::*Method)(QByteArray);
    ConcretaCallBack(T* instance,Method thod)
        :m_instance(instance),m_thod(thod){}
    void execute(QByteArray arrData)override{
        (m_instance->*m_thod)(arrData);
    }
private:
    T* m_instance;
    Method m_thod;
};
struct QueueHeader {
    int itemCount; // 队列中项目的数量
    int headOffset; // 队列头的偏移量
    int tailOffset; // 队列尾的偏移量
    // ... 可以添加更多控制信息,如锁状态等
};

struct QueueItem {
    int dataSize; // 数据块的大小
    // 数据块紧随其后
};
class SharedMemory : public QThread
{
public:
    typedef void (*GenericMethodPtr)(QByteArray arrData);
    explicit SharedMemory(const QString &key = nullptr);
    ~SharedMemory();
    bool create(int size);
    bool attach();
    bool detach();
    bool write(QByteArray data);
    QList<QByteArray> readAll();
    template<typename ClassType,typename Method>
    void bind(ClassType* targetObject,Method memFunc)
    {
        if(m_callback != nullptr)
        {
            delete m_callback;
            m_callback = nullptr;
        }
        m_callback = new ConcretaCallBack<ClassType>(targetObject,memFunc);
    }
    void run()override;
private:
    QSharedMemory m_sharedMemory;
    CallBack* m_callback = nullptr;
    QSystemSemaphore semaphore; // 用于同步对共享内存的访问
};

#endif // SHAREDMEMORY_H

sharedmemory.cpp

#include "sharedmemory.h"

SharedMemory::SharedMemory(const QString &key)
    : m_sharedMemory(key),
      semaphore("semaphore_" + key,1)
{

}

SharedMemory::~SharedMemory()
{
    if(m_callback != nullptr)
    {
        delete m_callback;
        m_callback = nullptr;
    }
}

bool SharedMemory::create(int size)
{
    if(m_sharedMemory.isAttached())
    {
        m_sharedMemory.detach();
    }
    if(!m_sharedMemory.create(size))
    {
        return false;
    }
    return true;

}

bool SharedMemory::attach()
{
    if(m_sharedMemory.isAttached())
    {
        return true;
    }
    return m_sharedMemory.attach();
}

bool SharedMemory::detach()
{
    if(!m_sharedMemory.isAttached())
    {
        return true;
    }
    return m_sharedMemory.detach();
}

bool SharedMemory::write(QByteArray data)
{
    semaphore.acquire(); // 请求信号量

    if (!attach()) {
        semaphore.release();
        qDebug()<<"Error:sharedMemory(W) attach faild!";
        return false;
    }

    if (!m_sharedMemory.lock()) {
//        m_sharedMemory.detach();
        semaphore.release();
        qDebug()<<"Error:sharedMemory(W) lock faild!";
        return false;
    }

    QueueHeader header;
    memcpy(&header, m_sharedMemory.data(), sizeof(QueueHeader));

    // 确认是否有足够的空间写入新的数据块
    if (m_sharedMemory.size() - header.tailOffset < data.size() + sizeof(QueueItem)) {
        m_sharedMemory.unlock();
//        m_sharedMemory.detach();
        semaphore.release();
        qDebug()<<"Error:sharedMemory(W) is full!";
        return false;
    }

    // 写入数据块
    QueueItem item;
    item.dataSize = data.size();
    memcpy((char *)m_sharedMemory.data() + header.tailOffset, &item, sizeof(QueueItem));
    memcpy((char *)m_sharedMemory.data() + header.tailOffset + sizeof(QueueItem), data.constData(), data.size());

    // 更新队列头信息
    header.itemCount += 1;
    header.tailOffset += sizeof(QueueItem) + data.size();
    memcpy(m_sharedMemory.data(), &header, sizeof(QueueHeader));

    m_sharedMemory.unlock();
//    m_sharedMemory.detach();
    semaphore.release(); // 释放信号量
    return true;

}

void SharedMemory::run()
{
    while(true)
    {
        QThread::msleep(1000);
        QByteArray frameData;
        QList<QByteArray> result = readAll();
        for (int i = 0;i<result.count();i++)
        {
            if(m_callback != nullptr)
            {
                m_callback->execute(result[i]);
            }
        }
    }
}

QList<QByteArray> SharedMemory::readAll()
{
    QList<QByteArray> dataQueue;
    semaphore.acquire(); // 请求信号量

//    if (!m_sharedMemory.isAttached()) {
    if (!attach()){
        semaphore.release();
        return dataQueue;
    }

    if (!m_sharedMemory.lock()) {
        m_sharedMemory.detach();
        semaphore.release();
        qDebug()<<"Error:sharedMemory(R) lock faild!";
        return dataQueue;
    }

    QueueHeader header;
    memcpy(&header, m_sharedMemory.data(), sizeof(QueueHeader));

    // 读取队列中的所有数据
    int offset = header.headOffset;
    for (int i = 0; i < header.itemCount; ++i) {
        QueueItem item;
        memcpy(&item, (char*)m_sharedMemory.data() + offset, sizeof(QueueItem));
        QByteArray data(item.dataSize, Qt::Uninitialized);
        memcpy(data.data(), (char*)m_sharedMemory.data() + offset + sizeof(QueueItem), item.dataSize);
        dataQueue.append(data);
        offset += sizeof(QueueItem) + item.dataSize;
    }

    // 清空队列
    header.itemCount = 0;
    header.headOffset = sizeof(QueueHeader);
    header.tailOffset = sizeof(QueueHeader);
    memcpy(m_sharedMemory.data(), &header, sizeof(QueueHeader));

    m_sharedMemory.unlock();
    m_sharedMemory.detach();
    semaphore.release(); // 释放信号量
    return dataQueue;
}



发送线程调用规则:

SharedMemory* m_sharedMemory = new SharedMemory("Data");
//第一次进来先分离复位
m_sharedMemory->detach();
m_sharedMemory->create(65535*10);
m_sharedMemory->write(QByteArray("testSend"));

接收线程调用规则:

//业务处理类
MainWindow mainW;
//共享指针初始化
SharedMemory sharedMem("Data");
//绑定处理函数
sharedMem.bind(&mainW,&MainWindow::DealMessage);
//线程遍历接收处理
sharedMem.start();

小彩蛋,子进程qDebug数据,主进程无法监视,这时候需要做个小小的处理,让所有输出输出到标准输出里面,这样子进程就能使用qDebug了文章来源地址https://www.toymoban.com/news/detail-799431.html

#include <stdio.h>
#include <iostream>
void myMessageOutput(QtMsgType type,const QMessageLogContext &context,const QString &msg)
{
    Q_UNUSED(context)
    QByteArray localMsg = msg.toLocal8Bit();
    switch (type) {
    case QtDebugMsg:
        fprintf(stdout,"%s",localMsg.constData());
        break;
    case QtInfoMsg:
        fprintf(stdout,"%s",localMsg.constData());
        break;
    case QtWarningMsg:
        fprintf(stdout,"%s",localMsg.constData());
        break;
    case QtCriticalMsg:
        fprintf(stdout,"%s",localMsg.constData());
        break;
    case QtFatalMsg:
        fprintf(stdout,"%s",localMsg.constData());
        abort();
    }
    fflush(stdout);
}
int main(int argc, char *argv[])
{

        QApplication a(argc, argv);
        qInstallMessageHandler(myMessageOutput);
        return a.exec();
}

到了这里,关于基于QSharedMemory的读写安全的共享内存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 7.7 实现进程内存读写

    内存进程读写可以让我们访问其他进程的内存空间并读取或修改其中的数据。这种技术通常用于各种调试工具、进程监控工具和反作弊系统等场景。在 Windows 系统中,内存进程读写可以通过一些 API 函数来实现,如 OpenProcess 、 ReadProcessMemory 和 WriteProcessMemory 等。这些函数提供

    2024年02月08日
    浏览(47)
  • 驱动开发:内核物理内存寻址读写

    在某些时候我们需要读写的进程可能存在虚拟内存保护机制,在该机制下用户的 CR3 以及 MDL 读写将直接失效,从而导致无法读取到正确的数据,本章我们将继续研究如何实现物理级别的寻址读写。 首先,驱动中的物理页读写是指在驱动中直接读写物理内存页(而不是虚拟内

    2024年02月11日
    浏览(44)
  • 驱动开发:内核读写内存多级偏移

    让我们继续在 《内核读写内存浮点数》 的基础之上做一个简单的延申,如何实现多级偏移读写,其实很简单,读写函数无需改变,只是在读写之前提前做好计算工作,以此来得到一个内存偏移值,并通过调用内存写入原函数实现写出数据的目的。 以读取偏移内存为例,如下

    2024年02月11日
    浏览(39)
  • 驱动开发:内核读写内存浮点数

    如前所述,在前几章内容中笔者简单介绍了 内存读写 的基本实现方式,这其中包括了 CR3切换 读写, MDL映射 读写, 内存拷贝 读写,本章将在如前所述的读写函数进一步封装,并以此来实现驱动读写内存浮点数的目的。内存 浮点数 的读写依赖于 读写内存字节 的实现,因为

    2024年02月06日
    浏览(55)
  • (26)Linux 进程通信之共享内存(共享储存空间)

    共享内存是System V版本的最后一个进程间通信方式。 共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内

    2024年01月16日
    浏览(46)
  • 寄存器内存读写指令(二) —— 多寄存器读写 LDM / STM

    有的时候,CPU可能会遇到 a++; b++; c++,这个时候为了提升效率,CPU可能会一次将多个寄存器里的变量保存到内存中。这个时候之前介绍的 LDR / STR 指令虽然也能实现,但只能操作一个寄存器的读写。 因此,考虑到这点,下面介绍多个寄存器的读写指令 将 多个寄存器 的数据写

    2024年02月07日
    浏览(60)
  • QT Sqlite 内存模式 简单读写

    //本文描述了QT Sqlite 内存模式 ,使用QT 自带库文件,写入和读取。 //QT 6.2.4 MSVC2019调试通过。 //需要在pro文件中加入 QT += sql #include QCoreApplication #include QSqlDatabase #include QSqlQuery #include QDebug #include QSqlDriver //#include QSqlError //#include QStringList //#include QVariant static bool createConnection(

    2024年04月25日
    浏览(36)
  • Postgresql源码(110)分析dsm动态共享内存分配与共享内存mq实例(dsm/toc接口备忘录)

    相关 《Postgresql源码(90)共享内存申请CreateSharedMemoryAndSemaphores》 《Linux内存映射函数mmap与匿名内存块》 《Linux共享内存与子进程继承》 用dsm框架的流程 评估共享内存大小:多次用shm_toc_estimate_chunk、shm_toc_estimate_keys向estimate中增加数据结构,最后用shm_toc_estimate得出刚才增加

    2024年02月14日
    浏览(63)
  • 五、共享内存

    共享内存允许两个或者多个进程共享给定的存储区域。 2.1、共享内存是进程间共享数据的一种最快的方法。 一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。 2.2、使用共享内存要注意的是多个进程之间对一个给定存储区访问

    2024年02月09日
    浏览(15)
  • 第二十五节 共享内存

    什么是共享内存?顾名思义,共享内存就是将内存进行共享,它允许多个不相关的进程访问同一个逻辑内存,直接将一块裸露的内存放在需要数据传输的进程面前,让它们自己使用。因此,共享内存是效率最高的一种IPC 通信机制,它可以在多个进程之间共享和传递数据,进程

    2024年02月13日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包