IPC : Mémoire partagée

Démontre comment partager des données d'image entre différents processus à l'aide du mécanisme IPC de la mémoire partagée.

L'exemple Mémoire partagée montre comment utiliser la classe QSharedMemory pour mettre en œuvre la communication inter-processus à l'aide de la mémoire partagée. Pour créer l'exemple, exécutez make. Pour exécuter l'exemple, démarrez deux instances de l'exécutable. La fonction main() crée un site application et une instance de la classe Dialog de notre exemple. La boîte de dialogue est affichée, puis le contrôle est transmis à l'application de manière standard.

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

Deux instances de la classe Dialog apparaissent.

Capture d'écran de l'exemple de la mémoire partagée

La classe Dialog hérite de QDialog. Elle encapsule l'interface utilisateur et une instance de QSharedMemory. Elle possède également deux slots publics, loadFromFile() et loadFromMemory() qui correspondent aux deux boutons de la boîte de dialogue.

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;
};

Le constructeur crée les widgets de l'interface utilisateur et connecte le signal clicked() de chaque bouton à la fonction slot correspondante.

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"));
}

Notez que "QSharedMemoryExample" est transmis au constructeur de QSharedMemory() pour être utilisé comme clé. Celle-ci sera utilisée par le système comme identifiant du segment de mémoire partagée sous-jacent.

Cliquez sur le bouton Load Image From File... dans l'une des boîtes de dialogue. Le slot loadFromFile() est invoqué. Il teste tout d'abord si un segment de mémoire partagée est déjà attaché au processus. Si c'est le cas, ce segment est détaché du processus, ce qui nous permet de démarrer l'exemple correctement.

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));

L'utilisateur est ensuite invité à sélectionner un fichier image à l'aide de QFileDialog::getOpenFileName(). Le fichier sélectionné est chargé dans un fichier QImage. L'utilisation d'un fichier QImage nous permet de nous assurer que le fichier sélectionné est une image valide et d'afficher immédiatement l'image dans la boîte de dialogue à l'aide de setPixmap().

Ensuite, l'image est transférée dans un QBuffer à l'aide d'un QDataStream, ce qui nous donne la taille, que nous utilisons ensuite pour create() notre segment de mémoire partagée. La création d'un segment de mémoire partagée entraîne automatiquement l'attribution de attaches au processus. L'utilisation de QBuffer nous permet d'obtenir un pointeur sur les données de l'image, que nous utilisons ensuite pour effectuer un memcopy() de QBuffer dans le segment de mémoire partagée.

    // 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();
}

Notez que nous lock() le segment de mémoire partagée avant de copier dedans, et nous unlock() à nouveau immédiatement après la copie. Cela garantit que nous avons un accès exclusif au segment de mémoire partagée pour effectuer notre memcopy(). Si un autre processus possède le verrou du segment, notre processus se bloquera jusqu'à ce que le verrou devienne disponible.

Notez également que la fonction ne fait pas detach() du segment de mémoire partagée après memcopy() et unlock(). Rappelons que lorsque le dernier processus se détache d'un segment de mémoire partagée, le segment est libéré par le système d'exploitation. Puisque ce processus est le seul à être attaché au segment de mémoire partagée en ce moment, si loadFromFile() se détache du segment de mémoire partagée, le segment sera détruit avant que nous ne passions à l'étape suivante.

Lorsque la fonction revient, si le fichier sélectionné était qt.png, la première boîte de dialogue ressemble à ceci.

Capture d'écran de l'exemple de la mémoire partagée

Dans la deuxième boîte de dialogue, cliquez sur le bouton Display Image From Shared Memory. Le slot loadFromMemory() est invoqué. Il commence par attaches le processus vers le même segment de mémoire partagée créé par le premier processus. Ensuite, il locks le segment pour un accès exclusif et lie un QBuffer aux données de l'image dans le segment de mémoire partagée. Elle transmet ensuite les données à QImage et à unlocks le segment.

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));
}

Dans ce cas, la fonction fait detach() à partir du segment, car nous avons fini de l'utiliser. Enfin, la page QImage est affichée. À ce stade, les deux boîtes de dialogue doivent afficher la même image. Lorsque vous fermez la première boîte de dialogue, le destructeur Dialog appelle le destructeur QSharedMemory, qui se détache du segment de mémoire partagée. Comme il s'agit du dernier processus à être détaché du segment, le système d'exploitation va maintenant libérer la mémoire partagée.

Exemple de projet @ 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.