A simple C++ shared memory program written on linux: segmentation fault

28,535

Solution 1

You are placing vectors and strings into shared memory. Both those classes allocate memory of their own, which will be allocated within the address space of whatever process generates the allocation, and will produce a segfault when accessed from the other process. You could try specifying allocators to use that shared memory, but since in C++03 allocators are assumed to be stateless I'm not sure if it will be possible.

Consider checking out how Boost.Interprocess does it.

Solution 2

You have a number of problems. The obvious one is that you don't construct your object. In opaque form, you are currently doing:

class Foo;

Foo * p = get_memory();

p->bar = 5;  // ouch!

What you should be doing, at the very least:

void * addr = get_memory(sizeof(Foo));
Foo * p = ::new (addr) Foo;
// do work
p->~Foo(); // done

(Just replace Foo by LOCK for your situation.)

However, it gets more complicated: vector and string require dynamic allocations themselves. That memory must live in the same address space as your LOCK. So the standard way to solve this is to write your own allocator and pass that:

template <template <typename> class Alloc>
struct Lock
{
  typedef std::basic_string<char, std::char_traits<char>, Alloc<char>> shared_string;

  shared_string name;
  shared_string type;

  std::vector<shared_string, Alloc<shared_string>> pids;
};

Finally, you have to write a conforming allocator class that puts memory in the same address space as the one in which your LOCK object will ultimately go:

template <typename T>
class shared_allocator { /* write this! */ }

typedef Lock<shared_allocator> LOCK;

Solution 3

I know it is very long time ago. But I was searching how-to (remember) about shared mem and this thread is interesting.

My Two cents to the demander :

  • Please consider that reading and writing to SharedMemory is exactly like reading and writing into a FileSystem.

  • Shared Memory ID == file_handle from open(...);

    So... How do you CORRECTLY serialize and then read-write std::string or even std::vector into a FILE ? Do you really 'expand/shrink' a std::string or std::vector from within a FILE ??

Thank you :)

Share:
28,535
Terry Li
Author by

Terry Li

Updated on May 06, 2020

Comments

  • Terry Li
    Terry Li about 4 years
    #include <stdio.h> 
    #include <sys/shm.h> 
    #include <sys/stat.h> 
    #include <string>
    #include <vector>
    #include <iostream>
    
    using namespace std;
    
    struct LOCK {
      string name;
      string type;
      vector <string> pids;
    };
    
    int main () 
    
    {
    
      int segment_id; 
    
      LOCK* shared_memory; 
    
      struct shmid_ds shmbuffer; 
    
      int segment_size; 
    
      const int shared_segment_size = 0x6400; 
    
    
    
      /* Allocate a shared memory segment.  */ 
    
      segment_id = shmget (IPC_PRIVATE, shared_segment_size, 
    
                         IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR); 
    
      /* Attach the shared memory segment.  */ 
    
      shared_memory = (LOCK*) shmat (segment_id, 0, 0); 
    
      printf ("shared memory attached at address %p\n", shared_memory); 
    
      /* Determine the segment's size. */ 
    
      shmctl (segment_id, IPC_STAT, &shmbuffer); 
    
      segment_size  =               shmbuffer.shm_segsz; 
    
      printf ("segment size: %d\n", segment_size); 
    
      /* Write a string to the shared memory segment.  */ 
    
      //sprintf (shared_memory, "Hello, world."); 
      shared_memory -> name = "task 1";
      shared_memory -> type = "read";
      (shared_memory -> pids).push_back("12345");
      (shared_memory -> pids).push_back("67890");
    
      /* Detach the shared memory segment.  */ 
    
      shmdt (shared_memory); 
    
    
    
      /* Reattach the shared memory segment, at a different address.  */ 
    
      shared_memory = (LOCK*) shmat (segment_id, (void*) 0x5000000, 0); 
    
      printf ("shared memory reattached at address %p\n", shared_memory); 
    
      /* Print out the string from shared memory.  */ 
    
      //printf ("%s\n", shared_memory -> name); 
      cout << "Name of the shared memory: " + shared_memory -> name << endl;
    
      /* Detach the shared memory segment.  */ 
    
      shmdt (shared_memory); 
    
    
    
      /* Deallocate the shared memory segment.  */ 
    
      shmctl (segment_id, IPC_RMID, 0); 
    
    
    
      return 0; 
    
    } 
    

    I got the code from a tutorial on shared memory. It worked until I defined struct LOCK and tried to write LOCKs instead of char* into the shared memory.

    Could someone please help me figure out the problem here that causes the segmentation fault?

  • David Schwartz
    David Schwartz over 12 years
    Even if it did allocate in shared memory, it still wouldn't work. The classes internally track absolute pointers to the memory they allocate. If the shared memory segments maps at a different address, the pointers won't point where they should. You also have to use relative pointers (relative to the base of the shared memory segment).
  • Tamás Szelei
    Tamás Szelei over 12 years
    @DavidSchwartz, doesn't the OS hides that from the userspace programs?
  • David Schwartz
    David Schwartz over 12 years
    @TamásSzelei No. How could it? If you map the shared memory at address X, then your pointer to an object in it will be X+A (where A is the relative offset). If another process maps that shared memory at address Y (because X was in use in that process, or just by bad luck), it needs a pointer to Y+A to access the same data. You can't just pass the absolute pointer (X+A) from one process to another or put it in shared memory. You have to pass the relative pointer (A) and each process needs to add its base mapping address to the pointer before it dereferences it.
  • K-ballo
    K-ballo over 12 years
    As it was pointed out at comments in my answer, a custom allocator is not enough. Relative pointers are needed to point to the right place in each process address space.
  • Kerrek SB
    Kerrek SB over 12 years
    @K-ballo: The allocator would be expected to take care of that, non? The new stateful allocators should make life a lot easier, as they can now store a base address.
  • Kerrek SB
    Kerrek SB over 12 years
    @K-ballo: Allocators have significant freedom of defining anything they please to be a "pointer type"... I'm pretty sure this could be rigged up.
  • Kerrek SB
    Kerrek SB over 12 years
    @K-ballo: well, you can always fake a state by having a global, so in a crunch it could be one in C++98 too...