SharedMemory 容器问题

SharedMemory container problems

我创建了以下代码来测试共享内存分配器和容器..

分配器(基本分配器,只保留指向内存块的指针 + 大小:

template<typename T>
struct SharedMemoryAllocator
{
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T value_type;

    void* memory;
    std::size_t size;


    SharedMemoryAllocator(void* memory, std::size_t size) noexcept : memory(memory), size(size) {};
    SharedMemoryAllocator(const SharedMemoryAllocator& other) noexcept : memory(other.memory), size(other.size) {};

    template<typename U>
    SharedMemoryAllocator(const SharedMemoryAllocator<U>& other) noexcept : memory(other.memory), size(other.size) {};

    template<typename U>
    SharedMemoryAllocator& operator = (const SharedMemoryAllocator<U>& other) { return *this; }
    SharedMemoryAllocator<T>& operator = (const SharedMemoryAllocator& other) { return *this; }
    ~SharedMemoryAllocator() {}

    pointer address(reference value) const {return &value;}
    const_pointer address(const_reference value) const {return &value;}

    pointer allocate(size_type n, const void* hint = 0) {return static_cast<T*>(memory);}
    void deallocate(T* ptr, size_type n) {}

    template<typename U, typename... Args>
    void construct(U* ptr, Args&&... args) {::new(static_cast<void*>(ptr)) U(std::forward<Args>(args)...);}
    void construct(pointer ptr, const T& val) {new(static_cast<T*>(ptr)) T(val);}

    template<typename U>
    void destroy(U* ptr) {ptr->~U();}
    void destroy(pointer ptr) {ptr->~T();}

    size_type max_size() const {return size / sizeof(T);}

    template<typename U>
    struct rebind {typedef SharedMemoryAllocator<U> other;};
};

template <typename T, typename U>
inline bool operator == (const SharedMemoryAllocator<T>& a, const SharedMemoryAllocator<U>& b)
{
    return (a == b);
}

template <typename T, typename U>
inline bool operator != (const SharedMemoryAllocator<T>& a, const SharedMemoryAllocator<U>& b)
{
    return !(a == b);
}

容器(只是一个使用 SharedMemory 分配器分配内存的容器):

template<typename T, typename allocator = std::allocator<T>>
class CVector
{
private:
    T* memory;
    std::size_t size, capacity;
    allocator alloc;

public:
    CVector() : memory(nullptr), size(0), capacity(0), alloc(allocator()) {}
    CVector(const allocator &alloc) : memory(nullptr), size(0), capacity(0), alloc(alloc) {}
    ~CVector()
    {
        if(memory)
        {
            for(std::size_t i = 0; i < this->size; ++i)
            {
                alloc.destroy(memory + i);
            }

            alloc.deallocate(memory, capacity);
            memory = nullptr;
        }
    }

    void reserve(std::size_t size)
    {
        if(capacity < size)
        {
            capacity = size;
            void* mem = alloc.allocate(capacity);

            if(memory && memory != mem)
            {
                memcpy(static_cast<char*>(mem), memory, size * sizeof(T));

                for(std::size_t i = 0; i < this->size; ++i)
                {
                    alloc.destroy(memory + i);
                }

                alloc.deallocate(memory, capacity);
                memory = nullptr;
            }

            memory = static_cast<T*>(mem);
        }
    }

    void push_back(T&& value)
    {
        if(capacity == 0)
        {
            reserve(1);
        }

        if(size >= capacity)
        {
            reserve(capacity * 2);
        }

        alloc.construct(memory + size++, value);
    }

    T& operator[](std::size_t size)
    {
        return *(memory + size);
    }

    const T& operator[](std::size_t size) const
    {
        return *(memory + size);
    }
};

主要:

int main()
{
    MemoryMap mem{"Local\Test", 5000, std::ios::in | std::ios::out};
    mem.open();
    mem.map();

    typedef CVector<int, SharedMemoryAllocator<int>> SHMVec;


    SHMVec* vec = ::new(mem.data()) SHMVec(SharedMemoryAllocator<int>(static_cast<char*>(mem.data()) + sizeof(SHMVec), 1024 - sizeof(SHMVec)));

    vec->reserve(100);
    vec->push_back(100);
    vec->push_back(200);
    vec->push_back(300);

    std::cout<<"Address: "<<mem.data()<<"\n";
    std::cin.get();

    SHMVec* ptrVec = reinterpret_cast<SHMVec*>(mem.data());
    std::cout<<(*ptrVec)[0];


    vec->~SHMVec();
}

我在某处读到 std::vector 不能放在 SharedMemory 中,因为它可能会在当前进程的地址 space 中进行一些自己的跟踪。所以我决定写我自己的 "vector" 这只是一个便宜的 class.

接下来,我分配一个共享内存块,并将容器构建到该块中,如上文 "main" 所示。

在另一个程序中,我做(主要):

int main()
{
    MemoryMap mem{"Local\Test", 5000, std::ios::in};
    mem.open();
    mem.map();

    typedef CVector<int, SharedMemoryAllocator<int>> SHMVec;

    std::cout<<"Address: "<<mem.data()<<"\n";
    SHMVec* ptrVec = reinterpret_cast<SHMVec*>(mem.data());
    std::cout<<(*ptrVec)[0];
}

当两个程序都将共享内存块映射到 0x370000 时,它就可以工作了。但是,如果一个程序在 0x370000 处分配 SharedMemoryBlock,而第二个程序在 0x380000 处分配 SharedMemoryBlock,则它会崩溃(第二个程序在尝试访问第一个创建的容器时崩溃)。

知道为什么会这样吗?容器位于共享内存块中。为什么块地址必须完全相同很重要?

这是您的问题:

template<typename T, typename allocator = std::allocator<T>>
class CVector
{
private:
    T* memory;
    ^^^^^^^^^^^

因为在您的程序中,SHMVector 对象本身存储在共享内存中,所以您将其数据成员存储在共享内存中。因此,指向元素的指针(在本例中为 memory)存储在共享内存中。

如果共享内存段加载到不同的地址,那么 memory 将指向两个程序之一的内存 space 中的无效地址。

可能是简单的解决方案:不要将 SHMVec 对象本身放在共享内存中,只将元素放在共享内存中。

boost::interprocess 可以帮助您将容器保存在共享内存中 - 请参阅 http://www.boost.org/doc/libs/1_38_0/doc/html/interprocess/allocators_containers.html#interprocess.allocators_containers.containers_explained.containers