无法在共享内存

时间:2017-10-31 01:25:17

标签: c++ boost shared-memory boost-interprocess

我想使用运行时指定的最大条目数在共享内存中创建一个无锁环缓冲区。我的代码基于我在GitHub中找到的示例。我使用此代码在共享内存中成功创建了一个无锁环缓冲区 在我的例子中,我需要指定环形缓冲区在运行时构造时可以接受的最大条目数,而不是在每个示例的编译时。下面显示了在示例中构造shm::ring_buffer的调用。

namespace bip = boost::interprocess;

namespace shm
{
    using char_alloc = bip::allocator<char, bip::managed_shared_memory::segment_manager>;
    using shared_string = bip::basic_string<char, std::char_traits<char>, char_alloc>;
    using ring_buffer = boost::lockfree::spsc_queue<shared_string, boost::lockfree::capacity<200>>;
}

共享内存段按如下方式分配:

mQueuingSharedMem = std::make_unique<bip::managed_shared_memory>(
    bip::open_or_create, (mSharedMemoryName + "Queuing").c_str(), rSHMSize);

根据GitHub示例,当我通过可选的boost::lockfree::capacity<>模板参数构造在编译时指定的最大大小的环形缓冲区时,一切正常(注意:共享内存段construct方法需要[]中的#ring_buffers和构造函数参数在后面的括号中指定。

auto pSharedMemAddr = mQueuingSharedMem->construct<
   shm::ring_buffer>(rQueuingPortName.c_str())[1](/*aMaxNumMessages*/);

我认为为了在运行时构造上面的shm :: ring_buffer,我需要从boost::lockfree::capacity<200>中删除第二个shm::spsc_queue硬编码大小参数,而是传递{的最大大小{1}}和shm::ring_buffer的共享内存分配器。我找到了类似的答案here,但我无法使其适应我的代码。

我对上面的代码进行了以下更改,试图在运行时指定环形缓冲区的大小:

shm::shm_string

我得到了大量无法理解的编译器错误,我不太清楚如何修复:

namespace bip = boost::interprocess;
namespace shm
{
    using char_alloc = bip::allocator<char, bip::managed_shared_memory::segment_manager>;
    using shared_string = bip::basic_string<char, std::char_traits<char>, char_alloc>;
    using ring_buffer = boost::lockfree::spsc_queue<shared_string/*, boost::lockfree::capacity<200>*/>;
}

    shm::char_alloc char_alloc(mQueuingSharedMem->get_segment_manager());
    auto pSharedMemAddr = mQueuingSharedMem->construct<
        shm::ring_buffer>(rQueuingPortName.c_str())[1](aMaxNumMessages);

1 个答案:

答案 0 :(得分:1)

动态大小仅在您指定进程间内存分配器时才有效:http://www.boost.org/doc/libs/1_65_1/doc/html/boost/lockfree/allocator.html

可悲的是,虽然spsc_queue does support有状态分配器:

  

定义分配器。 boost.lockfree支持有状态分配器,并与Boost.Interprocess分配器兼容

它似乎不支持将分配器传递给它的元素(uses_allocator<>)所需的shared_string协议,即使使用scoped_allocator_adaptor¹也不行。

我之前碰到过这个:

所以我的建议是删除其中一个成分:

  • 使元素成为非动态容器(容器容器需要范围分配器感知或显式元素构造(
  • 使队列固定大小(这通常是共享内存情况的好主意,IYAM)
  • 添加一层间接...

对于后者,您可以将队列存储为manged bip::shared_ptr<shared_string>而不是:

<强> Live On Coliru

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/managed_mapped_file.hpp> // for Coliru
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>

#include <boost/lockfree/spsc_queue.hpp>

#include <iostream>

/// noisy - traces special members
struct noisy {
    noisy& operator=(noisy&&) noexcept { std::cout << "operator=(noisy&&)\n"; return *this;      } 
    noisy& operator=(const noisy&)     { std::cout << "operator=(const noisy&)\n"; return *this; } 
    noisy(const noisy&)                { std::cout << "noisy(const noisy&)\n";                   } 
    noisy(noisy&&) noexcept            { std::cout << "noisy(noisy&&)\n";                        } 
    ~noisy()                           { std::cout << "~noisy()\n";                              } 
    noisy()                            { std::cout << "noisy()\n";                               } 
};

namespace bip = boost::interprocess;
namespace blf = boost::lockfree;

namespace Shared {
    using Segment = bip::managed_mapped_file; // Coliru unsupported: managed_shared_memory;
    using Manager = Segment::segment_manager;
    template <typename T> using Alloc = bip::allocator<T, Manager>;

    using String = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;

    // using Element = String;
    // For debug/demonstration
    struct Element : String, noisy { using String::String; }; // inherit constructors

    using Ptr = bip::managed_shared_ptr<Element, Segment>::type;

    using Buffer = blf::spsc_queue<Ptr, blf::allocator<Alloc<Ptr> > >;
}

static std::string unique_id_gen() {
    static std::atomic_size_t s_gen { 0 };
    return "buffer_element" + std::to_string(++s_gen);
}

int main() {
    struct shm_remove {
        shm_remove() { bip::shared_memory_object::remove("MySharedMemory"); }
        ~shm_remove() { bip::shared_memory_object::remove("MySharedMemory"); }
    } remover;

    Shared::Segment segment(bip::create_only, "MySharedMemory", 4 << 20);

    auto& buffer = *segment.construct<Shared::Buffer>(bip::unique_instance)[1](20, segment.get_segment_manager());

    auto create = [&segment](auto&&... args) {
        return make_managed_shared_ptr(segment.construct<Shared::Element>(unique_id_gen().c_str())
                (
                    std::forward<decltype(args)>(args)...,
                    segment.get_segment_manager()
                ), segment);
    };

    std::cout << "Pushing\n";

    for (auto msg : { "hello", "world", "bye", "cruel", "world" })
        buffer.push(create(msg));

    std::cout << "Popping\n";
    {
        Shared::Ptr into;
        while (buffer.pop(into)) {
            std::cout << "Popped: '" << *into << "'\n";
        }
        std::cout << "Going out of scope\n";
    } // RAII
    std::cout << "Out of scope\n";

    {
        // make sure any other owned queue elements are freed if the queue is destroyed before it's empty:
        for (auto msg : { "HELLO", "WORLD", "BYE", "CRUEL", "WORLD" })
            buffer.push(create(msg));

        std::cout << "Destroying buffer containing 5 elements\n";
        segment.destroy<Shared::Buffer>(bip::unique_instance);
    }
}

打印:

Pushing
noisy()
noisy()
noisy()
noisy()
noisy()
Popping
Popped: 'hello'
~noisy()
Popped: 'world'
~noisy()
Popped: 'bye'
~noisy()
Popped: 'cruel'
~noisy()
Popped: 'world'
Going out of scope
~noisy()
Out of scope
noisy()
noisy()
noisy()
noisy()
noisy()
Destroying buffer containing 5 elements
~noisy()
~noisy()
~noisy()
~noisy()
~noisy()

¹搜索我的答案,了解如何在共享内存中使用其他容器容器