Как хранить stl-объекты в общей памяти (С++)?
У меня есть следующий код:
class A {
double a, b, c;
...
};
class B {
map<int, A> table; // Can have maximum of MAX_ROWS elements.
...
};
class C {
B entries;
queue<int> d;
queue<int> e;
...
};
Теперь я хочу сохранить объект типа C в общей памяти, чтобы различные процессы могли добавлять, обновлять и читать. Как я могу это сделать? (Примечание. Я знаю, как хранить простой массив C, который имеет фиксированный размер в общей памяти. Также помните, что B.table может иметь произвольные записи.
Ответы
Ответ 1
Используйте boost:: interprocess, эта библиотека предоставляет эту функциональность.
EDIT: Вот некоторые изменения, которые вам нужно сделать:
В примере уже описан распределитель, который будет выделяться из блока разделяемой памяти, вам нужно передать это значение в map
и queue
. Это означает, что вам придется изменить свои определения:
class B
{
map<int, A, less<int>, MapShmemAllocator> table;
// Constructor of the map needs the instance of the allocator
B(MapShmemAllocator& alloc) : table(less<int>(), alloc)
{ }
}
Для queue
это немного сложно, потому что он действительно просто адаптер, поэтому вам нужно передать реальный класс реализации в качестве параметра шаблона:
typedef queue<int, deque<int, QueueShmemAllocator> > QueueType;
Теперь ваш класс C
слегка изменится:
class C
{
B entries;
QueueType d, e;
C(MapShmemAllocator& allocM, QueueShmemAllocator& allocQ) : entries(allocM), d(allocQ), e(allocQ)
{ }
}
Теперь из диспетчера сегментов создайте экземпляр C
с помощью распределителя.
C *pC = segment.construct<C>("CInst")(allocM_inst, allocQ_inst);
Я думаю, что это должно было сделать трюк. ПРИМЕЧАНИЕ. Вам нужно будет предоставить два распределителя (один для queue
и один для map
), не уверен, что вы можете построить два распределителя из одного и того же менеджера сегментов, но я не понимаю, почему нет.
Ответ 2
Это может быть сложно. Для начала вам понадобится специальный распределитель: Boost
У Interprocess есть один, и я бы начал с него. В вашем точном примере,
этого может быть достаточно, но в более общем плане вам необходимо убедиться в том, что
все подтипы также используют общую память. Таким образом, если вы хотите
строка, для этой строки также потребуется специальный распределитель, что означает
что он имеет другой тип, чем std::string
, и вы не можете копировать или
назначьте его с помощью std::string
(но вы можете использовать два итератора
конструктор, например:
typedef std::basic_string<char, std::char_traits<char>, ShmemAllocator> ShmemString;
std::map<ShmemString, X, std::less<ShmemString>, ShmemAllocator> shmemMap;
с доступом вроде:
shmemMap[ShmemString(key.begin(), key.end())] ...
И, конечно, любые типы, которые вы определяете, которые входят в карту, также должны использовать
разделяемая память для любых распределений: Boost Interprocess имеет
offset_ptr
, который может помочь здесь.
Ответ 3
Построение и использование STL-объектов в общей памяти еще не сложно (особенно с использованием boost:: interprocess wrappers). Конечно, вы также должны использовать механизмы синхронизации (также не проблема с boost named_mutex).
Реальная задача - сохранить согласованность объектов STL в общей памяти. В принципе, если один из процессов выходит из строя в плохую точку во времени, он оставляет другие процессы с двумя большими проблемами:
-
Заблокированный мьютекс (может быть разрешен с использованием сложных сопоставлений PID-to-mutex, надежных мьютексов (где это возможно), временных мьютексов и т.д.
-
Объект STL в несогласованном состоянии (например, полу-обновленная структура карты во время процедуры erase()). В общем, это еще не восстанавливается, вам нужно уничтожить и перестроить объект в области разделяемой памяти с нуля (возможно, убивая все остальные процессы). Вы можете попытаться перехватить все возможные внешние сигналы в вашем приложении и скрестить пальцы, надеясь, что все будет хорошо, и процесс не завершится неудачно в плохой момент.
Просто помните об этом при принятии решения об использовании разделяемой памяти в вашей системе.