Note: This tutorial assumes that you have completed the previous tutorials: Semaphores.
(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Shared Memory

Description: Simplified api-like interface to shared memory storage.

Keywords: ecl shared memory

Tutorial Level: INTERMEDIATE

Overview

Used in conjunction with semaphores, shared memory sections provide the fastest way for processes to directly communicate with each other. Usually you reserve a block and then cast in and out of the direct memory addresses allocated within the reserved memory. However, this is not typesafe and error-prone, so the shared memory wrapper here instead allocates a block that is implicitly tied to a data structure. The following steps provide an easy, convenient way to access the reserved memory block through a typesafe and well-structured api.

  • Formulate a data structure of fixed size.
  • Create a wrapper class that includes a semaphore, shared memory object, a read/write interface and pointer to the data.
  • Instantiate the shared memory using with the data structure as a template argument.
  • Point to the data section stored in the shared memory.
  • Initialise the data section (if its the first accessor).
  • Establish the read/write interface which will internally use semaphores to access the shared memory.

   1 struct DataStructure {
   2     int i;
   3 };
   4 
   5 class Data {
   6 public:
   7     void readInteger(int i);
   8     void writeInteger(int j);
   9 
  10 private:
  11     void initialise();
  12 
  13     SharedMemory< DataStructure > sm;
  14     Semaphore semaphore;
  15     DataStructure *data;
  16 };
  17 
  18 inline Data::Data() : sm("sm_test"), semaphore("sem_test"), data(NULL) {
  19     data = sm.data();
  20     initialise();
  21 }
  22 
  23 inline Data::readInteger(int i) {
  24     semaphore.lock();
  25     i = data->i;
  26     semaphore.unlock();
  27 }
  28 inline Data::writeInteger(int i) {
  29     semaphore.lock();
  30     data->i = i;
  31     semaphore.unlock();
  32 }

Important Usage Notes

No Variable Length Data Structures

Ensure you do not use variable length types (such as stl containers) inside the data structure because the shared memory section allocates memory based on the initial size of the data structure.

Who Initialises the Shared Memory Block

Initialisation and closure can be a little complicated. With initialisation, you want to ensure that only the first accessor initialises the memory to desired defaults, but there is no way of guaranteeing via the shared memory class that you're the first accessor - in some cases you could be accessing an already reserved block. One way of getting around this is to set a magic number in the data structure and see if that reads rubbish when you initialise. If it does - then appropriately initialise all you data structure variables and set your magic number accordingly - subsequent accesses from different processes should find the correct magic number and not initialise.

Closing the Shared Memory Block

With regards to closure, there is also no guarantee that you can actually clean up the memory block. If everything runs fine and closes without error, then it will be fine, but if your program crashes in some unforeseen way and not all of the operating system signals are set up (usually too much detail to bother with), then the memory block will remain on the system. Often its easier to get some external script to clean the memory sections.

Wiki: ecl_ipc/Tutorials/Shared Memory (last edited 2012-01-24 23:38:35 by DanielStonier)