【问题标题】:Can boost's interprocess segment manager allocators be themselves shared with other processes?boost 的进程间段管理器分配器可以自己与其他进程共享吗?
【发布时间】:2017-12-12 11:16:16
【问题描述】:

我正在使用 boost::interprocess 创建一个共享的进程间映射。 为此,我从映射所在的共享内存段的 segment_manager 创建了一个分配器。

映射的元素值类型是一个基本字符串,它本身被模板化为使用从同一段管理器创建的字符分配器。 在一个过程中,我创建地图,而在另一个过程中,我在该过程中使用地图迭代器搜索项目,在某些情况下,我使用迭代器调用 map::erase。

这会导致访问冲突异常,并且我知道我正在使用有效的迭代器进行调用。 访问冲突发生在对作为迭代器指向的“对”的“第二个”的 basic_string 的析构函数的调用中。 当我在插入后立即在写入过程中使用迭代器执行相同的擦除操作时,没有访问冲突。

看起来读取进程试图使用元素的分配器释放元素的内存,该分配器是在写入过程中创建的,而该分配器仅在创建它的进程中有效。

这是否意味着分配器本身不能共享?

我希望分配器在两个进程中都可用,因为它的状态应该只包含在两个进程中都有效的相对指针。 如果不是,我如何在进程之间共享使用共享内存(堆)分配的元素? 在将它们传递给 basic_string 元素之前,我是否应该在编写过程中以特殊方式创建这些分配器,以允许我在另一个进程的 anerase 操作中使用它们?

还有什么可能导致访问冲突?

【问题讨论】:

  • 字符串是在分配器上模板化的,但是您是在每次创建字符串时传递对分配器实例的引用,还是只是让它默认构造?如果你能分享一些你的代码,那会很有帮助。
  • 你不能让它默认构造,因为没有默认构造函数@Frank
  • @David 不用多说,只显示出现问题的代码。因为现在我们只能猜测,这不会导致最有用的 cmets/answers。不过我试过了。
  • 嗨弗兰克和sehe,感谢您的帮助,很抱歉没有提供代码。代码相当长,我不确定要引用哪一部分。我最终解决了这个问题,我将很快解释解决方案,以及对问题的更好解释。

标签: c++ boost shared-memory interprocess


【解决方案1】:

分配器很好(魔法在offset_ptr 中,并且跨进程边界是透明的)。

如果“客户端”破坏了字符串,那么您正在做的不是阅读。您很可能会收到一份副本,例如:

auto by_copy = smap.find(key)->second; // makes a copy

试试,例如去做

auto const& by_ref = smap.find(key)->second; // doesn't copy

或者,您可能正在执行smap[key],如果密钥不存在,它会自动分配。这可能会导致老式的竞争条件(在进程之间共享数据很像在线程之间共享数据:您需要适当的同步)。

最后,你没有提到 /anything/ 关于密钥,但如果它也是一个字符串,那么只有按密钥查找很容易从共享内存中分配(而且,它是一个临时的,它会破坏)。比赛条件再次迫在眉睫。另见want to efficiently overcome mismatch between key types in a map in Boost.Interprocess shared memory

演示

如果没有合适的SSCCEMCVE,让我向你扔一个。您可能会发现自己的做法有所不同。

#include <iostream>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/map.hpp>

namespace bip = boost::interprocess;

namespace shared {
    namespace bc  = boost::container;

    using Segment = bip::managed_shared_memory;
    using Manager = Segment::segment_manager;
    template <typename T>
    using Alloc   = bc::scoped_allocator_adaptor<bip::allocator<T, Manager> >;

    using String  = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;
    template <typename K, typename V, typename Cmp = std::less<K> >
        using Map = bip::map<K, V, Cmp, Alloc<std::pair<K const, V> > >;
};

int main() {
    using namespace shared;

    Segment smt(bip::open_or_create, "de06c60a-0b80-4b20-a805-b3f405f35427", 20ul<<20); // 20 mb
    auto& mat = *smt.find_or_construct<Map<String, String> >("dict")(smt.get_segment_manager());

    if (mat.empty()) {
        mat.emplace("1", "one");
        mat.emplace("2", "two");
        mat.emplace("3", "three");
    } else {
        // shared string factory
        auto ss = [&](auto... stuff) { return String(stuff..., smt.get_segment_manager()); };

        auto  copy = mat.at(ss("3")); // constructs and destructs temp String("3"); constructs copy
        auto& ref  = mat.at(ss("2")); // constructs and destructs temp String("2"); no copy
        std::cout << "copy: " << copy << "\n";
        std::cout << "ref: "  << ref  << "\n";

        // iterate with no shared temps or copies:
        for (auto& p : mat)
            std::cout << "entry '" << p.first << "' -> '" << p.second << "'\n";
    } // destructs copy
}

在 Coliru 上也是如此,但使用内存映射文件(因为那里不允许共享内存):

Live On Coliru

using Segment = bip::managed_mapped_file;

第一次运行不打印,后续运行:

copy: three
ref: two
entry '1' -> 'one'
entry '2' -> 'two'
entry '3' -> 'three'

【讨论】:

【解决方案2】:

是的,boost 提供的用于共享内存的分配器可以被其他进程使用。 我将首先介绍它的简短内容,然后更好地解释最初的问题是什么。

简而言之——我使用的是用 char、char_traits 和共享内存分配器实例化的 basic_string。我应该改用 boosts boost::interprocess::basic_string 。

这里有更多细节。

在我的代码中,我正在使用 boost 的进程间映射创建一个共享映射。该地图中的元素是字符串。 我正确使用 boosts 段管理器为映射创建分配器,并使用相同的段管理器为放置在该映射中的字符串创建一个字符分配器。

但是,我在地图中创建和存储的字符串是模板化类型 std::basic_string。我正确地为这个模板提供了分配器类型,并且我正确地为它的构造函数中的字符串提供了分配器实例。

此外,存储在映射中的字符串可以被另一个打开共享内存的处理器读取。但是,当读取过程试图擦除条目时,因此删除字符串时存在访问冲突。

用 boost::interprocess::basic_string 替换 basic_string 后,这个问题就消失了。

如果我知道 boost 在进程间命名空间中有自己的 basic_string 我肯定会使用它;我已经知道我需要使用 boost 的地图而不是标准地图。我只是不知道有一个 interprocess::basic_string

作为旁注,我仍然想知道为什么 std 容器显然不适合与共享内存的分配器一起使用,即使它们确实提供了允许实现者指定应该使用哪个分配器的模板参数

【讨论】:

  • 我认为 Scott Meyers 引用 here 是最终的原因。它解释了为什么 Boost Container 将其作为设计目标“容器的内部指针应该是类型 allocator::pointer 并且容器可能不会假设 allocator::pointer 是原始指针。”跨度>
  • 这里最重要的一课是代码对话。如果您显示了代码,我们可以立即指出它。为了好玩,我走得更远,原型化了a map that allows heterogeneous lookup(使用多索引)。将统计信息/跟踪与regular map&lt;&gt; 进行比较。请注意异构查找键的效率优势。
猜你喜欢
  • 1970-01-01
  • 2012-09-18
  • 2013-12-08
  • 1970-01-01
  • 2014-04-22
  • 1970-01-01
  • 1970-01-01
  • 2014-11-26
  • 1970-01-01
相关资源
最近更新 更多