IPC:共有メモリ

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

「共有メモリ」のサンプルでは、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つ表示されます。

「共有メモリ」の例のスクリーンショット

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

「共有メモリ」の例のスクリーンショット

2番目のダイアログで、「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 が表示されます。この時点で、両方のダイアログに同じ画像が表示されているはずです。最初のダイアログを閉じると、DialogのデストラクタがQSharedMemory のデストラクタを呼び出し、共有メモリセグメントから切り離されます。これがセグメントから切り離される最後のプロセスであるため、オペレーティングシステムは共有メモリを解放します。

サンプルプロジェクト @ code.qt.io

© 2026 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.