Qt Using Shared Memory with Different Processes and Shared Libraries

Qt Using Shared Memory with Different Processes and Shared Libraries


Shared memory is necessary to maintain and share data between different processes with different address spaces. Suppose you have 2 Qt Applications, each linking with some shared library ( libtest-lib.so ), and you want one buffer to be allocated and shared by these different Qt Processes. There are standard C++ Posix APIs for handling shared memory, but Qt provides the class QSharedMemory (http://qt-project.org/doc/qt-4.8/qsharedmemory.html#attach). This class simplifies the process of handling synchronization of the data across seperate processes and is portable to different platforms.

Suppose we have a shared library called libtest-lib.so that we want to link against with 2 other , seperate Qt applications and share data. The library is implemented as shown below. The comments should mostly be clear, the major points when using QSharedMemory are:

1.) A key defines the shared memory in order to share it ( there is a nativeKey() method wich can be used to share the data with non-Qt processes because it uses the OS refernce to the shared memory

2.) You need to 'attach' to the memory after defining the key you want

3.) Once attached, you lock() the data to deal with it, and unlock() it after you are done so as to keep the data protected and un-corrupted

4.) If every process that uses the shared memory calls the detach() function, then the memory is released in a Unix environment

test-lib_global.h:

#ifndef TESTLIB_GLOBAL_H

#define TESTLIB_GLOBAL_H


#include <QtCore/qglobal.h>


#if defined(TESTLIB_LIBRARY)

# define TESTLIBSHARED_EXPORT Q_DECL_EXPORT

#else

# define TESTLIBSHARED_EXPORT Q_DECL_IMPORT

#endif


#endif // TESTLIB_GLOBAL_H


#ifndef TESTLIB_H

#define TESTLIB_H

testlib.h:

#include "test-lib_global.h"

// Use shared memory

#include <QSharedMemory>


/// Normal Qt Includes

#include <QBuffer>

#include <QDebug>


class TESTLIBSHARED_EXPORT TestLib : public QObject

{


Q_OBJECT


public:


TestLib();


// Get a Static int across apps linked to this library - Notice that it is a reference

TESTLIBSHARED_EXPORT static int& GetSingleInt();


// Initialize shared memory

void createInitialSharedMemory();


// Write to shared memory

void writeToSharedMemory( QString text );

private:

// Create shared memory

QSharedMemory sharedAudioMemory;

};

#endif // TESTLIB_H

testlib.cpp:

#include "testlib.h"

TestLib::TestLib() {

}

void TestLib::createInitialSharedMemory() {

// Create shared memory

qDebug() << "Creating new shared memory...";

sharedAudioMemory.setKey("globalAudioBufferSharedMemory");

// First, test whether a shared memory segment is already attached to the process.

// If so, detach it

if (sharedAudioMemory.isAttached())

{

sharedAudioMemory.detach();

}

// Try to create memory of our required size

int sizeOfSharedData = 60 * 8000 * 2; // 60 Seconds x 8 KHz * 2 Bytes ( signed short )

if ( !sharedAudioMemory.create( sizeOfSharedData ) )

{

qDebug() << "ERROR: Failed to Allocate Shared Memory of size: " << sizeOfSharedData << "ERROR: " << sharedAudioMemory.errorString();

}


}

void TestLib::writeToSharedMemory( QString text ) {


// Create a buffer and data stream and some text to shove in there

QBuffer buffer;

buffer.open( QBuffer::ReadWrite );

QDataStream out( &buffer );

out << text;

int size = buffer.size();


// Assign the key so we can update the data

sharedAudioMemory.setKey("globalAudioBufferSharedMemory");

if (!sharedAudioMemory.attach()) {


// If an attempt of reading from the shared memory before data is written

qDebug() << "Cannot attach to shared memory to update! ERROR: " << sharedAudioMemory.errorString();

}

// Write into the shared memory

sharedAudioMemory.lock();

qDebug() << "Writing data to buffer: " << text;

char *to = (char*)sharedAudioMemory.data();

const char *from = buffer.data().data();

qDebug() << "Pointer In Shared Memory is: " << &to;

memcpy( to, from, qMin( sharedAudioMemory.size(), size ) );

sharedAudioMemory.unlock();


}

int& TestLib::GetSingleInt()

{

// keep the actual value as static to this function

int min = 5;

int max = 500;

static int s_value(min + (rand() % (int)(max - min + 1)));


// but return a reference so that everybody can use it

return s_value;

}

This library is linked against by 2 other Qt Apps, they can read and modify the data as shown below:

Qt App A main() function:

// Create the shared memory and load some data

TestLib loadData;

loadData.createInitialSharedMemory();

loadData.writeToSharedMemory("12345 init shared memory");


// Create shared memory

QSharedMemory sharedAudioMemory("globalAudioBufferSharedMemory");


// Attempt to attach to shared memory for the audio buffer

if (!sharedAudioMemory.attach()) {


//If an attempt of reading from the shared memory before data is written

qDebug() << "ERROR: Failed to attach to shared memory...";

return -1;


}


// Define a buffer to read the data in

QBuffer buffer;

QDataStream in(&buffer);

QString text;


// Lock the memory and read the data

sharedAudioMemory.lock();

char* pointer = (char*)sharedAudioMemory.constData();

buffer.setData((char*)sharedAudioMemory.constData(), sharedAudioMemory.size()); // Sets the contents of the internal buffer to be the first size bytes of data.

buffer.open(QBuffer::ReadOnly);

in >> text;

sharedAudioMemory.unlock();


// Print the data

qDebug() << "SHARED TEXT IS: " << text;

Qt App B functions to update the data contained and shared in the QSharedMemory:

int B::getAudioBuffer( QByteArray *byteArray ) {


qDebug() << "Extracting Audio Buffer Data...";


// Attempt to attach to shared memory for the audio buffer

if (!sharedAudioMemory.attach()) {


// If an attempt of reading from the shared memory before data is written

qDebug() << "ERROR: Failed to attach to shared memory...";


}


// Define a buffer to read the data in

QBuffer buffer;

QDataStream in(&buffer);

QString text;


// Lock the memory and read the data

sharedAudioMemory.lock();

char* pointer = (char*)sharedAudioMemory.constData();

buffer.setData((char*)sharedAudioMemory.constData(), sharedAudioMemory.size()); // Sets the contents of the internal buffer to be the first size bytes of data.

buffer.open(QBuffer::ReadOnly);

in >> text;

sharedAudioMemory.unlock();


// Print the data

qDebug() << "SHARED TEXT IS: " << text;


updateText();


return 0;


}


void B::updateText() {


// Create seed for the random

// That is needed only once on application startup

QTime time = QTime::currentTime();

qsrand((uint)time.msec());


// Get random value between 0-100

int randomValue = qrand() % ((1000 + 1) - 2) + 2;


// Create the shared memory and load some data

TestLib loadData;

QString randomText = QString("%1 test asdf %2 test asdf %3 test asdf %4").arg(randomValue).arg(randomValue).arg(randomValue).arg(randomValue);

loadData.writeToSharedMemory(randomText);

}

So now we have 2 seperate Qt Apps, linking against a shared library that can modify and read the same shared memory. Happy coding :)



ClassyBits 2016