Qt进程间通信

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();
}
相关推荐
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner11 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner11 天前
DicomViewer (添加模型类)3
qt
xcyxiner12 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00614 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术14 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript