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 类的实例。

Screenshot of the Shared Memory example

类 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文件,你的第一个对话框看起来像这样。

Screenshot of the Shared Memory example

在第二个对话框中,点击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的析构函数,从而从共享内存段中分离。由于这是最后一个从段中分离的进程,操作系统现在将释放共享内存。

示例项目 @ code.qt.io

© 2024 The Qt Company Ltd。本文件内包含的文档贡献属其各自的拥有者所有。本文件中提供的文档是根据GNU软件基金会发布的GNU自由文档许可证版本1.3的条款许可的。Qt和相关标志是The Qt Company Ltd在芬兰和/或其他国家/地区的商标。所有其他商标均为其各自所有者的财产。