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();
}
相关推荐
艾莉丝努力练剑2 小时前
确保多进程命名管道权限一致的方法
java·linux·运维·服务器·开发语言·网络·c++
2201_756206342 小时前
1111111
开发语言·python
一瓢西湖水2 小时前
CPU使用超过阈值分析
java·开发语言·jvm
特种加菲猫2 小时前
透过源码看本质:list 的模拟实现与核心原理
开发语言·c++
李日灐2 小时前
改造红黑树实现封装 map/set:感受C++ 标准容器的精妙设计与底层实现
开发语言·数据结构·c++·后端·算法·红黑树
故事和你912 小时前
sdut-程序设计基础Ⅰ-期末测试(重现)
大数据·开发语言·数据结构·c++·算法·蓝桥杯·图论
重庆兔巴哥2 小时前
如何在Dev-C++中使用MinGW-w64编译器?
linux·开发语言·c++
魔道不误砍柴功2 小时前
Java Function 高级使用技巧:从工程实战中来
java·开发语言·python
三佛科技-187366133972 小时前
LP3783A芯茂微5V2.1A低功耗原边反馈充电器芯片替代PL3378/C
c语言·开发语言