QSharedMemory
共享内存(Shared Memory)是一种进程间通信(Inter-Process Communication, IPC)机制,允许多个进程共享同一块内存区域。共享内存提供了高效的数据交换方式,适用于需要频繁传递大量数据的场景。
Qt中的共享内存机制主要依赖于QSharedMemory类。该类提供了用于创建和管理共享内存区域的接口,允许进程在内存中创建一个共享的数据缓冲区.
使用步骤
- 创建共享内存:使用
QSharedMemory类的create()函数可以创建一个共享内存对象,并指定一个唯一的键(key)来标识共享内存区域。如果该键对应的共享内存已经存在,则会返回false。 - 分配内存:使用
QSharedMemory类的attach()函数将共享内存附加到当前进程的地址空间。 - 写入和读取数据:通过共享内存附加到的地址,进程可以直接读取或写入共享内存中的数据。可以使用任何数据结构,如数组、结构体等来组织共享的数据。
- 分离和删除共享内存:当进程不再需要访问共享内存时,可以使用
QSharedMemory类的detach()函数将共享内存从当前进程的地址空间中分离。而使用QSharedMemory类的remove()函数可以删除共享内存区域,释放相关资源。
跨平台特性差异:
- windows:内核自动回收,进程退出后内存自动释放
- linux:QSharedMemory 持有,需显式调用析构函数
示例
写入端
cpp
#include <QtCore/QCoreApplication>
#include <QSharedMemory>
#include <QDebug>
#include <QThread>
#include <qmath.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSharedMemory * shared_mm = new QSharedMemory();
shared_mm->setKey("my_shared_data");
if (!shared_mm->attach(QSharedMemory::ReadWrite)) {
shared_mm->create(20, QSharedMemory::ReadWrite);
int no = 0;
QString ret = "";
while (true)
{
no += 1;
shared_mm->lock();
ret = QString("shared no:%0").arg(no);
memset(shared_mm->data(), 0, shared_mm->size());
memcpy(shared_mm->data(), ret.toStdString().data(), qMin(ret.size(), shared_mm->size()));
shared_mm->unlock();
QThread::sleep(2);
qDebug() << ret;
}
}
else {
qDebug() << "app1 attach `my_shared_data` failed!";
}
return a.exec();
}
读取端
cpp
#include <QtCore/QCoreApplication>
#include <QSharedMemory>
#include <QDebug>
#include <QThread>
#include <qmath.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSharedMemory * shared_mm = new QSharedMemory();
shared_mm->setKey("my_shared_data");
if (!shared_mm->attach(QSharedMemory::ReadWrite)) {
shared_mm->create(20, QSharedMemory::ReadWrite);
int no = 0;
QString ret = "";
while (true)
{
no += 1;
shared_mm->lock();
ret = QString("shared no:%0").arg(no);
memset(shared_mm->data(), 0, shared_mm->size());
memcpy(shared_mm->data(), ret.toStdString().data(), qMin(ret.size(), shared_mm->size()));
shared_mm->unlock();
QThread::sleep(2);
qDebug() << ret;
}
}
else {
qDebug() << "app1 attach `my_shared_data` failed!";
}
return a.exec();
}
如果是一个进程写,多个进程读的场景,可以考虑内存分页,不同的进程读取不同的内存段
cpp
# 示例代码
const int PAGE_SIZE = 2048;// 内存页2k
int app_page=2;
memcpy(shared_mm->data()+2*PAGE_SIZE, ret.toStdString().data(), qMin(ret.size(), shared_mm->size()));
QSystemSemaphore
如果两端都需要写入,需要借助系统信号来控制读、写的时机(信号量为0时,会阻塞线程或进程)。
app1
初始化'sys_shared_semaphore1'、'sys_shared_semaphore2'两个信号量,同时启动时初始化'my_shared_data'共享存储区。
app1向共享存储区写入数据,设置sys_shared_semaphore1的信号量为1;然后等待sys_shared_semaphore2,获取到信号量后读取数据。
cpp
#include <QtCore/QCoreApplication>
#include <QSharedMemory>
#include <QDebug>
#include <QThread>
#include <QMath.h>
#include <QSystemSemaphore>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSystemSemaphore semaphore1("sys_shared_semaphore1", 0);
QSystemSemaphore semaphore2("sys_shared_semaphore2", 0);
QSharedMemory * shared_mm = new QSharedMemory();
shared_mm->setKey("my_shared_data");
char buff[20] = { 0 };
if (!shared_mm->attach(QSharedMemory::ReadWrite)) {
shared_mm->create(20, QSharedMemory::ReadWrite);
int no = 0;
QString ret = "";
while (true)
{
no += 1;
shared_mm->lock();
ret = QString("app1 no:%0").arg(no);
memset(shared_mm->data(), 0, shared_mm->size());
memcpy(shared_mm->data(), ret.toStdString().data(), qMin(ret.size(), shared_mm->size()));
shared_mm->unlock();
semaphore1.release(1);
if(semaphore2.acquire()){
memset(buff, 0, 20);
shared_mm->lock();
memcpy(&buff, shared_mm->constData(), sizeof(buff));
qDebug() << "read sharememory:" << buff;
shared_mm->unlock();
}
}
}
else {
qDebug() << "app1 attach `my_shared_data` failed!";
}
return a.exec();
}
app2
与app1差不多,启动后先附加'my_shared_data',附加成功表示app1已经启动;然后等待sys_shared_semaphore1信号,获取信号后读取数据。
app2向共享存储区写入数据后,设置sys_shared_semaphore2的信号量为1,此时app1获取信号后可以读取数据。
cpp
#include <QtCore/QCoreApplication>
#include <QSharedMemory>
#include <QDebug>
#include <QThread>
#include <QMath.h>
#include <QSystemSemaphore>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSystemSemaphore semaphore1("sys_shared_semaphore1", 0);
QSystemSemaphore semaphore2("sys_shared_semaphore2", 0);
QSharedMemory * shared_mm = new QSharedMemory();
shared_mm->setKey("my_shared_data");
while (true)
{
if (shared_mm->attach(QSharedMemory::ReadWrite)) break;
QThread::msleep(1000);
qDebug() << "wait sharememory attach...";
}int no = 0;
QString ret = "";
char buff[20] = { 0 };
while (true)
{
no += 1;
if (semaphore1.acquire()) {
memset(buff, 0, 20);
shared_mm->lock();
memcpy(&buff, shared_mm->constData(), sizeof(buff));
qDebug() << "read sharememory:" << buff;
shared_mm->unlock();
}
shared_mm->lock();
ret = QString("app2 no:%0").arg(no);
memset(shared_mm->data(), 0, shared_mm->size());
memcpy(shared_mm->data(), ret.toStdString().data(), qMin(ret.size(), shared_mm->size()));
shared_mm->unlock();
semaphore2.release(1);
}
return a.exec();
}
QtRO
Qt Remote Object (QtRO) 也是一个用于进程间通信(IPC)的模块,专门用于在不同进程或计算机之间交换信息。它在Qt 5.9之后推出,基于Socket封装,兼容LPC(Local Process Communication)和RPC(Remote Process Communication)。
为了实现远程对象调用,QtRO使用了一种基于信号和槽的机制,类似于Qt本地对象之间的通信。
qt工程中,QtRO依赖 network、remoteobjects两个模块
rep文件
rep文件中包含了通信之间定义的接口。
c++
// 文件名:app_rep_intf.rep
class AppRemoteInterface
{
SIGNAL(signalMsg(const QString &)) //server下发消息给client
SLOT(int onMsg(const QString &)) //server接收client的消息
}
server
rep_app_rep_intf_source.h 是app_rep_intf.rep生成的
.pro文件中需要添加:
txt
REPC_SOURCE += $$PWD/app_rep_intf.rep
vs 工程中需要设置app_rep_intf.rep属性:out file type:source (Source header)
c++
#pragma once
#include <QDebug>
#include "rep_app_rep_intf_source.h"
class AppRemoteInterface :public AppRemoteInterfaceSource {
Q_OBJECT
public:
AppRemoteInterface(QObject *parent = nullptr) : AppRemoteInterfaceSource() {
}
virtual int onMsg(const QString & msg);
void sendMsgToClient(const QString &msg);
signals:
void signalRevMsg(const QString &msg);
};
#include "appremoteinterface.h"
int AppRemoteInterface::onMsg(const QString & msg) {
emit signalRevMsg(msg);
return 0;
}
void AppRemoteInterface::sendMsgToClient(const QString &msg) {
emit signalMsg(msg);
}
main.cpp
c++
#include <QtCore/QCoreApplication>
#include <QRemoteObjectHost>
#include <QDebug>
#include <QTimer>
#include "appremoteinterface.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QRemoteObjectHost app_host;
app_host.setHostUrl(QUrl("local:apprmt"));
AppRemoteInterface *app_int = new AppRemoteInterface();
app_host.enableRemoting(app_int);
qDebug() << "server app running....";
QObject::connect(app_int, &AppRemoteInterface::signalRevMsg, [](const QString &msg) {
qDebug() << "server recv:" << msg;
});
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [app_int]() {
app_int->sendMsgToClient("this is server msg!");
});
timer.start(3000);
return a.exec();
}
client
rep_app_rep_intf_replica.h 是app_rep_intf.rep生成的
.pro文件中需要添加:
txt
REPC_REPLICA += app_rep_intf.rep
vs 工程中需要设置app_rep_intf.rep属性:out file type:replica (Replica header)
cpp
#include <QtCore/QCoreApplication>
#include <QRemoteObjectHost>
#include <QDebug>
#include <QTimer>
#include <QMetaObject>
#include "rep_app_rep_intf_replica.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QRemoteObjectNode app_node;
AppRemoteInterfaceReplica* app_inf = app_node.acquire<AppRemoteInterfaceReplica>();
app_node.connectToNode(QUrl("local:apprmt"));
QObject::connect(app_inf, &AppRemoteInterfaceReplica::signalMsg, [](const QString &msg) {
qDebug() << "client recv:" << msg;
});
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [app_inf]() {
if (app_inf->isReplicaValid()) {
app_inf->onMsg("this is client msg"); // 直接调用服务端的SOLT
}
});
timer.start(3000);
return a.exec();
}