IPC: 共享内存
演示如何使用共享内存 IPC 机制在不同的进程之间共享图像数据。
共享内存示例展示了如何使用 QSharedMemory 类来通过共享内存实现进程间通信。构建示例,请运行 make。运行示例,请启动两个可执行文件实例。main() 函数创建一个 application 和示例的 Dialog 类的实例。对话框显示后,控制方式以标准方式传递到应用程序。
int main(int argc, char *argv[]) { QApplication application(argc, argv); Dialog dialog; dialog.show(); return application.exec(); }
出现两个 Dialog 类的实例。
类 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() 构造函数以用作密钥。这将被系统用作底层共享内存段的标识符。
在其中一个对话框上单击 从文件加载图像...
按钮。调用 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。这给出了大小,然后我们使用它来 创建() 我们的共享内存段。创建共享内存段会自动 附加() 该段到进程。在这里使用 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(); }
请注意,我们在将其复制到其中之前 锁定() 共享内存段,并在复制后立即 解锁() 它。这确保了我们可以对共享内存段进行独占访问来执行我们的 memcopy()。如果有其他进程锁定了该段,则我们的进程将阻塞,直到锁可用。
注意,函数在执行memcopy()和unlock()后并没有detach()从共享内存段中分离。回想一下,当最后一个进程从一个共享内存段中分离时,操作系统会释放该段。因为目前这个进程是唯一连接到共享内存段的进程,如果loadFromFile()从共享内存段中分离,段将在我们到达下一步之前被销毁。
当函数返回时,如果你选择的是qt.png文件,你的第一个对话框看起来像这样。
在第二个对话框中,点击Display Image From Shared Memory
按钮。调用loadFromMemory()槽。它首先附加进程到第一个进程创建的同一共享内存段。然后它锁定该段以实现独占访问,并将一个QBuffer链接到共享内存段中的图片数据。然后它将数据流式传输到一个QImage并解锁段。
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的析构函数,从而从共享内存段中分离。由于这是最后一个从段中分离的进程,操作系统现在将释放共享内存。
© 2024 The Qt Company Ltd。本文件内包含的文档贡献属其各自的拥有者所有。本文件中提供的文档是根据GNU软件基金会发布的GNU自由文档许可证版本1.3的条款许可的。Qt和相关标志是The Qt Company Ltd在芬兰和/或其他国家/地区的商标。所有其他商标均为其各自所有者的财产。