Die Boost C++ Bibliotheken

Shared Memory

Shared Memory ist üblicherweise die schnellste Form der Interprozesskommunikation. Dabei wird ein Speicherbereich gleichzeitig mehreren Prozessen zur Verfügung gestellt. So kann ein Prozess Daten in diesen Speicherbereich ablegen, und ein anderer Prozess kann die abgelegten Daten aus dem Speicherbereich lesen.

Boost.Interprocess bietet eine Klasse boost::interprocess::shared_memory_object an, die einen derartigen Speicherbereich repräsentiert. Um diese Klasse nutzen zu können, muss die Headerdatei boost/interprocess/shared_memory_object.hpp eingebunden werden.

Beispiel 33.1. Shared Memory erstellen
#include <boost/interprocess/shared_memory_object.hpp>
#include <iostream>

using namespace boost::interprocess;

int main()
{
  shared_memory_object shdmem{open_or_create, "Boost", read_write};
  shdmem.truncate(1024);
  std::cout << shdmem.get_name() << '\n';
  offset_t size;
  if (shdmem.get_size(size))
    std::cout << size << '\n';
}

Der Konstruktor von boost::interprocess::shared_memory_object erwartet drei Parameter. Der erste Parameter gibt an, ob der Shared Memory erstellt oder nur geöffnet werden soll. Im Beispiel 33.1 ist boost::interprocess::open_or_create angegeben, was bedeutet, dass der Shared Memory geöffnet wird, wenn er bereits existiert, anderfalls neu erstellt wird.

Das Öffnen setzt voraus, dass auf einen Shared Memory zugegriffen werden kann, der bereits erstellt wurde. Um Speicherbereiche identifizieren zu können, werden ihnen Namen gegeben. Der zweite Parameter, der dem Konstruktor von boost::interprocess::shared_memory_object übergeben wird, gibt den Namen an.

Der dritte und letzte Parameter legt fest, wie ein Prozess auf den Shared Memory zugreifen soll. Beispiel 33.1 darf den Shared Memory sowohl lesen als auch schreiben, da boost::interprocess::read_write angegeben ist.

Nachdem ein Objekt vom Typ boost::interprocess::shared_memory_object erstellt wurde, existiert ein entsprechender Shared Memory. Der Shared Memory ist jedoch von Beginn an 0 Bytes groß. Um den Shared Memory nutzen zu können, muss truncate() aufgerufen werden. Dieser Methode wird die Größe in Bytes übergeben, auf die der Shared Memory anwachsen soll. Für Beispiel 33.1 bedeutet dies, dass der Shared Memory Platz für 1024 Bytes bietet.

Beachten Sie, dass Sie truncate() nur aufrufen dürfen, wenn Sie den Shared Memory mit boost::interprocess::read_write geöffnet haben. Andernfalls wird eine Ausnahme vom Typ boost::interprocess::interprocess_exception geworfen.

Sie können truncate() mehrfach aufrufen, um die Größe des Shared Memory anzupassen.

Wenn Sie so wie im Beispiel 33.1 einen Shared Memory erstellt haben, können Sie mit Methoden wie get_name() und get_size() den Namen und die Größe abfragen.

Da Sie den Shared Memory erstellt haben, um über diesen Speicherbereich Daten mit anderen Prozessen auszutauschen, müssen Sie in irgendeiner Weise auf die 1024 Bytes zugreifen. Dazu müssen Sie den Shared Memory in den Speicherbereich eines Prozesses abbilden. Dies erfolgt mit Hilfe der Klasse boost::interprocess::mapped_region.

Wenn Sie sich wundern, warum zwei Klassen eingesetzt werden, um auf einen Shared Memory zuzugreifen: Die Klasse boost::interprocess::mapped_region kann auch andere Objekte in den Speicherbereich eines Prozesses abbilden. Sie wird nicht nur im Zusammenhang mit boost::interprocess::shared_memory_object verwendet.

Beispiel 33.2. Shared Memory in den Speicherbereich eines Prozesses abbilden
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>

using namespace boost::interprocess;

int main()
{
  shared_memory_object shdmem{open_or_create, "Boost", read_write};
  shdmem.truncate(1024);
  mapped_region region{shdmem, read_write};
  std::cout << std::hex << region.get_address() << '\n';
  std::cout << std::dec << region.get_size() << '\n';
  mapped_region region2{shdmem, read_only};
  std::cout << std::hex << region2.get_address() << '\n';
  std::cout << std::dec << region2.get_size() << '\n';
}

Um die Klasse boost::interprocess::mapped_region verwenden zu können, müssen Sie die Headerdatei boost/interprocess/mapped_region.hpp einbinden. Dem Konstruktor dieser Klasse müssen Sie als ersten Parameter ein Objekt vom Typ boost::interprocess::shared_memory_object übergeben. Der zweite Parameter legt fest, ob nur lesend oder auch schreibend auf den Speicherbereich zugegriffen werden kann.

Im Beispiel 33.2 werden zwei Objekte vom Typ boost::interprocess::mapped_region erstellt: Der Shared Memory namens Boost wird zweimal in den Speicherbereich des Prozesses abgebildet. Das Programm gibt über den Aufruf der Methoden get_address() und get_size() die Adresse und die Größe des abgebildeten Speicherbereichs aus. Während get_size() in beiden Fällen den gleichen Wert zurückgibt, nämlich 1024, ist der Rückgabewert der beiden Aufrufe von get_address() verschieden.

Im Beispiel 33.3 wird auf die abgebildeten Speicherbereiche zugegriffen, um zum Test eine Zahl zu speichern und zu lesen.

Beispiel 33.3. Eine Zahl in den Shared Memory schreiben und wieder lesen
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>

using namespace boost::interprocess;

int main()
{
  shared_memory_object shdmem{open_or_create, "Boost", read_write};
  shdmem.truncate(1024);
  mapped_region region{shdmem, read_write};
  int *i1 = static_cast<int*>(region.get_address());
  *i1 = 99;
  mapped_region region2{shdmem, read_only};
  int *i2 = static_cast<int*>(region2.get_address());
  std::cout << *i2 << '\n';
}

Die Zahl 99 wird über region an den Anfang des 1024 Bytes großen Shared Memory geschrieben. Anschließend wird auf den Anfang des Speicherbereichs region2 zugegriffen und eine Zahl auf die Standardausgabe ausgegeben. Obwohl region und region2 zwei unterschiedliche Speicherbereiche im Prozess darstellen – deswegen war der Rückgabewert von get_address() im vorherigen Beispiel verschieden – gibt das Beispiel 99 aus. Denn sowohl region als auch region2 greifen auf den gleichen Shared Memory zu.

Beispiel 33.4. Shared Memory löschen
#include <boost/interprocess/shared_memory_object.hpp>
#include <iostream>

using namespace boost::interprocess;

int main()
{
  bool removed = shared_memory_object::remove("Boost");
  std::cout << std::boolalpha << removed << '\n';
}

Wenn Sie einen Shared Memory löschen möchten, greifen Sie auf die statische Methode remove() zu, die von der Klasse boost::interprocess::shared_memory_object zur Verfügung gestellt wird. Sie müssen ihr wie im Beispiel 33.4 lediglich den Namen des Shared Memory übergeben, der gelöscht werden soll.

Boost.Interprocess unterstützt in gewisser Weise das RAII-Idiom. So können Sie die Klasse boost::interprocess::remove_shared_memory_on_destroy verwenden, indem Sie dem Konstruktor den Namen eines existierenden Shared Memory übergeben. Wird das Objekt vom Typ dieser Klasse gelöscht, wird der Shared Memory automatisch im Destruktor freigegeben.

Beachten Sie, dass der Konstruktor von boost::interprocess::remove_shared_memory_on_destroy keinen Shared Memory erstellt oder öffnet. Die Klasse ist daher kein typischer Vertreter des RAII-Idioms.

Wenn Sie remove() nicht aufrufen, bleibt der Shared Memory bestehen, auch wenn Ihr Programm endet. Es hängt vom Betriebssystem ab, ob und wann der Shared Memory gelöscht wird. Windows und viele Unix-Betriebssysteme inklusive Linux löschen einen Shared Memory automatisch, wenn das System neugestartet wird.

Unter Windows gibt es eine besondere Art von Shared Memory, der automatisch gelöscht wird, wenn der letzte Prozess beendet wurde, der den Shared Memory verwendet hat. Sie können diesen Shared Memory verwenden, indem Sie auf die Klasse boost::interprocess::windows_shared_memory zugreifen. Diese Klasse ist in der Headerdatei boost/interprocess/windows_shared_memory.hpp definiert. Sehen Sie sich dazu Beispiel 33.5 an.

Beispiel 33.5. Windows-spezifischen Shared Memory verwenden
#include <boost/interprocess/windows_shared_memory.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>

using namespace boost::interprocess;

int main()
{
  windows_shared_memory shdmem{open_or_create, "Boost", read_write, 1024};
  mapped_region region{shdmem, read_write};
  int *i1 = static_cast<int*>(region.get_address());
  *i1 = 99;
  mapped_region region2{shdmem, read_only};
  int *i2 = static_cast<int*>(region2.get_address());
  std::cout << *i2 << '\n';
}

Beachten Sie, dass die Klasse boost::interprocess::windows_shared_memory keine Methode truncate() anbietet. Stattdessen müssen Sie die Größe des Shared Memory als vierten Parameter dem Konstruktor übergeben.

Auch wenn die Klasse boost::interprocess::windows_shared_memory nicht portabel ist und nur unter Windows verwendet werden kann: Sie ist vor allem dann von Nutzen, wenn Sie mit anderen Windows-Anwendungen Daten austauschen wollen, die diese besondere Art von Shared Memory verwenden.