IPC: 共有メモリー

共有メモリ IPC メカニズムを使用して、異なるプロセス間で画像データを共有する方法を説明します。

Shared Memory の例では、QSharedMemory クラスを使用して、共有メモリを使用したプロセス間通信を実装する方法を示します。このサンプルをビルドするには、makeを実行します。サンプルを実行するには、実行可能ファイルのインスタンスを 2 つ起動します。main()関数はapplication 、この例のDialogクラスのインスタンスを作成します。ダイアログが表示され、標準的な方法でアプリケーションに制御が渡されます。

int main(int argc, char *argv[])
{
    QApplication application(argc, argv);
    Dialog dialog;
    dialog.show();
    return application.exec();
}

Dialogクラスのインスタンスが2つ現れます。

Screenshot of the Shared Memory example

DialogクラスはQDialog を継承しています。QSharedMemoryまた、ダイアログ上の2つのボタンに対応するloadFromFile()とloadFromMemory()という2つのパブリック・スロットを持ちます。

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だった場合、最初のダイアログはこのようになります。

Screenshot of the Shared Memory example

つ目のダイアログで、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 デストラクタを呼び出し、共有メモリセグメントから切り離します。これがセグメントから切り離される最後のプロセスなので、オペレーティングシステムは共有メモリーを解放します。

プロジェクト例 @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。