سؤال

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!

هل كانت مفيدة؟

المحلول

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);

نصائح أخرى

std::string is an object, not just a contiguous memory buffer. You'd have to initialize the string or maybe use the placement new operator to create the string object.

Your call to mmap returns a pointer to uninitialized memory. It does not point to a string. Just because you cast the pointer to string* doesn't magically create a std::string at the location pointed to by currentSessionIP.

When you do *currentSessionIP = "", you try to modify something that is not really a std::string, so this will segfault.

If you really want, you can create a string at that location using placement new, but I wouldn't recommend that.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top