【问题标题】:Shared lifecycle cross processes with boost::interprocess::shared_ptr使用 boost::interprocess::shared_ptr 共享生命周期跨进程
【发布时间】:2015-05-28 11:51:12
【问题描述】:

我正在尝试使用 boost::interprocess 在进程之间共享数据并利用 shared_ptr 进行生命周期管理。我有一个驻留在共享内存中的地图,两个进程应该访问它。

    boost::shared_ptr<boost::interprocess::managed_mapped_file> segment =
          boost::make_shared<boost::interprocess::managed_mapped_file>
                 (boost::interprocess::open_or_create,
                  "./some-mmap.txt", //file name
                  65536);           //segment size in bytes

    pair_allocator_type alloc_inst(segment->get_segment_manager());

    elements = boost::interprocess::make_managed_shared_ptr(
            segment->find_or_construct<map_type>("elements")
            (std::less<IdType>(), alloc_inst),
            *segment
    );

在一个测试程序中,我有一个父进程和一个子进程,它们基本上都使用上面的代码。因此,它们使用相同的底层文件、相同的共享对象名称(“元素”)、相同的类型等。

但是,我注意到每当子进程死亡时,集合的大小就会下降到 0。奇怪。我进行了调查,似乎它与elements 的破坏有关(当此共享指针超出范围时)。每当elements 超出范围时,底层集合的大小就会变为0。

我还看到elements 在父进程和子进程中都有use_count 正好1。对于父母来说这是有道理的,但我不明白为什么孩子会这样。我的假设是当 Child 进程死亡时,use_count 会降为 0,然后集合被清除。

我想要的是当子进程死亡时,指向的对象(地图)不会被破坏。我不应该假设哪些进程是活跃的,哪些不是。

  • 我是否以错误的方式初始化boost::interprocess::shared_ptr
  • 我是否完全错过了此指针的语义 --> 它是否仅用于管理共享内存对象的生命周期在一个进程内而不是跨进程?
  • 如何拥有一个shared_ptr,它的use_count 是跨进程共享的?

编辑 - 收集说明

elements 是一个boost::interprocess::map,它将某个IdType 映射到指向ShmemType 的共享内存共享指针。当 Child 进程死亡时,elements 的大小会降为 0。

typedef boost::interprocess::managed_mapped_file::segment_manager segment_manager_type;
typedef std::pair<const IdType, ShmemType::pointer_type> pair_type;
typedef boost::interprocess::allocator<pair_type, segment_manager_type> pair_allocator_type;
typedef boost::interprocess::map<IdType, ShmemType::pointer_type, std::less<IdType>, pair_allocator_type> map_type;

编辑 - 来自 boost 文档的示例

我从 boost 文档中获取了示例并对其进行了扩展,以追踪我最初问题的根本原因。

#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <string>
#include <cstdlib> //std::system

using namespace boost::interprocess;

typedef allocator<int, managed_mapped_file::segment_manager>  ShmemAllocator;
typedef vector<int, ShmemAllocator> MyVector;

#include <iostream>

//Main function. For parent process argc == 1, for child process argc == 2
int main(int argc, char *argv[])
{
   if(argc == 1){ //Parent process

      //Create a new segment with given name and size
       managed_mapped_file segment(open_or_create, "./a_MySharedMemory.txt", 65536);

      //Initialize shared memory STL-compatible allocator
      const ShmemAllocator alloc_inst (segment.get_segment_manager());

//      MyVector* elements = segment.find_or_construct<MyVector>("some-vector")      //object name
//                      (alloc_inst);
       typedef boost::interprocess::managed_shared_ptr<MyVector, boost::interprocess::managed_mapped_file>::type map_pointer_type;

        map_pointer_type elements = boost::interprocess::make_managed_shared_ptr(
                segment.find_or_construct<MyVector>("some-vector")      //object name
                (alloc_inst),
                segment
        );

      for(int i = 0; i < 100; ++i)  //Insert data in the vector
          elements->push_back(i);

      std::cout << elements->size() << std::endl;
        std::cout << elements->at(0) << std::endl;
        std::cout << elements->at(30) << std::endl;

      //Launch child process
      std::string s(argv[0]); s += " child ";
      if(0 != std::system(s.c_str()))
         return 1;

      std::cout << elements->size() << std::endl;
      std::cout << elements->at(0) << std::endl;
      std::cout << elements->at(30) << std::endl;

   }
   else{ //Child process
      //Open the managed segment
       managed_mapped_file segment(open_only, "./a_MySharedMemory.txt");
       const ShmemAllocator alloc_inst (segment.get_segment_manager());

       typedef boost::interprocess::managed_shared_ptr<MyVector, boost::interprocess::managed_mapped_file>::type map_pointer_type;

       map_pointer_type elements = boost::interprocess::make_managed_shared_ptr(
            segment.find_or_construct<MyVector>("some-vector")      //object name
            (alloc_inst),
            segment
    );

//       MyVector* elements = segment.find_or_construct<MyVector>("some-vector")      //object name
//                      (alloc_inst);

      //Use vector in reverse order
      std::sort(elements->rbegin(), elements->rend());

   }

   return 0;
}

在这种情况下,在子进程死亡后,向量在父进程中的大小 == 0。如果我使用原始指针 (MyVector* elements = segment.find_or_construct...),那么集合可以在父进程中按预期使用。

所以我仍然对共享指针的行为有疑问

【问题讨论】:

  • "集合的大小降至 0" - 哪个集合?
  • 我在文中澄清了。 elements-&gt;size() 在子进程死亡后在父进程中给出 0,即使在子进程中没有修改 elements 映射。
  • 在子进程死亡之前?
  • 是一个。我所做的测试是父添加一个元素,生成一个子进程,子进程读取这个元素,子进程死亡,父进程读取那个元素。最后一次读取应该成功,但它失败了(elements 映射中没有任何内容)。
  • 大小下降到 0 没有意义。这不是智能指针所做的,无论如何都不是没有自定义删除器。您应该检查地图实际上是同一个对象(并且它仍然存在!)。您是否使该细分市场保持足够长的时间? (例如,将分段设为全局以进行测试)

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


【解决方案1】:
  • 问: 我是否以错误的方式初始化 boost::interprocess::shared_ptr

你做得对。不过,您不需要共享指针中的段。只需确保该段的寿命超过任何进程间 shared_ptrs(或者,就此而言,任何对共享内存段的引用)。

  • 问: 我是否完全忽略了这个指针的语义 --> 它是否只用于管理一个进程内的共享内存对象生命周期,而不是跨进程?

没有。

  • 问: 如何让shared_ptruse_count 跨进程共享?

文档:make_managed_shared_ptr

从已在传递的托管段中分配的 T 类型指针返回使用默认分配器和删除器构造的共享指针的实例

other page 明确提到(强调我的):

由于 shared_ptr 所需的引用计数和其他辅助数据也必须在托管段中创建,并且删除器必须从段中删除对象,因此用户必须指定分配器对象并构造 shared_ptr 的非空实例时的删除器对象

【讨论】:

  • 不用说 (?) 我认为您的问题出在其他地方。有关问题,请参阅my comment
  • 鉴于我如何初始化共享指针,辅助数据是否会在托管段中?
  • Y.E.S. 我不能再详细说明了。
【解决方案2】:

我设法解决了这个问题。问题与如何制作共享指针有关

如果您调用boost::interprocess::make_managed_shared_ptr N 次来创建指向共享内存中对象的共享指针,您将获得指向共享内存中相同 对象的本质不同(完全不相关)的共享指针。我是在 Parent 和 Child 进程中这样做的,然后当 Child 进程死亡时,使用计数变为 0 并擦除了指向的对象(地图)。

解决方案是显式创建一个命名共享指针。

typedef boost::interprocess::allocator<void, segment_manager_type>  void_allocator_type;
typedef boost::interprocess::deleter<map_type, segment_manager_type>  map_deleter_type;
typedef boost::interprocess::shared_ptr<map_type, void_allocator_type, map_deleter_type> map_pointer_type;

segment_manager_type* segment_manager = segment->get_segment_manager();
pair_allocator_type alloc_inst(segment_manager);

segment->construct<map_pointer_type>("elements ptr")(
            segment->construct<map_type>("elements")(std::less<IdClass>(), alloc_inst),      //object to own
            void_allocator_type(segment_manager),  //allocator
            map_deleter_type(segment_manager)         //deleter
            );

然后在Child进程中只得到这个指针的一个副本

map_pointer_type elements = *segment->find<map_pointer_type>("elements ptr").first;

【讨论】:

    【解决方案3】:

    我现在看到,如果我在子进程死后尝试在共享内存中的父进程中创建任何对象,我会收到一个断言错误:

    assertion "hdr->m_value_alignment == algn" failed: 
    file "/usr/include/boost/interprocess/detail/segment_manager_helper.hpp", line 181, 
    function: static boost::interprocess::detail::block_header* boost::interprocess::detail::block_header::block_header_from_value(const void*, size_t, size_t)
    

    所以现在我怀疑这是我从父进程生成子进程的方式。

      //Launch child process
      std::string s(argv[0]); s += " child";
      std::cout << "Child about to be born" <<  std::endl;
    
      if(0 != std::system(s.c_str())){
         std::cout << "(Parent): Child failed!" << std::endl;
         return 1;
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多