¿Cómo almacenar objetos stl en la memoria compartida(C++)?
shared-memory (3)
Tengo el siguiente patrón de código:
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;
...
};
Ahora quiero almacenar un objeto de tipo C en una memoria compartida, de modo que diferentes procesos puedan anexar, actualizar y leer. ¿Cómo puedo hacer esto? (Nota: sé cómo almacenar una simple matriz C que tiene un tamaño fijo en la memoria compartida. Además, recuerde que B.table puede tener entradas arbitrarias.
Esto puede ser complicado. Para empezar, necesitará un asignador personalizado: Boost Interprocess tiene uno, y yo comenzaría con él. En su ejemplo exacto, esto puede ser suficiente, pero en general, deberá asegurarse de que todos los subtipos también utilicen la memoria compartida. Por lo tanto, si desea mapear desde una cadena, esa cadena también necesitará un asignador personalizado, lo que significa que tiene un tipo diferente de std::string
, y no puede copiarlo o asignarlo desde una std::string
(pero puede usar el constructor de dos iteradores, por ejemplo:
typedef std::basic_string<char, std::char_traits<char>, ShmemAllocator> ShmemString;
std::map<ShmemString, X, std::less<ShmemString>, ShmemAllocator> shmemMap;
con accesos como:
shmemMap[ShmemString(key.begin(), key.end())] ...
Y, por supuesto, cualquier tipo que defina que vaya al mapa también debe usar memoria compartida para cualquier asignación: Boost Interprocess tiene un offset_ptr
que puede ayudar aquí.
Use boost :: interprocess , esta biblioteca expone esta funcionalidad.
EDITAR: Aquí hay algunos cambios que deberá hacer:
El ejemplo ya define un asignador que se asignará desde el bloque de memoria compartida, debe pasar esto al map
y a la queue
. Esto significa que tendrá que cambiar sus definiciones:
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)
{ }
}
Para queue
, esto es un poco complicado, debido al hecho de que en realidad es solo un adaptador, por lo que debe pasar la clase de implementación real como un parámetro de plantilla:
typedef queue<int, deque<int, QueueShmemAllocator> > QueueType;
Ahora su clase C
cambia ligeramente:
class C
{
B entries;
QueueType d, e;
C(MapShmemAllocator& allocM, QueueShmemAllocator& allocQ) : entries(allocM), d(allocQ), e(allocQ)
{ }
}
Ahora desde el administrador de segmento, construye una instancia de C
con el asignador.
C *pC = segment.construct<C>("CInst")(allocM_inst, allocQ_inst);
Creo que debería hacer el truco. NOTA: Necesitará proporcionar dos asignadores (uno para la queue
y otro para el map
), sin estar seguro si puede construir dos asignadores del mismo administrador de segmentos, pero no veo por qué no.
La creación y el uso de objetos STL en la memoria compartida aún no es complicado (especialmente con el uso de contenedores boost :: interprocess). Seguro también deberías usar mecanismos de sincronización (tampoco es un problema con el named_mutex de boost).
El verdadero desafío es mantener la consistencia de los objetos STL en una memoria compartida. Básicamente, si uno de los procesos falla en un punto malo en el tiempo, deja a otros procesos con dos grandes problemas:
Un mutex bloqueado (se puede resolver usando mapeos PID a mutex difíciles, mutexes robustos (donde estén disponibles), mutexes temporizados, etc.
Un objeto STL en el estado incoherente (por ejemplo, estructura de mapa semi-actualizada durante el procedimiento de borrado). En general, esto aún no es recuperable, necesita destruir y volver a construir el objeto en una región de memoria compartida desde el principio (probablemente también matando a todos los demás procesos). Puede tratar de interceptar todas las señales externas posibles en su aplicación y cruzar los dedos para que todo salga bien y el proceso nunca falle en un mal momento.
Solo tenga esto en cuenta cuando decida usar memoria compartida en su sistema.