【问题标题】:is it possible to place std::vector to shared memory?是否可以将 std::vector 放置到共享内存中?
【发布时间】:2011-07-18 19:52:58
【问题描述】:

我会使用 CreateFileMapping() windows API 函数在共享内存中创建 std::vector。我知道如何创建共享内存并对其进行管理,但是如何将 std::vector 放置到内存中的固定地址? 在我的情况下,我不能使用 boost 或其他库,我使用的是 CBuilder++ 2010。 我认为可能使用的一种变体

std::vector<int> myVec; 
myVec *mv;
mv = shared_memory_addr ?

但是我如何检测向量的实际大小以调整内存大小?

【问题讨论】:

标签: c++ stl


【解决方案1】:

使用placement new 在共享内存中构造一个向量。您还需要一个用于向量的分配器,以便它可以使用共享内存来存储其元素。如果向量只是存储 int,并且您可以将共享内存部分放在每个进程中的相同虚拟地址,这可能会起作用。

【讨论】:

  • +1 将共享内存放置在相同的逻辑地址是这项工作的必要条件。
  • 我认为它可以与您可以控制分配器的任何对象一起使用(并且通常没有指向非映射内存的指针)...所以 std::vector> 例如就可以了。
  • 而且您不能将对象与 vtables 一起使用(因此有关于“仅存储 int”的评论)。
  • 正如 Ben 所说,这仅在您有固定映射时才有效。根据man mmap(参见 MAP_FIXED),这是“不推荐的”。您可能会发现自己(在某些过程中)无法将数据映射到需要的位置,在这种情况下,此解决方案将失败。
  • 我存储的是我自己的类的项目,而不是整数,顺便说一句,我如何在这里使用“新”?据我所知,它是从堆中分配的,或者我可以重载 operator new?
【解决方案2】:

您需要实现自己的分配器来实现这一点。分配器是一个std::vector&lt;&gt; 模板参数。

【讨论】:

  • 这不是真的:已经为此目的制作了分配器,无需自己实现(请参阅我关于 Boost Interprocess 的回答)。此外,这个答案还不够:正如 Ben 在另一条评论中提到的那样,必须警惕在不同进程中将共享内存映射到不同基址(这是难以避免的幼稚解决方案的陷阱)。
  • “已经为此目的制作了分配器,无需自己实现” ... 您自己的暗示使用标准库中未内置的任何内容在我的回答中。
【解决方案3】:

我会使用 Boost.Interprocess,它解释了如何做到这一点: http://www.boost.org/doc/libs/1_38_0/doc/html/interprocess/quick_guide.html#interprocess.quick_guide.qg_interprocess_container

请注意,这不使用std::vector&lt;&gt;,它不适合共享内存使用,因为它通常是根据三个指针(开始、结束、容量或某些等价物)实现的,并且地址之间会有所不同过程。所以 Boost.Interprocess 有它自己的向量类,它是专门为你想要做的事情而构建的。

【讨论】:

  • std::vector 的指针问题通过在每个进程中将共享内存映射到相同的虚拟地址来缓解。
  • 谢谢,但我不能使用 boost,因为 cbuilder 2010 泄漏了用于新 boost 的 c++ 模板支持。
  • @Adam Mitz:这可能不可行:man mmap 明确表示“不推荐使用MAP_FIXED”。您可能会发现自己处于“正确”地址范围已被映射的情况,然后您将失败。
  • @Sergey:你知道你的工具链支持的最新版本的 Boost 是什么吗?进程间是 Boost 的一个相当新的部分,但不是最近的。您可能还会发现您的编译器可以处理“足够”的 Boost 以使 Interprocess 工作,即使它不能处理所有 Boost。
  • @Zwinck: CreateFileMapping() 是 Win32 API,所以 mmap() 不能在这里播放。如果在您完全控制的过程中仔细完成,您可以安排一个通用映射。
【解决方案4】:

实际上,您必须同时做这两件事:使用placement new 在共享内存中构造std::vector 实例,并使用自定义分配器使向量也将其数据放置在共享内存中。

请记住,您需要同步对向量的任何访问(除非您只需要读取访问)-std::vector 通常不是线程安全的,并且不会声明其任何成员 volatile,这使得同时访问编译器范围之外 - 就像它发生在共享内存区域中一样 - 非常危险。

...毕竟,我不会这样做。共享内存是一个非常低级、非常棘手的概念,它不适合高级数据容器,例如std::vector,在一种语言中(从 cpp03 开始​​)不能为并发问题提供良好的内置解决方案并且不知道存在共享内存之类的东西。

...它甚至可能触发未定义的行为:虽然std::vector 通常使用其allocator 为其元素获取存储空间,但它(据我所知)允许进一步分配使用malloc 或任何其他分配策略(我认为微软的std::vector 实现在调试版本中做到这一点)的内存(即用于内部目的,无论可能是什么)......这些指针仅对内存的一侧有效映射。

为了避免std::vector,我只需预先在映射范围内分配足够的内存,并使用一个简单的计数器来保持有效元素的数量。那应该是安全的。

【讨论】:

  • 并且您需要确保映射发生在同一个地址。关于同步的要点。
  • 谢谢亚历山大,是的,我正在寻找两个可能的答案:在我的 dll 的每个进程附加中使用一个 std::vector 我可以使用命名管道(这是创建服务器线程和客户端进程)或使用共享内存。共享内存方法的代码更少,但现在我想我会回到命名管道和服务器/客户端线程......
【解决方案5】:

您可以使用共享内存,映射到一个固定地址,也就是说,每个进程中的地址都是相同的,允许使用原始指针。

因此,如果您执行以下操作,您可以拥有(多个)共享内存区域:

#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());
    }
};

那么,你可以使用:

       std::vector<int, SharedMemoryAllocator<MySegment, int> vec;

并且向量的元素将被放入共享内存中(当然,vec 也必须在那里分配)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-09
    • 2015-02-19
    • 2021-03-13
    • 2021-11-23
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多