0


基于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了

#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();
}
标签: 安全

本文转载自: https://blog.csdn.net/BikeFriday/article/details/135567088
版权归原作者 BikeFriday 所有, 如有侵权,请联系我们删除。

“基于QSharedMemory的读写安全的共享内存”的评论:

还没有评论