news 2026/8/18 13:11:35

67.QT-QSharedMemory

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
67.QT-QSharedMemory

1.QSharedMemory介绍

QSharedMemory提供了多个线程和进程对共享内存段的访问。它还提供了一种方法,让单个线程或进程锁定内存以进行独占访问。

当使用这个类时,请注意以下平台差异:

  • Windows: QSharedMemory不“拥有”共享内存段。当有QSharedMemory实例附加到特定共享内存段的所有线程或进程销毁了它们的QSharedMemory实例或者退出了,Windows内核会自动释放共享内存段。
  • Unix: QSharedMemory“拥有”共享内存段。当最后一个线程或进程将一个QSharedMemory实例附加到一个特定的共享内存段时,通过销毁它的QSharedMemory实例从这个段中分离出来,Unix内核释放这个共享内存段。但是如果最后一个线程或进程在没有运行QSharedMemory析构函数的情况下崩溃了(未释放),共享内存段会在崩溃时幸存下来。

在对共享内存进行读写操作之前,记得使用lock()锁定共享内存,并且记得在操作完成后使用unlock()释放锁。

它的构造函数如下所示:

QSharedMemory(const QString &key, QObject *parent = nullptr)

通过指定的parent和key,构造一个共享内存对象。由于key已经被设置,所以不需要再调用setKey()或setNativeKey()指定要访问的内存块了,create()和attach()就可以被调用了。

其它常用函数如下所示:

QString QSharedMemory::key() const //返回被setKey()所赋值的key如果未设置key则返回空字符串,如果应用程序是和非Qt平台的,则需要使用nativeKey()访问 bool QSharedMemory::lock() //用来锁定共享内存的互斥值,锁住成功则返回true. 若另一个进程已经锁住了共享内存段,本函数将会阻塞直到锁被另一个进程释放。到那时,本函数才会获得锁并返回true. 如果本函数返回false,那就说明你已经忽略了一个由create()或attach()返回的false,而其原因可能是由于某个系统错误而导致setNativeKey()或QSystemSemaphore::acquire()失败。 bool QSharedMemory::unlock() //释放共享内存段上的锁并返回true,如果共享内存段没有被lock,或者如果锁被其他进程持有,本函数什么都不会做而只是返回false. bool QSharedMemory::create(int size, AccessMode mode=ReadWrite) //创建一个大小为size个字节的共享内存段, 然后用给定的访问模式mode附着到共享内存上。Mode取值有以下几种: QSharedMemory::ReadOnly : 共享内存段是只读的。不允许写入共享内存段。尝试写入使用ReadOnly创建的共享内存段会导致程序中止。 QSharedMemory::ReadWrite : 允许对共享内存段进行读写操作。 bool QSharedMemory::attach(AccessMode mode = ReadWrite) //尝试将进程附加到由传递给构造函数或调用setKey()或setNativeKey()的键标识的共享内存段上。默认访问模式为“ReadWrite”。也可以是ReadOnly。如果附加操作成功,则返回true。如果返回false,则调用error()或者errorString()来确定发生了哪个错误。在附加共享内存段成功之后,则可以通过调用data()来获得一个指向共享内存的指针。 bool QSharedMemory::isAttached() const //如果该进程附加到共享内存段,则返回true。 bool QSharedMemory::detach() //将进程从共享内存段中分离。如果这是连接到共享内存段的最后一个进程,那么共享内存段将被系统释放,也就是说,内容将被销毁。如果函数分离了共享内存段,则返回true。如果它返回false,通常意味着该段没有连接,或者被另一个进程锁定。 void * QSharedMemory::data() //如果附加了共享内存段,则返回指向共享内存段内容的指针。否则返回null。在对共享内存进行读写操作之前,记得使用lock()锁定共享内存,并且记得在操作完成后使用unlock()释放锁。

2.QSharedMemory示例

界面如下所示(左边写入字符串、右边实时更新):

写内存widget-示例代码如下所示:

#include "widget.h" #include "ui_widget.h" #include <QDebug> #include <QTextEdit> #include <QByteArray> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget), sharememory("shareTest", this) { ui->setupUi(this); } Widget::~Widget() { delete ui; } void Widget::on_textEdit_textChanged() { if(sharememory.isAttached()) { sharememory.detach(); } QByteArray arr = ui->textEdit->toPlainText().toLocal8Bit(); int len = arr.length(); qDebug()<<ui->textEdit->toPlainText()<<len; if(!sharememory.create(len)) { qDebug() << sharememory.errorString(); return ; } sharememory.lock(); char *dest = reinterpret_cast<char *>(sharememory.data()); for (int i = 0; i < len; i++) { dest[i] = arr[i]; } sharememory.unlock(); }

读内存widget-示例代码如下所示:

#include "widget.h" #include "ui_widget.h" #include <QDebug> #include <QTextEdit> #include <QTimer> #include <QByteArray> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget), sharememory("shareTest", this) { ui->setupUi(this); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); timer->start(200); } void Widget::timeout() { if (!sharememory.isAttached()) { if (!sharememory.attach()) { qDebug() << sharememory.errorString(); } else { sharememory.lock(); QByteArray arr((char*)sharememory.data(), sharememory.size()); ui->textEdit->setPlainText(QString(arr)); sharememory.unlock(); sharememory.detach(); } } } Widget::~Widget() { delete ui; }

3.Linux下查看共享内存

1、查看共享内存,使用命令:ipcs -m

2、删除共享内存,使用命令:ipcrm -m [shmid]

如下图所示:

1、key:共享内存的key

2、shmid:共享内存的编号

3、owner:共享内存的创建用户

4、perms:共享内存的权限

5、bytes:共享内存的大小

6、nattch:连接到共享内存的进程数

7、status:共享内存的状态

4.QSharedMemory与其他语言进程通信

如果使用QSharedMemory与非QT程序通信,就没法使用setKey()来设置共享内存名称了,需要使用setNativeKey()设置名称和nativeKey()访问。

注意:使用setNativeKey()后,QSharedMemory的lock()和unlock()函数也会失效。

接下来我们来实现一个主进程Master示例来模拟非Qt的程序:

并且需要和子进程维护协议如下所示:

4.1 主进程发送区

发送区用于主进程下发协议用的,当主进程填入一个完整的json文本给子进程后,子进程接收到就会去处理json文本,并把发送区buff[0]=’\0’进行清空处理。
如果发送区填入的是一个非完整的json文本,比如”12345”, 子进程不会做任何清空处理。

4.2 主进程接收区

接收区用于主进程接收数据用的,主进程需要主动去读取该接收区内容,当主进程通过发送区发送一段json文本后,如果json文本有返回内容,那么就要开启定时器去轮询读取子进程返回的数据内容。如果读取到内容,主进程可以通过buff[0]=’\0’进行清空处理

4.3 心跳区

用于检测子进程是否启动成功,并检测子进程是否正常运行
当主进程启动子进程并且参数格式正确后,子进程会立即更新心跳区内容为一个正整数unsigned int数字,然后会定时1S去进行数字加1(从0递增),并更新内容中的数字。
提示:

  • 主进程可以启动子进程后,去定时轮询心跳区内容,如果更新为一个数字说明启动子进程成功,如果超过5秒都没内容,说明启动进程失败,即可弹窗通知报错等操作。
  • 当检测到启动成功后,可以定时去读取心跳区内容是否每隔2秒进行更新,如果长时间未更新说明子进程挂掉或通信失败、需要杀掉所有同名子进程,然后重新启动一个新的子进程来进行交互处理。

4.4 主进程核心代码

主进程头文件成员如下所示:

QList<QSharedMemory*> _memList; QSharedMemory _memTx; QSharedMemory _memRx; QSharedMemory _memHeartbeat; unsigned int _heart{999};

主进程示例cpp代码如下所示:

#include "widget.h" #include "ui_widget.h" #include <QDebug> #include <QMessageBox> #include <QTimer> #include <QProcess> #include <QJsonDocument> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) , _memTx(this) , _memRx(this) , _memHeartbeat(this) { ui->setupUi(this); _memTx.setNativeKey("_memTxTest"); _memRx.setNativeKey("_memRxTest"); _memHeartbeat.setNativeKey("_memHeartbeatTest"); qDebug()<<"_memTx:"<<_memTx.nativeKey(); _memList = {&_memTx, &_memRx, &_memHeartbeat}; int size = 4096; for(int i = 0; i < _memList.length();i++) { if(_memList[i]->isAttached()) { // 如果已存在,则先尝试分离,再重新创建,这是一种更健壮的做法 _memList[i]->detach(); } if (!_memList[i]->create(size)) { qDebug() << "创建共享内存失败:" << _memList[i]->errorString(); QTimer::singleShot(2000, this, [&]{ QMessageBox::warning(NULL,"创建共享内存失败", "创建共享内存失败",QMessageBox::Ok); QApplication::instance()->quit(); }); return; } qDebug() << "共享内存创建成功,大小:" << _memList[i]->size(); } QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, [&]{ if (!_memRx.isAttached()) { if (!_memRx.attach()) { qDebug() << "attach _memRx.errorString:" << _memRx.errorString(); } } if(_memRx.isAttached()) { _memRx.lock(); QByteArray arr((char*)_memRx.data()); if(arr.length()) { // 处理主进程接收区域内容,处理完后 dest[0] = '\0' 清空处理 qDebug()<<"_memRx arr"<<arr.length(); ui->_textRecv->setPlainText(QString(arr)); QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(arr, &error); if(error.error == QJsonParseError::NoError) { char *dest = reinterpret_cast<char *>(_memRx.data()); dest[0] = '\0'; } qDebug()<<"recv _memRx:"<<arr.length()<<arr; } _memRx.unlock(); } if (!_memHeartbeat.isAttached()) { if (!_memHeartbeat.attach()) { qDebug() << "attach _memHeartbeat.errorString:" << _memHeartbeat.errorString(); } } if(_memHeartbeat.isAttached()) { _memHeartbeat.lock(); QByteArray memHeartbeat((char*)_memHeartbeat.data()); if(memHeartbeat.length()) { // 处理心跳值 uint data = QString(memHeartbeat).toUInt(); if(data != _heart) { ui->_textHeartbeat->setPlainText(QString(memHeartbeat)); qDebug()<<"recv _memHeartbeat:"<<memHeartbeat.length()<<memHeartbeat; _heart = data; } } _memHeartbeat.unlock(); } }); timer->start(200); } Widget::~Widget() { delete ui; } void Widget::on_pushButton_2_clicked() // 打开子进程 按钮 { QProcess* m_curProcess = new QProcess(this); m_curProcess->start(QCoreApplication::applicationDirPath() + "/" + ui->lineExeName->text(), { _memTx.nativeKey() ,_memRx.nativeKey() ,_memHeartbeat.nativeKey() }); } void Widget::on_pushButton_clicked() // 发送 按钮(下发通信) { QByteArray arr = ui->_textSend->toPlainText().toUtf8(); int len = arr.length(); if(!len) return; _memTx.lock(); char *dest = reinterpret_cast<char *>(_memTx.data()); strcpy(dest, arr); _memTx.unlock(); } void Widget::on_pushButton_3_clicked() // 读回发送区域按钮,读取发送区域,判断子进程是否处理完成 { char *dest = reinterpret_cast<char *>(_memTx.data()); qDebug()<<"读回发送区域:"<<dest; ui->_textSend->setPlainText(dest); _memTx.unlock(); }

4.5 子进程核心代码

头文件相关成员和函数如下所示:

void iniMemorys(QString masterSend, QString masterRecv, QString heartbeat); void sendMaster(QVariantMap map); void handlerMasterData(QVariantMap map); QSharedMemory _masterTx; QSharedMemory _masterRx; QSharedMemory _memHeartbeat; unsigned int _memHeartbeatCnt; // 心跳值

cpp相关函数实现如下所示:

void ShareMemoryComm::iniMemorys(QString masterSend, QString masterRecv, QString heartbeat) { _masterTx.setNativeKey(masterSend); _masterRx.setNativeKey(masterRecv); _memHeartbeat.setNativeKey(heartbeat); _memHeartbeatCnt = 0; QTimer *heartbeatTimer = new QTimer(this); connect(heartbeatTimer, &QTimer::timeout, this, &ShareMemoryComm::heartbeatTimeout); heartbeatTimeout(); // 立即发一次心跳,告诉主进程启动成功并通信正常 heartbeatTimer->setInterval(1000); heartbeatTimer->start(); QTimer *masterTxTimer = new QTimer(this); connect(masterTxTimer, &QTimer::timeout, this, [&]{ if (!_masterTx.isAttached()) { if (!_masterTx.attach()) { qDebug() << "attach _masterTx.errorString:" << _masterTx.errorString(); return; } } _masterTx.lock(); QByteArray arr((char*)_masterTx.data()); if(arr.length()) { qDebug()<<"_masterTx 接收内容:"<<arr.length()<<arr.left(40); QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(arr, &error); if(error.error == QJsonParseError::NoError) { handlerMasterData(doc.toVariant().toMap()); char *dest = reinterpret_cast<char *>(_masterTx.data()); dest[0] = '\0'; } } _masterTx.unlock(); }); masterTxTimer->setInterval(200); masterTxTimer->start(); } void ShareMemoryComm::heartbeatTimeout() { if (!_memHeartbeat.isAttached()) { if (!_memHeartbeat.attach()) { qDebug() << "attach _memHeartbeat.errorString:" << _memHeartbeat.errorString(); return; } } _memHeartbeat.lock(); QByteArray arr = QString::number(_memHeartbeatCnt++).toUtf8(); char *dest = reinterpret_cast<char *>(_memHeartbeat.data()); strcpy(dest, arr); qDebug()<<"_memHeartbeatCnt:"<<_memHeartbeatCnt; _memHeartbeat.unlock(); } void ShareMemoryComm::handlerMasterData(QVariantMap map) { if(!map.contains("type")) return; QString type = map["type"].toString(); if(type == "messageBox") { QString title = map["title"].toString(); QString text = map["text"].toString(); map["success"] = true; map["message"] = ""; sendMaster(map); Backend::instance()->messageBoxHintShow(title, text); } } void ShareMemoryComm::sendMaster(QVariantMap map) { QByteArray arr = QJsonDocument::fromVariant(map).toJson(QJsonDocument::Compact); if (!_masterRx.isAttached()) { if (!_masterRx.attach()) { qDebug() << "attach _masterRx.errorString:" << _masterRx.errorString(); return; } } _masterRx.lock(); if(_masterRx.size() > arr.size()) { char *dest = reinterpret_cast<char *>(_masterRx.data()); strcpy(dest, arr); } _masterRx.unlock(); }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/18 13:10:09

加密音乐打不开?3个问题带你彻底搞懂

加密音乐打不开&#xff1f;3个问题带你彻底搞懂 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库&#xff1a; 1. https://github.com/unlock-music/unlock-music &#xff1b;2. https://git.unlock-music.dev/um/web 项目地址: https://gitcode.com/g…

作者头像 李华
网站建设 2026/8/18 13:09:52

Java设计模式---代理模式

引言 在软件设计领域&#xff0c;代理模式&#xff08;Proxy Pattern&#xff09;作为一种经典的结构型设计模式&#xff0c;其核心思想是在客户端与目标对象之间引入一个代理对象&#xff0c;以控制对目标对象的访问。这种模式不仅能够在不修改原有代码的基础上增强功能&…

作者头像 李华
网站建设 2026/8/18 13:09:28

第 4 篇:「当数据库坏了」— 优雅降级与七层防守

开场:系统设计的最高考验 现实很残酷:没有什么系统永远可用。 SQLite 可能失败的方式有很多: ❌ 磁盘满了 → 无法写索引 ❌ 权限拒绝 → 无法创建 .loopagent 目录 ❌ 文件锁冲突 → 5 秒超时 ❌ Writer 太慢 → Reader 等待中... ❌ 数据库损坏 → 打开时 SQLITE_CORRU…

作者头像 李华
网站建设 2026/8/18 13:08:50

AI智能体开发指南:企业如何构建智能体系统

什么是AI智能体 听到智能体这个词的诸多之人, 其首要反应乃是科幻电影里的机器人, 可实际上并非那般玄奥可疑。 要说企业AI智能体, 它可不是那种你问一句它就答一句的聊天机器人, 它是一个能自主做成任务的AI程序, 是一个会自己思索、会自己谋划、会自己施行复杂任务的智能系统…

作者头像 李华
网站建设 2026/8/18 13:06:02

nxdumptool 终极指南:3 步完成 Switch 卡带与数字游戏的完整备份

nxdumptool 终极指南&#xff1a;3 步完成 Switch 卡带与数字游戏的完整备份 【免费下载链接】nxdumptool Generates XCI/NSP/HFS0/ExeFS/RomFS/Certificate/Ticket dumps from Nintendo Switch gamecards and installed SD/eMMC titles. 项目地址: https://gitcode.com/gh_m…

作者头像 李华