2012-12-08 16 views
7

próbuję skonstruować mapę w pamięci współdzielonej z następujących typówMapa <int, void*> w pamięci współdzielonej użyciu boost :: Interprocess

tworzę współdzielonego obszaru pamięci tak:

managed_shared_memory segment(create_only ,"MyMap" ,size);  

ShMemAllocator_t alloc_inst (segment.get_segment_manager()); 

map = segment.construct<MyMap_t>("MyMap")  
          (std::less<int>() 
          ,alloc_inst); 

The wartości w mapie są następujące:

 typedef pair<MutexType, boost::interprocess::offset_ptr<void> > ValueType ; 

MutexType jest sama struktura zawierająca odczytu i zapisu (mutex korzystając read_lock i WRITE_LOCK); zdefiniowane następująco:

typedef struct mutex_struct{ 
    sharable_lock<interprocess_mutex> read_lock(interprocess_mutex, defer_lock); 
    scoped_lock<interprocess_mutex> write_lock(interprocess_mutex, defer_lock); 
} MutexType; 

„wielkość” to łączna wielkość mapy (w zakresie przedmiotów, więc suma wielkości danych wskazywanego przez wszystkich void).

Jak mogę zagwarantować, że te puste dane * znajdują się również w tym segmencie pamięci, który utworzyłem, jak utworzyć instancję w istniejącym regionie pamięci współużytkowanej). Powodem tego jest to, że chcę przydzielić ten duży bufor tylko raz, ale ciągle usuwać/dodawać do niego obiekty (mapa modeluje pamięć podręczną). Muszę jeszcze znaleźć sposób, w jaki można przydzielić wiele obiektów w tym samym segmencie pamięci na mapie. Ponadto próba przypisania pary MutexType zwraca błąd kompilacji stwierdzający, że nie podano operatora "połączenia".

Odpowiedz

11

W zasadzie już tam jesteś. Wywołaj dowolny typ obiektu przydzielanego w pamięci współużytkowanej SecondValue_t. Zamiast ShMemAllocator_t zdefiniuj inny typ podzielnika międzyprocesowego, na przykład SecondValueAllocator_t, do przydzielania obiektów SecondValue_t. Za każdym razem, gdy chcesz wstawić obiekt ValueType do mapy, druga wartość obiektu ValueType jest przydzielana z instancją SecondValueAllocator_t.

Oto cały przykład, częściowo za pomocą kodu w my answer do Interprocess reader/writer lock with Boost:

#include <cstdlib> 
#include <functional> 
#include <iostream> 
#include <string> 
#include <utility> 

#include <boost/scope_exit.hpp> 
#include <boost/interprocess/managed_shared_memory.hpp> 
#include <boost/interprocess/allocators/allocator.hpp> 
#include <boost/interprocess/allocators/private_node_allocator.hpp> 
#include <boost/interprocess/containers/map.hpp> 
#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp> 
#include <boost/interprocess/sync/scoped_lock.hpp> 
#include <boost/interprocess/sync/sharable_lock.hpp> 
#include <boost/interprocess/sync/upgradable_lock.hpp> 

#define SHARED_MEMORY_NAME "SO13783012-MyMap" 

// https://stackoverflow.com/questions/13783012/map-of-int-void-in-shared-memory-using-boostinterprocess 

using namespace boost::interprocess; 

typedef int SecondValue_t; 
typedef allocator<SecondValue_t, managed_shared_memory::segment_manager> SecondValueAllocator_t; 

typedef struct mutex_struct { 
    //... 
} MutexType; 

typedef std::pair<MutexType, SecondValueAllocator_t::pointer> ValueType; 

typedef map<int, ValueType>::value_type MyMapValueType; 
typedef allocator<MyMapValueType, managed_shared_memory::segment_manager> MyMapEntryAllocator_t; 
typedef map<int, ValueType, std::less<int>, MyMapEntryAllocator_t> MyMap_t; 

struct shared_data { 
private: 
    typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex_type; 

    mutable upgradable_mutex_type mutex; 
    MyMap_t my_map; 

public: 
    shared_data(const MyMapEntryAllocator_t& alloc) 
     : my_map(MyMap_t::key_compare(), alloc) 
    { 
    } 

    // Tries to get the mapped value for the given key `k'. If successful, the mapped value is 
    // copied into `out' and `true' is returned. Otherwise, returns `false' and does not modify 
    // `out'. 
    bool try_get(MyMap_t::mapped_type& out, MyMap_t::key_type k) const { 
     boost::interprocess::sharable_lock<upgradable_mutex_type> lock(mutex); 
     MyMap_t::const_iterator pos = my_map.find(k); 
     if (pos != my_map.end()) { 
      out = pos->second; 
      return true; 
     } 
     return false; 
    } 

    void put(MyMap_t::key_type k, MyMap_t::mapped_type v) { 
     boost::interprocess::scoped_lock<upgradable_mutex_type> lock(mutex); 
     my_map.insert(MyMap_t::value_type(my_map.size(), v)); 
    } 
}; 

int main(int argc, char *argv[]) 
{ 
    if (argc != 2) { 
     std::cerr << "Usage: " << argv[0] << " WHICH\n"; 
     return EXIT_FAILURE; 
    } 

    const std::string which = argv[1]; 

    if (which == "parent") { 
     shared_memory_object::remove(SHARED_MEMORY_NAME); 
     BOOST_SCOPE_EXIT(argc) { 
      shared_memory_object::remove(SHARED_MEMORY_NAME); 
     } BOOST_SCOPE_EXIT_END; 
     managed_shared_memory shm(create_only, SHARED_MEMORY_NAME, 65536); 

     MyMapEntryAllocator_t entry_alloc(shm.get_segment_manager()); 
     shared_data& d = *shm.construct<shared_data>("theSharedData")(entry_alloc); 

     SecondValueAllocator_t second_value_alloc(shm.get_segment_manager()); 

     // Insert some test data. 
     SecondValueAllocator_t::pointer p; 
     p = second_value_alloc.allocate(1); 
     second_value_alloc.construct(p, -3); 
     d.put(0, std::make_pair(MutexType(), p)); 
     p = second_value_alloc.allocate(1); 
     second_value_alloc.construct(p, 70); 
     d.put(1, std::make_pair(MutexType(), p)); 
     p = second_value_alloc.allocate(1); 
     second_value_alloc.construct(p, -18); 
     d.put(2, std::make_pair(MutexType(), p)); 
     p = second_value_alloc.allocate(1); 
     second_value_alloc.construct(p, 44); 
     d.put(3, std::make_pair(MutexType(), p)); 
     p = second_value_alloc.allocate(1); 
     second_value_alloc.construct(p, 0); 
     d.put(4, std::make_pair(MutexType(), p)); 

     // Go to sleep for a minute - gives us a chance to start a child process. 
     sleep(60); 
    } else { 
     managed_shared_memory shm(open_only, SHARED_MEMORY_NAME); 
     std::pair<shared_data *, std::size_t> find_res = shm.find<shared_data>("theSharedData"); 
     if (!find_res.first) { 
      std::cerr << "Failed to find `theSharedData'.\n"; 
      return EXIT_FAILURE; 
     } 
     shared_data& d = *find_res.first; 

     MyMap_t::mapped_type v; 
     int i = 0; 
     for (; d.try_get(v, i); ++i) { 
      std::cout << i << ": " << *v.second << '\n'; 
     } 

     // Add an entry. 
     srand(time(NULL)); 
     SecondValueAllocator_t second_value_alloc(shm.get_segment_manager()); 
     SecondValueAllocator_t::pointer p = second_value_alloc.allocate(1); 
     second_value_alloc.construct(p, (rand() % 200) - 100); 
     d.put(i, v = std::make_pair(MutexType(), p)); 
     std::cout << "placed " << *v.second << " into the map.\n"; 
    } 

    return EXIT_SUCCESS; 
} 

Test po przez uruchomienie procesu macierzystego pierwszego,

 
./SO13783012 parent 

następnie kilka dzieci:

 
./SO13783012 child 

Przykładowa wydajność:

 
> ./SO13783012 child 
0: -3 
1: 70 
2: -18 
3: 44 
4: 0 
placed 5: -63 into the map. 
> ./SO13783012 child 
0: -3 
1: 70 
2: -18 
3: 44 
4: 0 
5: -63 
placed 6: -42 into the map. 
> ./SO13783012 child 
0: -3 
1: 70 
2: -18 
3: 44 
4: 0 
5: -63 
6: -42 
placed 7: -28 into the map. 
+0

(Wiem, że jest trochę stara, ale) Zauważyłem, że blokada pisarza nie działa tak naprawdę w twoim przykładzie.Jeśli spawnujesz wiele obiektów potomnych pisarza, wartość i może być niezdefiniowana, a Ty spróbujesz wstawić parę używając tej samej wartości, z powodu problemów z współbieżnością - jeśli ten przykład powinien pokazać dodanie innego wpisu po ostatnim kluczu - wtedy metoda put powinna się zbierać bieżący rozmiar mapy i wstaw element o rozmiarze + 1. – Steve

+0

Witam @Steve, nie jestem pewien, czy podążam. Dlaczego wartość 'i' jest niezdefiniowana? –

+1

W pętli for - liczyć ++ i do momentu, gdy żadna wartość nie zostanie zwrócona z d.try_get - pozwala założyć, że jest teraz 10. Później próbujesz umieścić klucz i = 10 z d.put (i, v = std :: make_pair (MutexType(), p)) ;. Jeśli w międzyczasie inny proces zapisywania dziecka już wprowadzi coś na mapę - 10 będzie duplikatem. Załóżmy, że dwóch pisarzy-dzieci - każdy wykonuje jedną linię naraz - oboje docierają do i = 10, ale dziecko 1 stawia pierwszy wpis z i = 10 - w ten sposób drugie dziecko zawiedzie. To dlatego, że i = 10 znajduje się poza blokadą zapisu/odczytu – Steve

0

Można użyć ponownego powiązania w podzielniki <>

// convert an allocator<T> to allocator<U> 
template<typename U> 
struct rebind { 
    typedef Allocator<U> other; 
}; 

dokonać

typedef std::allocator< char, managed_shared_memory::segment_manager > char_alloc; 

następnie przydzielić pamięć jak ten

char_alloc char_alloc_obj; 
char * ptr = new (char_alloc_obj.allocate(size)) char[size]; 
void * vptr = (void *) ptr; 

Domyślna std :: przydzielania <> oznacza mniej. Współużytkowany alokator pamięci będzie miał stan, więc musisz dowiedzieć się, jak skopiować stan między alokatory różnych typów.