IPC: Memoria Compartida

Demuestra cómo compartir datos de imagen entre diferentes procesos utilizando el mecanismo IPC de Memoria Compartida.

El ejemplo de Memoria Compartida muestra cómo utilizar la clase QSharedMemory para implementar la comunicación entre procesos utilizando memoria compartida. Para construir el ejemplo, ejecute make. Para ejecutar el ejemplo, inicie dos instancias del ejecutable. La función main() crea un application y una instancia de la clase Dialog de nuestro ejemplo. Se muestra el diálogo y luego se pasa el control a la aplicación de la forma estándar.

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

Aparecen dos instancias de la clase Dialog.

Captura de pantalla del ejemplo de memoria compartida

La clase Dialog hereda de QDialog. Encapsula la interfaz de usuario y una instancia de QSharedMemory. También tiene dos ranuras públicas, loadFromFile() y loadFromMemory() que corresponden a los dos botones del diálogo.

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

El constructor construye los widgets de la interfaz de usuario y conecta la señal clicked() de cada botón a la función del slot correspondiente.

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

Nótese que "QSharedMemoryExample" se pasa al constructor QSharedMemory() para ser utilizado como clave. Esto será utilizado por el sistema como el identificador del segmento de memoria compartida subyacente.

Pulse el botón Load Image From File... en uno de los cuadros de diálogo. Se invoca la ranura loadFromFile(). Primero, comprueba si un segmento de memoria compartida ya está unido al proceso. Si es así, ese segmento se separa del proceso, por lo que podemos estar seguros de iniciar el ejemplo correctamente.

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

A continuación, se pide al usuario que seleccione un archivo de imagen utilizando QFileDialog::getOpenFileName(). El archivo seleccionado se carga en un QImage. Usar un QImage nos permite asegurarnos de que el archivo seleccionado es una imagen válida, y también nos permite mostrar inmediatamente la imagen en el diálogo usando setPixmap().

A continuación, la imagen se transmite a un QBuffer utilizando un QDataStream. Esto nos da el tamaño, que luego utilizamos para create() nuestro segmento de memoria compartida. La creación de un segmento de memoria compartida automáticamente attaches el segmento al proceso. Usando un QBuffer aquí nos permite obtener un puntero a los datos de la imagen, que luego usamos para hacer un memcopy() desde el QBuffer al segmento de memoria compartida.

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

Tenga en cuenta que lock() el segmento de memoria compartida antes de copiar en él, y unlock() de nuevo inmediatamente después de la copia. Esto asegura que tenemos acceso exclusivo al segmento de memoria compartida para hacer nuestra memcopy(). Si algún otro proceso tiene el segmento bloqueado, entonces nuestro proceso se bloqueará hasta que el bloqueo esté disponible.

Observe también que la función no detach() del segmento de memoria compartida después de memcopy() y unlock(). Recordemos que cuando el último proceso se separa de un segmento de memoria compartida, el segmento es liberado por el sistema operativo. Dado que este proceso es el único que está unido al segmento de memoria compartida en este momento, si loadFromFile() se separara del segmento de memoria compartida, el segmento sería destruido antes de que llegáramos al siguiente paso.

Cuando la función regresa, si el archivo que seleccionaste fue qt.png, tu primer diálogo luce así.

Captura de pantalla del ejemplo de memoria compartida

En el segundo diálogo, haga clic en el botón Display Image From Shared Memory. La ranura loadFromMemory() es invocada. Primero attaches el proceso al mismo segmento de memoria compartida creado por el primer proceso. Luego locks el segmento para acceso exclusivo y enlaza un QBuffer a los datos de la imagen en el segmento de memoria compartida. A continuación, transmite los datos a un QImage y unlocks el segmento.

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

En este caso, la función hace detach() desde el segmento, porque ahora efectivamente hemos terminado de usarlo. Finalmente, se muestra el QImage. En este punto, ambos diálogos deberían estar mostrando la misma imagen. Cuando se cierra el primer diálogo, el destructor del diálogo llama al destructor de QSharedMemory, que se separa del segmento de memoria compartida. Como este es el último proceso que se separa del segmento, el sistema operativo liberará la memoria compartida.

Proyecto de ejemplo @ 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.