memoria - tamaño de un vector dinamico c++
¿Es posible colocar std:: vector en la memoria compartida? (5)
Yo crearía std :: vector en memoria compartida usando la función de API de Windows CreateFileMapping (). Sé cómo crear memoria compartida y administrarla, pero ¿cómo colocar std :: vector en una dirección fija en la memoria? No puedo usar boost u otras bibliotecas en mi caso, estoy usando CBuilder ++ 2010. Una variante creo que es tal vez el uso
std::vector<int> myVec;
myVec *mv;
mv = shared_memory_addr ?
Pero, ¿cómo puedo detectar el tamaño real de los vectores para cambiar el tamaño de la memoria?
Necesita implementar su propio asignador para lograr eso. El asignador es un parámetro de plantilla std::vector<>
.
Puede usar la memoria compartida, mapeada en una dirección fija , es decir, la dirección sería la misma en cada proceso que permite el uso de punteros sin procesar.
Entonces, si haces algo como lo siguiente, puedes tener (múltiples) regiones de memoria compartida:
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
struct MySegment {
static const size_t alloc_size = 1048576;
static const void *getAddr() { return (void *)0x400000000LL; }
static const char *getSegmentName() { return "MySegment"; }
};
template <typename MemorySegment>
class SharedMemory {
public:
typedef boost::interprocess::fixed_managed_shared_memory shared_memory_t;
typedef shared_memory_t::segment_manager segment_manager_t;
static shared_memory_t *getSegment() {
if (!segment) {
assert(MemorySegment::getAddr() != 0 && "want a fixed address for all processes");
segment = new boost::interprocess::managed_shared_memory(
boost::interprocess::open_or_create,
MemorySegment::getSegmentName(),
MemorySegment::alloc_size,
MemorySegment::getAddr());
}
return segment;
}
static segment_manager_t *getSegmentManager() {
return getSegment()->get_segment_manager(); }
private:
static boost::interprocess::managed_shared_memory *segment;
};
template <typename MemorySegment>
typename SharedMemory<MemorySegment>::shared_memory_t *SharedMemory<MemorySegment>::segment = NULL;
template <class MemorySegment, class T>
class SharedMemoryAllocator {
public:
typedef boost::interprocess::allocator<T, typename SharedMemory<MemorySegment>::segment_manager_t> InterprocessAllocator;
// Delegate all calls to an instance of InterprocessAllocator,
pointer allocate(size_type n, const void *hint = 0) { return TempAllocator().allocate(n, hint); }
void deallocate(const pointer &p, size_type n) { return TempAllocator().deallocate(p, n); }
size_type max_size() const { return TempAllocator().max_size(); }
void construct(const pointer &ptr, const_reference v) { return TempAllocator().construct(ptr, v); }
void destroy(const pointer &ptr) { return TempAllocator().destroy(ptr); }
typedef typename InterprocessAllocator::value_type value_type;
typedef typename InterprocessAllocator::pointer pointer;
typedef typename InterprocessAllocator::reference reference;
typedef typename InterprocessAllocator::const_pointer const_pointer;
typedef typename InterprocessAllocator::const_reference const_reference;
typedef typename InterprocessAllocator::size_type size_type;
typedef typename InterprocessAllocator::difference_type difference_type;
SharedMemoryAllocator() {}
template <class U> SharedMemoryAllocator(const SharedMemoryAllocator<MemorySegment, U> &u) {}
template <typename OtherT> struct rebind { typedef SharedMemoryAllocator<MemorySegment, OtherT> other; };
private:
static InterprocessAllocator TempAllocator() {
return InterprocessAllocator(SharedMemory<MemorySegment>::getSegmentManager());
}
};
Entonces, podrías usar:
std::vector<int, SharedMemoryAllocator<MySegment, int> vec;
y los elementos del vector se pondrán en la memoria compartida (por supuesto, vec
también tendrá que asignarse allí).
Usaría Boost.Interprocess, que tiene una explicación de cómo hacer esto: http://www.boost.org/doc/libs/1_38_0/doc/html/interprocess/quick_guide.html#interprocess.quick_guide.qg_interprocess_container
Tenga en cuenta que esto no utiliza std::vector<>
, que no es adecuado para el uso de memoria compartida, ya que normalmente se implementa en términos de tres punteros (inicio, fin, capacidad o algunos equivalentes), y las direcciones diferirán entre procesos. Así que Boost.Interprocess tiene su propia clase vectorial, que está especialmente diseñada para lo que estás tratando de hacer.
Use la colocación nueva para construir un vector en la memoria compartida. También necesitará un asignador para el vector para que pueda usar memoria compartida para su almacenamiento de elementos. Si el vector solo almacena int, y puede poner la sección de memoria compartida en la misma dirección virtual en cada proceso, esto podría funcionar.
En realidad, tiene que hacer ambas cosas : use la ubicación nueva para construir la instancia std::vector
en la memoria compartida Y use un asignador personalizado para que el vector también coloque sus datos dentro de la memoria compartida.
Tenga en cuenta que necesita sincronizar cualquier acceso al vector (excepto si solo necesita acceso de lectura) - std::vector
no es generalmente seguro para subprocesos y no declara ninguno de sus miembros volatile
, lo que hace que el acceso simultáneo sea el alcance del compilador, como ocurre en una región de memoria compartida, es extremadamente peligroso.
... después de todo, no lo haría . La memoria compartida es un concepto de muy bajo nivel y muy complicado, no se adapta bien a contenedores de datos de alto nivel como std::vector
, en un lenguaje que (a partir de cpp03) no ofrece buenas soluciones integradas para la concurrencia problemas y que no es consciente de que existe algo así como la memoria compartida.
... incluso podría desencadenar un comportamiento indefinido : mientras que std::vector
generalmente utiliza su allocator
para buscar el almacenamiento de sus elementos, está (hasta donde sé) permitido asignar más memoria (es decir, para fines internos, cualquiera que sea ) utilizando malloc
o cualquier otra estrategia de asignación (creo que la implementación de std::vector
Microsoft hace eso en compilaciones de depuración) ... estos punteros solo serían válidos para un lado de la asignación de memoria.
Para evitar std::vector
, simplemente asignaría suficiente memoria en el rango mapeado por adelantado y usaría un contador simple para mantener el número de elementos válidos. Eso debería ser seguro.