Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

string in mmap shared memory c++

My program is having segfault.

I'm trying access string from forks, but I can't even put data to shared memory.

My code:

static string * currentSessionIP;

int main(int argc, char *argv[])
{
    currentSessionIP = static_cast<string*>(mmap(NULL, 4096, PROT_READ | PROT_WRITE,
         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
    *currentSessionIP = "";
}

There are no errors when mmap, but when I'm trying to assign something, it's segfaults.

How can I access to string from any fork(), and even change it?
Thanks in advance!

like image 243
How to change username Avatar asked Sep 02 '25 06:09

How to change username


1 Answers

You didn't build the std::string, i.e. call it's constructor. The constructor is responsible for initializing the object data. Imagine string is implemented like this:

struct string {
    char* data;
    size_t length;
    // ...
};

You put this struct in valid memory with your cast, no problem there, but the value of the fields data and length is not initialized, so data points to a random address and length is garbage, thus when you use the string you get a crash. You must explicitly call the constructor, something like currentSessionIP->string(); before using it, but that's impossible in C++. Instead you must use the placement new syntax, which combines the cast and the call to the constructor like this:

void * mem = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));

currentSessionIP = new (mem) string;

This syntax is shorter and safer since you're sure the constructor is called. But you're still not done, because the string struct will be in shared memory, but that's just the "header" : the data has no reason to point inside your shared memory block.

By default std::string uses std::allocator which uses standard memory allocation, on your program heap. The solution is to use a custom allocator (template argument to std::basic_string) to ensure data always points in the shared memory.Your allocator will be called by the string object at initialization and when you resize the string.

Have a look at this tutorial or at boost interprocess, meanwhile a very crude and incomplete implementation below, just to dive an idea:

// a very simple shared memory allocator
struct shared_alloc {
    typedef char value_type;
    typedef char* pointer;
    //... see http://en.cppreference.com/w/cpp/memory/allocator

    pointer cur; // points to a free memory location
    shared_alloc() {
        cur = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
        // todo: lifecycle (free memory block sometime)
    }
    pointer allocate(size_t n) {
        pointer p = cur;
        cur += n; // todo bound checking
        return p;
    }
    void deallocate(pointer p, size_t n) {
        // todo: do something smart
    }
};

// an allocator object
shared_alloc alloc;

// a string with a custom allocator
typedef std::basic_string<char, std::char_traits<char>, shared_alloc> shared_string;

// allocate shared mem for the string object (the header)
void * header_mem = alloc.allocate(sizeof(shared_string));

// create a string object at this location, and use our allocator for the data
shared_string * currentSessionIP =
  new( header_mem ) shared_string(alloc);
like image 162
Antoine Avatar answered Sep 04 '25 20:09

Antoine