File_mapping

Using the Boost IPC library, it is possible to associate a file content with shared memory, that gives a number of advantages, like delegating to the OS all the trouble connected with data sync and caching.

By the way, sometimes could be useful to use file_mapping even if we are not interested in sharing memory, but just to simplify the data management on a file.

Here is an example:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstring>
#include <cstddef>
#include <cstdlib>
#include "boost/interprocess/file_mapping.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;

namespace
{
const std::size_t FILE_SIZE = 10000;
const char* FILE_NAME = "file.bin";

class FileManager // 1.
{
public:
enum Mode { create_remove, create, remove, access };
FileManager(const char* fname, Mode mode) : fname_(fname), mode_(mode)
{
if(mode == create_remove || mode == create)
{
std::filebuf fbuf;
fbuf.open(FILE_NAME, std::ios_base::in | std::ios_base::out
| std::ios_base::trunc | std::ios_base::binary);
//Set the size
fbuf.pubseekoff(FILE_SIZE-1, std::ios_base::beg);
fbuf.sputc(0);
}
}

mapped_region getMappedRegion(mode_t mode)
{
//Create a file mapping
file_mapping m_file(fname_.c_str(), mode);

//Map the whole file with read-write permissions in this process
return mapped_region(m_file, mode);
}

~FileManager()
{
if(mode_ == remove || mode_ == create_remove)
{
file_mapping::remove(fname_.c_str());
std::cout << "File " << fname_ << " removed" << std::endl;
}
}
private:
std::string fname_;
Mode mode_;
};

// 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 checkMemoryOne(void* address, std::size_t size)
{
const char* mem = static_cast<const char*>(address);
for(std::size_t i = 0; i < size; ++i)
{
if(*mem++ != 1)
{
std::cout << "Memory check failed" << std::endl;
return;
}
}
std::cout << "Memory check succeeded" << std::endl;
}

void sharedMemoryAccess() // 2.
{
FileManager fm(FILE_NAME, FileManager::access);
mapped_region region = fm.getMappedRegion(read_only);

//Get the address of the mapped region
void* addr = region.get_address();
std::size_t size = region.get_size();

checkMemoryOne(addr, size);
}

void fileAccess() // 3.
{
std::filebuf fbuf;
fbuf.open(FILE_NAME, std::ios_base::in | std::ios_base::binary);

//Read it to memory
std::vector<char> vect(FILE_SIZE, 0);
fbuf.sgetn(&vect[0], std::streamsize(vect.size()));

checkMemoryOne(&vect[0], FILE_SIZE);
}
}

// parent process
void ip06a(const char* progName)
{
FileManager fm(FILE_NAME, FileManager::create_remove);
mapped_region region = fm.getMappedRegion(read_write);

//Get the address of the mapped region
void* addr = region.get_address();
std::size_t size = region.get_size();

//Write all the memory to 1
std::memset(addr, 1, size);

launchChildProcess(progName);
}

// child process
void ip06b()
{
std::cout << "Accessing the shared memory" << std::endl;
sharedMemoryAccess();

std::cout << "Accessing the file on disk" << std::endl;
fileAccess();
}

1. the class FileManager is just a little wrapper to the basic functionality for the file access. Its constructor keeps track of the associate file name and the way we want to access it. Only if we want to actually create it, the filebuf functionality are called to access the file, otherwise, if we just want to access the data (or remove the file) we rely on the fact that someone else should have already crete the file. The getMappedRegion() method performs the mapping between shared memory and file, and return the mapped region, in the mode we require. The destructor remove the file, only when required.
2. the function sharedMemoryAccess() shows how to work with the mapped_region.
3. as comparison, the function fileAccess() performs the same action of sharedMemoryAccess(), but accessing directly the file.

The main for this application, basically just calls the parent or the child function accordingly to the passed parameter:
if(argc == 1)
ip06a(argv[0]);
else
ip06b();

The code is based on an example provided by the Boost Library Documentation.

No comments:

Post a Comment