Shared_memory_object

As far as I know, the new C++ standard (C++0x, still a draft when I'm writing) does not bring much in the interprocess communication (IPC) field. I guess the problem is that almost any environment provides its own native way to approach the issue, and at this point it is not easy to find a solution that would make anyone happy.

Luckily Boost provides an interprocess library that is vary useful in keeping the code as portable as possible.

Here is a first example that shows how to manage shared memory among different processes.

We create two processes. A first one takes care of allocating the shared memory, then it spawns a new process that accesses the shared memory, and does something with it before terminating. Then the parent process gets back in control, removes the shared memory and terminates.

This is the main of our application:
int main(int argc, char* argv[])
{
  if(argc == 1)
    ip05a(argv[0]);
  else
    ip05b();

  system("pause");
}
If we call the executable without parameters, the only argument passed to main() is the executable name, and we use this information to consider this process as the parent one for our application. Otherwise we assume to be in the child process.

This is the implementation for the two functions:
#include <cstring>
#include <cstdlib>
#include <string>
#include <iostream>
#include "boost/interprocess/shared_memory_object.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;

namespace
{
  const char* MY_SHARED = "MySharedMemory";
  const size_t MY_SHARED_SIZE = 1000;

  // Remove shared memory on construction and destruction
  class Remover
  {
  private:
    std::string name_;
    void remove() { shared_memory_object::remove(name_.c_str()); }
  public:
    Remover(const char* name) : name_(name) { remove(); }
    ~Remover() { remove(); }
  };

  // ensure the executable file name is quoted in case it has internal blanks
  bool launchChildProcess(const char* progName)
  {
    bool quote = strchr(progName, ' ') == 0 ? false : true;
    std::string s((quote ? "\"" : ""));
    s += progName;
    if(quote)
      s += "\"";
    s += " child";

    if(std::system(s.c_str()) != 0)
    {
      std::cout << "error in the child process" << std::endl;
      return false;
    }
    return true;
  }
}

void ip05a(const char* progName)
{
  try
  {
    Remover remover(MY_SHARED); // 1
    shared_memory_object shm(create_only, MY_SHARED, read_write); // 2
    shm.truncate(MY_SHARED_SIZE); // 3
    mapped_region region(shm, read_write); // 4
    std::memset(region.get_address(), 1, region.get_size()); // 5

    launchChildProcess(progName);
  }
  catch(interprocess_exception& ie)
  {
    std::cout << "can't create the shared memory: " << ie.what() << std::endl;
  }
}

void ip05b()
{
  try
  {
    shared_memory_object shm(open_only, MY_SHARED, read_only); // 6
    mapped_region region(shm, read_only);
    std::cout << "Working on a mapped region with size " << region.get_size() << std::endl;

    // do something
    char* mem = static_cast<char*>(region.get_address());
    for(std::size_t i = 0; i < region.get_size(); ++i)
    {
      if(*mem++ != 1)
      {
        std::cout << "unexpected value in the shared memory" << std::endl;
        return;
      }
    }

    std::cout << "shared memory read correctly" << std::endl;
  }
  catch(interprocess_exception& ie)
  {
    std::cout << "can't work on the shared memory: " << ie.what() << std::endl;
  }
}
  1. We create Remover object in order to ensure that the shared memory is correctly destroyed when we left the parent process. The object calls shared_memory_object::remove() for the specified name both on construction and deletion of the object. The call on construction could be seen as an overkilling, but it is cheap, and saves us some trouble in case for any reason the shared memory allocated from a previous execution is unexpectedly still there.
  2. We create an instance of shared_memory_object passing the flag create_only so, if already exists an object in shared memory with the passed name, an exception is raised. Besides, we specify the read_write access mode since we actually want to modify the associated memory.
  3. The shared_memory_object.truncate() method is used to set the size for the object.
  4. Mapped_region maps a shared_memory_object in a region, making the memory available to the current process. We specify here too that we want access the memory in read_write mode.
  5. The mapped_region memory location is accessed through get_address() and get_size().
  6. We create an instance of shared_memory_object passing the flag open_only so, if does not exist yet an object in shared memory with the passed name, an exception is raised. Besides, we specify the read_only access mode since we want just to read the associated memory.
The code is based on an example provided by the Boost Library Documentation.

No comments:

Post a Comment