IPC:共享内存
演示如何使用共享内存 IPC 机制在不同进程间共享图像数据。
共享内存示例展示了如何使用QSharedMemory 类来实现共享内存的进程间通信。要构建示例,请运行 make。要运行示例,请启动两个可执行文件实例。main()函数将创建一个application 和一个对话框类实例。显示对话框,然后按标准方式将控制权传递给应用程序。
int main(int argc, char *argv[]) { QApplication application(argc, argv); Dialog dialog; dialog.show(); return application.exec(); }
对话框类的两个实例就会出现。
Dialog 类继承于QDialog 。它封装了用户界面和QSharedMemory 的一个实例。它还有两个公共槽:loadFromFile() 和 loadFromMemory(),分别对应对话框上的两个按钮。
class Dialog : public QDialog { Q_OBJECT public: Dialog(QWidget *parent = nullptr); public slots: void loadFromFile(); void loadFromMemory(); private: void detach(); private: Ui::Dialog ui; QSharedMemory sharedMemory; };
构造函数构建用户界面部件,并将每个按钮的 clicked() 信号连接到相应的槽函数。
Dialog::Dialog(QWidget *parent) : QDialog(parent), sharedMemory(QNativeIpcKey(u"QSharedMemoryExample"_s)) { ui.setupUi(this); connect(ui.loadFromFileButton, &QPushButton::clicked, this, &Dialog::loadFromFile); connect(ui.loadFromSharedMemoryButton, &QPushButton::clicked, this, &Dialog::loadFromMemory); setWindowTitle(tr("SharedMemory Example")); }
请注意,"QSharedMemoryExample "被传递给QSharedMemory() 构造函数作为密钥使用。系统将把它用作底层共享内存段的标识符。
单击其中一个对话框上的Load Image From File...
按钮。loadFromFile() 槽将被调用。首先,它会测试进程是否已经附加了共享内存段。如果是,该段将从进程中分离,这样我们就能确保正确启动示例。
void Dialog::loadFromFile() { if (sharedMemory.isAttached()) detach(); ui.label->setText(tr("Select an image file")); QString fileName = QFileDialog::getOpenFileName(0, QString(), QString(), tr("Images (*.png *.xpm *.jpg)")); QImage image; if (!image.load(fileName)) { ui.label->setText(tr("Selected file is not an image, please select another.")); return; } ui.label->setPixmap(QPixmap::fromImage(image));
然后,使用QFileDialog::getOpenFileName() 要求用户选择一个图像文件。所选文件将加载到QImage 中。使用QImage 可以确保所选文件是有效的图像,还可以使用 setPixmap() 立即在对话框中显示图像。
接下来,我们使用QDataStream 将图像流式传输到QBuffer 中。这样我们就得到了图像的大小,然后将其用于create() 共享内存段。创建共享内存段会自动将该段attaches 给进程。通过QBuffer ,我们可以获得指向图像数据的指针,然后使用该指针将QBuffer 中的 memcopy() 复制到共享内存段中。
// load into shared memory QBuffer buffer; buffer.open(QBuffer::ReadWrite); QDataStream out(&buffer); out << image; int size = buffer.size(); if (!sharedMemory.create(size)) { if (sharedMemory.error() == QSharedMemory::AlreadyExists) { sharedMemory.attach(); } else { ui.label->setText(tr("Unable to create or attach to shared memory segment: %1") .arg(sharedMemory.errorString())); return; } } sharedMemory.lock(); char *to = (char*)sharedMemory.data(); const char *from = buffer.data().data(); memcpy(to, from, qMin(sharedMemory.size(), size)); sharedMemory.unlock(); }
请注意,在拷贝到共享内存段之前,我们会lock() 共享内存段,拷贝完成后,我们会立即再次unlock() 共享内存段。这确保了我们在执行 memcopy() 时对共享内存段的独占访问权限。如果其他进程拥有段锁,那么我们的进程就会阻塞,直到锁可用为止。
还要注意的是,在完成 memcopy() 和 unlock() 之后,函数不会从共享内存段detach() 。回想一下,当最后一个进程脱离共享内存段时,操作系统会释放该内存段。如果 loadFromFile() 从共享内存段分离,那么在进入下一步之前,共享内存段就会被销毁。
函数返回后,如果您选择的文件是 qt.png,那么第一个对话框看起来就像这样。
在第二个对话框中,单击Display Image From Shared Memory
按钮。loadFromMemory() 槽将被调用。首先,attaches 进程到第一个进程创建的共享内存段。然后,它将locks 该段作为独占访问,并将QBuffer 链接到共享内存段中的图像数据。然后将数据流导入QImage 和unlocks 段。
void Dialog::loadFromMemory() { if (!sharedMemory.attach()) { ui.label->setText(tr("Unable to attach to shared memory segment.\n" \ "Load an image first.")); return; } QBuffer buffer; QDataStream in(&buffer); QImage image; sharedMemory.lock(); buffer.setData((char*)sharedMemory.constData(), sharedMemory.size()); buffer.open(QBuffer::ReadOnly); in >> image; sharedMemory.unlock(); sharedMemory.detach(); ui.label->setPixmap(QPixmap::fromImage(image)); }
在这种情况下,函数会从段中执行detach() 操作,因为现在我们实际上已经完成了对它的使用。最后,显示QImage 。此时,两个对话框应显示相同的图像。关闭第一个对话框时,对话框析构函数会调用QSharedMemory 析构函数,该函数会从共享内存段中分离。由于这是最后一个脱离该段的进程,操作系统现在将释放共享内存。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.