SharedMemory 概述
QSharedMemory 是 Qt 提供的共享内存类,用于在多个进程间共享数据。它是 Qt 进程间通信(IPC)机制之一,特别适合大量数据的快速共享。
1. 主要特点:
-
跨平台:支持 Windows、Linux、macOS 等
-
高效:直接内存访问,无需序列化/反序列化
-
容量大:适合传输图像、视频帧等大型数据
-
进程隔离:不同进程可以安全访问同一内存区域
2. 核心功能
2.1 基本操作
// 创建共享内存段
QSharedMemory shared("MySharedMemory");
shared.create(1024); // 创建1KB大小的共享内存
// 附加到现有共享内存
QSharedMemory shared("MySharedMemory");
shared.attach();
// 分离共享内存
shared.detach();
// 检查是否已附加
if(shared.isAttached()) {
// 已附加到共享内存
}
2.2 数据访问
// 写入数据
shared.lock();
char *to = (char*)shared.data();
strcpy(to, "Hello Shared Memory");
shared.unlock();
// 读取数据
shared.lock();
const char *from = (const char*)shared.constData();
qDebug() << "Data:" << from;
shared.unlock();
3. 关键方法详解
3.1 create(size_t size, AccessMode mode = ReadWrite)
-
创建大小为
size
字节的新共享内存段 -
mode
指定访问模式:QSharedMemory::ReadOnly
或QSharedMemory::ReadWrite
-
返回
true
表示成功,false
表示失败
3.2 attach(AccessMode mode = ReadWrite)
-
附加到已存在的共享内存段
-
必须调用成功后才能访问数据
3.3 detach()
-
从共享内存段分离
-
不会删除共享内存段,只是断开当前进程的连接
3.4 lock() / unlock()
-
提供对共享内存的互斥访问
-
必须成对使用,确保线程安全
3.5 data() / constData()
-
返回指向共享内存数据的指针
-
data()
可读写,constData()
只读
4. 错误处理
QSharedMemory 提供错误检测机制:
if(!shared.create(1024)) {
switch(shared.error()) {
case QSharedMemory::AlreadyExists:
qDebug() << "共享内存已存在";
break;
case QSharedMemory::NotFound:
qDebug() << "共享内存不存在";
break;
case QSharedMemory::LockError:
qDebug() << "锁定失败";
break;
case QSharedMemory::OutOfResources:
qDebug() << "资源不足";
break;
case QSharedMemory::PermissionDenied:
qDebug() << "权限不足";
break;
case QSharedMemory::SizeError:
qDebug() << "大小错误";
break;
case QSharedMemory::UnknownError:
default:
qDebug() << "未知错误";
}
}
5. 实际应用示例
5.1 进程间通信模型
// 写入进程
QSharedMemory writer("SharedData");
if(writer.create(1024)) {
writer.lock();
sprintf((char*)writer.data(), "Process 1: %d", QDateTime::currentSecsSinceEpoch());
writer.unlock();
}
// 读取进程
QSharedMemory reader("SharedData");
if(reader.attach()) {
reader.lock();
qDebug() << "Received:" << (const char*)reader.constData();
reader.unlock();
reader.detach();
}
5.2 图像传输优化
// 高效传输图像结构
struct SharedImage {
int width;
int height;
QImage::Format format;
uchar pixels[1]; // 柔性数组
};
// 创建共享内存
QSharedMemory shared("SharedImage");
int size = sizeof(SharedImage) + image.byteCount();
if(shared.create(size)) {
shared.lock();
SharedImage* sharedImg = (SharedImage*)shared.data();
sharedImg->width = image.width();
sharedImg->height = image.height();
sharedImg->format = image.format();
memcpy(sharedImg->pixels, image.constBits(), image.byteCount());
shared.unlock();
}
6. 高级主题
6.1 平台差异
特性 | Windows | Unix-like 系统 |
---|---|---|
实现方式 | 内存映射文件 | System V 或 POSIX 共享内存 |
生命周期 | 随最后一个进程分离而销毁 | 显式删除或系统重启 |
性能 | 稍慢 | 更快 |
大小限制 | 受虚拟内存限制 | 通常有系统级限制 |
6.2 同步机制
推荐使用 QSystemSemaphore 进行进程同步:
QSystemSemaphore sem("SharedMemSem", 1, QSystemSemaphore::Create);
// 写入进程
sem.acquire();
// 写入共享内存
sem.release();
// 读取进程
sem.acquire();
// 读取共享内存
sem.release();
6.3 内存管理最佳实践
-
及时释放:不再使用时调用 detach()
-
错误恢复:处理 attach/create 失败的情况
-
大小估算:准确计算所需内存大小
-
原子操作:确保读写操作的原子性
-
清理机制:提供共享内存清理接口
7. 常见问题
7.1 共享内存残留问题
// 清理共享内存
QSharedMemory shared("OrphanedMemory");
if(shared.attach()) {
shared.detach();
if(shared.create(1)) {
shared.detach();
}
}
7.2 大小不足问题
// 动态调整共享内存大小
QSharedMemory shared("DynamicMemory");
if(shared.attach()) {
if(shared.size() < requiredSize) {
shared.detach();
if(!shared.create(requiredSize)) {
// 处理错误
}
}
}
7.3 多进程竞争问题
使用双重检查锁定模式:
QSystemSemaphore initSem("InitSem", 1, QSystemSemaphore::Create);
QSharedMemory shared("CompetitiveMemory");
initSem.acquire();
if(!shared.attach()) {
if(!shared.create(1024)) {
initSem.release();
// 处理错误
}
}
initSem.release();
8. 性能优化技巧
-
批量传输:减少锁操作频率
-
内存复用:重用共享内存段
-
数据压缩:传输前压缩大型数据
-
环形缓冲区:实现高效循环读写
-
零拷贝技术:直接操作内存数据
封装图像共享类
下面将封装一个完整的 ImageSharedMemory
类,用于在进程间高效传输图像数据,包含错误处理、同步机制和内存管理。
头文件 (imagesharedmemory.h)
#ifndef IMAGESHAREDMEMORY_H
#define IMAGESHAREDMEMORY_H
#include <QObject>
#include <QSharedMemory>
#include <QImage>
#include <QSystemSemaphore>
class ImageSharedMemory : public QObject
{
Q_OBJECT
public:
explicit ImageSharedMemory(const QString &memoryKey,
const QString &writeSemKey = "",
const QString &readSemKey = "",
QObject *parent = nullptr);
~ImageSharedMemory();
bool writeImage(const QImage &image);
QImage readImage();
bool isAttached() const;
QString lastError() const;
// 设置/获取压缩格式和质量
void setImageFormat(const QString &format, int quality = -1);
QString imageFormat() const;
int imageQuality() const;
signals:
void errorOccurred(const QString &error);
private:
bool initializeSharedMemory(qint64 requiredSize);
void cleanup();
QSharedMemory m_sharedMemory;
QSystemSemaphore m_writeSemaphore;
QSystemSemaphore m_readSemaphore;
QString m_memoryKey;
QString m_imageFormat;
int m_imageQuality;
QString m_lastError;
bool m_useSemaphores;
};
#endif // IMAGESHAREDMEMORY_H
实现文件 (imagesharedmemory.cpp)
#include "imagesharedmemory.h"
#include <QBuffer>
#include <QDebug>
ImageSharedMemory::ImageSharedMemory(const QString &memoryKey,
const QString &writeSemKey,
const QString &readSemKey,
QObject *parent)
: QObject(parent),
m_memoryKey(memoryKey),
m_sharedMemory(memoryKey),
m_writeSemaphore(writeSemKey, 1, QSystemSemaphore::Create),
m_readSemaphore(readSemKey, 0, QSystemSemaphore::Create),
m_imageFormat("PNG"),
m_imageQuality(-1),
m_useSemaphores(!writeSemKey.isEmpty() && !readSemKey.isEmpty())
{
}
ImageSharedMemory::~ImageSharedMemory()
{
cleanup();
}
bool ImageSharedMemory::writeImage(const QImage &image)
{
if (m_useSemaphores) {
m_writeSemaphore.acquire();
}
// 将图像转换为字节数组
QByteArray byteArray;
QBuffer buffer(&byteArray);
buffer.open(QIODevice::WriteOnly);
if (!image.save(&buffer, m_imageFormat.toLatin1(), m_imageQuality)) {
m_lastError = "Failed to convert image to byte array";
emit errorOccurred(m_lastError);
if (m_useSemaphores) m_writeSemaphore.release();
return false;
}
// 计算所需内存大小 (数据大小 + 4字节长度前缀)
const qint64 requiredSize = byteArray.size() + sizeof(quint32);
// 初始化共享内存
if (!initializeSharedMemory(requiredSize)) {
if (m_useSemaphores) m_writeSemaphore.release();
return false;
}
// 写入数据
m_sharedMemory.lock();
try {
char *to = static_cast<char*>(m_sharedMemory.data());
// 首先写入数据长度
const quint32 size = byteArray.size();
memcpy(to, &size, sizeof(quint32));
// 然后写入图像数据
memcpy(to + sizeof(quint32), byteArray.constData(), size);
} catch (...) {
m_sharedMemory.unlock();
m_lastError = "Memory copy failed";
emit errorOccurred(m_lastError);
if (m_useSemaphores) m_writeSemaphore.release();
return false;
}
m_sharedMemory.unlock();
if (m_useSemaphores) {
m_readSemaphore.release();
}
return true;
}
QImage ImageSharedMemory::readImage()
{
if (m_useSemaphores) {
m_readSemaphore.acquire();
}
// 附加到共享内存
if (!m_sharedMemory.attach(QSharedMemory::ReadOnly)) {
m_lastError = QString("Attach failed: %1").arg(m_sharedMemory.errorString());
emit errorOccurred(m_lastError);
if (m_useSemaphores) m_readSemaphore.release();
return QImage();
}
QImage image;
m_sharedMemory.lock();
try {
const char *from = static_cast<const char*>(m_sharedMemory.constData());
// 读取数据长度
quint32 size;
memcpy(&size, from, sizeof(quint32));
// 读取图像数据
QByteArray byteArray = QByteArray::fromRawData(from + sizeof(quint32), size);
if (!image.loadFromData(byteArray, m_imageFormat.toLatin1())) {
m_lastError = "Failed to load image from shared memory";
emit errorOccurred(m_lastError);
}
} catch (...) {
m_lastError = "Memory read failed";
emit errorOccurred(m_lastError);
}
m_sharedMemory.unlock();
m_sharedMemory.detach();
if (m_useSemaphores) {
m_writeSemaphore.release();
}
return image;
}
bool ImageSharedMemory::isAttached() const
{
return m_sharedMemory.isAttached();
}
QString ImageSharedMemory::lastError() const
{
return m_lastError;
}
void ImageSharedMemory::setImageFormat(const QString &format, int quality)
{
m_imageFormat = format.toUpper();
m_imageQuality = quality;
}
QString ImageSharedMemory::imageFormat() const
{
return m_imageFormat;
}
int ImageSharedMemory::imageQuality() const
{
return m_imageQuality;
}
bool ImageSharedMemory::initializeSharedMemory(qint64 requiredSize)
{
// 如果已附加但大小不足,先分离
if (m_sharedMemory.isAttached()) {
m_sharedMemory.detach();
}
// 创建新的共享内存段
if (!m_sharedMemory.create(requiredSize)) {
if (m_sharedMemory.error() == QSharedMemory::AlreadyExists) {
// 如果已存在,尝试附加
if (!m_sharedMemory.attach()) {
m_lastError = QString("Attach failed: %1").arg(m_sharedMemory.errorString());
emit errorOccurred(m_lastError);
return false;
}
// 检查大小是否足够
if (m_sharedMemory.size() < requiredSize) {
m_lastError = "Existing shared memory is too small";
emit errorOccurred(m_lastError);
return false;
}
} else {
m_lastError = QString("Create failed: %1").arg(m_sharedMemory.errorString());
emit errorOccurred(m_lastError);
return false;
}
}
return true;
}
void ImageSharedMemory::cleanup()
{
if (m_sharedMemory.isAttached()) {
m_sharedMemory.detach();
}
}
使用示例
写入进程
#include "imagesharedmemory.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 创建共享内存对象
ImageSharedMemory writer("ImageSharingDemo", "ImageWriteSem", "ImageReadSem");
writer.setImageFormat("JPEG", 80); // 使用JPEG压缩,质量80%
// 加载图像
QImage image("test.jpg");
if (image.isNull()) {
qDebug() << "Failed to load image";
return -1;
}
// 写入图像到共享内存
if (!writer.writeImage(image)) {
qDebug() << "Write failed:" << writer.lastError();
return -1;
}
qDebug() << "Image successfully written to shared memory";
return a.exec();
}
读取进程
#include "imagesharedmemory.h"
#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 创建共享内存对象
ImageSharedMemory reader("ImageSharingDemo", "ImageWriteSem", "ImageReadSem");
// 从共享内存读取图像
QImage image = reader.readImage();
if (image.isNull()) {
qDebug() << "Read failed:" << reader.lastError();
return -1;
}
// 显示图像
QLabel label;
label.setPixmap(QPixmap::fromImage(image));
label.show();
qDebug() << "Image successfully read from shared memory";
return a.exec();
}
功能特点
-
线程安全:使用信号量实现读写同步
-
灵活配置:可设置图像压缩格式和质量
-
错误处理:完善的错误检测和报告机制
-
内存管理:自动处理共享内存的创建、附加和分离
-
高效传输:使用长度前缀避免不必要的内存分配
-
跨平台:适用于Windows、Linux和macOS
高级用法
1. 传输图像元数据
可以扩展 ImageSharedMemory
类来传输额外的元数据:
// 在头文件中添加
struct ImageMetaData {
quint64 timestamp;
int sourceId;
// 其他元数据字段...
};
// 修改writeImage和readImage方法以支持元数据
2. 多图像传输
实现共享内存池来传输多个图像:
class ImageMemoryPool : public QObject
{
Q_OBJECT
public:
ImageMemoryPool(int poolSize, const QString &baseKey, QObject *parent = nullptr);
bool writeImage(int slot, const QImage &image);
QImage readImage(int slot);
// ...其他方法...
};
3. 性能优化
对于实时视频传输,可以考虑:
-
使用环形缓冲区
-
实现双缓冲或三缓冲技术
-
使用YUV格式减少数据量
-
实现零拷贝技术
替代方案比较
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
QSharedMemory | 高效,适合大数据量 | 需要同步机制 | 图像/视频传输,大数据共享 |
QLocalSocket | 简单易用 | 需要序列化,性能较低 | 结构化数据,频繁通信 |
D-Bus | 功能强大,支持远程调用 | 复杂,性能开销大 | 桌面应用,系统服务 |
QProcess | 简单,直接 | 通信能力有限 | 简单数据传递,进程控制 |
总结
QSharedMemory 是 Qt 中强大的进程间通信工具,特别适合需要高性能、大数据量传输的场景。正确使用时需要注意:
-
始终实现正确的锁定机制
-
处理所有可能的错误情况
-
设计合理的内存管理策略
-
考虑平台差异和限制
-
对于复杂场景,结合其他 IPC 机制使用
通过合理设计,QSharedMemory 可以成为实现高效进程间通信的利器。