You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MPI进程中销毁Boost.Interprocess共享内存对象时崩溃的问题

Boost共享内存对象销毁错误(MPI场景)

问题

父进程创建共享内存后,通过system启动mpiexec。MPI进程可正常打开共享内存,通过find<Type>("name")找到对象,但调用destroy<Type>(name)销毁对象时触发崩溃。无MPI的同类单进程/子进程场景运行正常。

相关代码

父进程创建共享内存

void Foo::DumpInitialData()
{
    using namespace boost::interprocess;
    shared_memory_object::remove("MySHM");
    typedef allocator<char, managed_shared_memory::segment_manager>
        ShmCharAllocator;
    typedef std::basic_string<char, std::char_traits<char>, ShmCharAllocator>
        ShmStr;

    string try_str;
    {
        ostringstream try_ss;
        Foo split(*this, ...);
        BinarySerialization ser(split);
        ser.Dump(try_ss);
        try_str = try_ss.str();
    }
    size_t try_size = sizeof(ShmStr) + 512
        + (try_str.capacity() + 1) * sizeof(char);

    managed_shared_memory segment(create_only, "MySHM", 
        try_size * (split_size + 1));
    ShmCharAllocator alloc_chars(segment.get_segment_manager());
    segment.construct<ShmStr>(
            ("Mpi" + to_string(0)).c_str()
    )(try_str, alloc_chars); 

    try {
        for (int i = 1; i < split_size; ++i) {
            stringstream ss;
            Foo split(*this, ...);
            BinarySerialization ser(split);
            ser.Dump(ss);

            segment.construct<ShmStr>(("Mpi" + to_string(i)).c_str())
                (ss.str(), alloc_chars);
        }
    } catch (interprocess_exception &e) {
        throw ...Exception(
            "No enough shared memeroy to passing split data."
        );
    }
}

MPI进程读取并销毁共享内存对象

void Foo::PumpMpiInitialData(int i)
{
    auto& world = mpi->world();
    vector<string> send(world.size());
    decltype(send)::value_type recv;
    if (i == 0) {
        using namespace boost::interprocess;
        typedef allocator<char, managed_shared_memory::segment_manager>
            ShmCharAllocator;
        typedef std::basic_string<
            char, std::char_traits<char>, ShmCharAllocator
        > ShmStr;
        {
            managed_shared_memory segment(
                open_only, "MySHM"
            );

            for_each(send | ba::indexed(), [&](const auto& s_it) {
                auto name = "Mpi" + to_string(s_it.index());
                const char* c_name = name.c_str();
                ShmStr* data = segment.find<ShmStr>(c_name).first;
                s_it.value() = string(*data);
                // 此处调用会触发错误
                bool succ = segment.destroy<ShmStr>(c_name);
            });
        }
        shared_memory_object::remove("MySHM");
    }
    bm::scatter(world, send, recv, 0);

    BinarySerialization ser(*this);
    istringstream ss(recv);
    ser.Pump(ss);
}

错误调用栈

>   ....exe!std::_Container_base12::_Orphan_all_unlocked_v3() Line 1243 C++
    ....exe!std::_Container_base12::_Orphan_all_locked_v3() Line 1098   C++
    ....exe!std::_Container_base12::_Orphan_all() Line 1260 C++
    ....exe!std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>::_Tidy_deallocate() Line 4618 C++
    ....exe!std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>::~basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>() Line 3005    C++
    ....exe!std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>::`scalar deleting destructor'(unsigned int)   C++
    ....exe!boost::interprocess::ipcdetail::placement_destroy<std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>>::destroy_n(void * mem, unsigned __int64 num, unsigned __int64 & destroyed) Line 61 C++
    ....exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>::priv_generic_named_destroy<char>(const char * name, boost::interprocess::iset_index<boost::interprocess::ipcdetail::index_config<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>>>> & index, boost::interprocess::ipcdetail::in_place_interface & table, boost::interprocess::ipcdetail::bool_<1> is_intrusive_index) Line 992    C++
    ....exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>::destroy<std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>>(boost::interprocess::ipcdetail::char_ptr_holder<char> name) Line 553  C++
    ....exe!boost::interprocess::ipcdetail::basic_managed_memory_impl<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index,8>::destroy<std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>>(const char * name) Line 568    C++
    ....exe!<lambda_da719d1c74e37fe397eecaa291110c18>::operator()<boost::range::index_value<std::string &,__int64>>(const boost::range::index_value<std::string &,__int64> & s_it) Line 222 C++

无MPI的正常运行示例

int main(int argc, char *argv[])
{
   using namespace boost::interprocess;
   typedef allocator<char, managed_shared_memory::segment_manager>
        ShmCharAllocator;
   typedef std::basic_string<char, std::char_traits<char>, ShmCharAllocator>
        ShmStr;

   if(argc == 1){  //父进程
      //构造和析构时移除共享内存
      struct shm_remove
      {
         shm_remove() { shared_memory_object::remove("MySharedMemory"); }
         ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
      } remover;

      {
      //创建共享内存段
      managed_shared_memory segment(create_only, "MySharedMemory", 65536);

      const ShmCharAllocator alloc_inst(segment.get_segment_manager());
      //初始化共享内存中的字符串对象
      segment.construct<ShmStr>("MyType0")(string("abc"), alloc_inst);
      }

      //启动子进程
      std::string s(argv[0]); s += " child ";
      if(0 != std::system(s.c_str()))
         return 1;
   } else {
       {
           using namespace std::chrono_literals;
           std::this_thread::sleep_for(10s);
       }
      //打开共享内存段
      managed_shared_memory segment(open_only, "MySharedMemory");

      const string str = "MyType0";
      auto* res = segment.find<ShmStr>(str.c_str()).first;
      string a = string(*res);

      segment.destroy<ShmStr>(str.c_str());
   }
   return 0;
}

原因分析与解决方案

原因

崩溃发生在VS STL的std::basic_string析构过程中,触发_Orphan_all操作——这是STL容器用于通知关联迭代器失效的逻辑。而使用Boost共享内存分配器的std::basic_string在MPI场景下,由于mpiexec启动的进程与父进程的内存上下文差异,或STL与Boost分配器的兼容性问题,导致析构时的迭代器通知逻辑出错。

解决方案

  1. 改用Boost原生共享内存字符串
    替换自定义的ShmStr为boost::interprocess::string,该类型专门为共享内存场景设计,避免STL实现的兼容性问题:

    // 替换原有的typedef
    typedef boost::interprocess::string ShmStr;
    // 创建对象时无需手动传入allocator,内部已关联共享内存段的分配器
    segment.construct<ShmStr>(("Mpi" + to_string(0)).c_str())(try_str.c_str());
    
  2. 销毁前解除本地对象引用
    在销毁共享内存对象前,确保本地指向该对象的指针已超出作用域,避免STL迭代器逻辑干扰:

    for_each(send | ba::indexed(), [&](const auto& s_it) {
        auto name = "Mpi" + to_string(s_it.index());
        const char* c_name = name.c_str();
        {
            // 局部作用域内持有指针,拷贝后自动销毁
            ShmStr* data = segment.find<ShmStr>(c_name).first;
            s_it.value() = string(*data);
        }
        bool succ = segment.destroy<ShmStr>(c_name);
    });
    
  3. 由父进程负责销毁共享内存
    避免在MPI进程中销毁共享内存对象,改为父进程等待mpiexec执行完毕后,再删除共享内存:

    // 父进程启动mpiexec后,等待其结束
    std::system("mpiexec ...");
    // 所有MPI进程完成数据读取后,父进程删除共享内存
    shared_memory_object::remove("MySHM");
    
  4. 检查共享内存大小是否足够
    确保创建共享内存时计算的try_size足够容纳所有对象及其内部数据,避免内存溢出导致的销毁异常。


内容的提问来源于stack exchange,提问作者Vonfry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 20:36:17