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分配器的兼容性问题,导致析构时的迭代器通知逻辑出错。
解决方案
改用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());销毁前解除本地对象引用
在销毁共享内存对象前,确保本地指向该对象的指针已超出作用域,避免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); });由父进程负责销毁共享内存
避免在MPI进程中销毁共享内存对象,改为父进程等待mpiexec执行完毕后,再删除共享内存:// 父进程启动mpiexec后,等待其结束 std::system("mpiexec ..."); // 所有MPI进程完成数据读取后,父进程删除共享内存 shared_memory_object::remove("MySHM");检查共享内存大小是否足够
确保创建共享内存时计算的try_size足够容纳所有对象及其内部数据,避免内存溢出导致的销毁异常。
内容的提问来源于stack exchange,提问作者Vonfry
相关产品推荐
相关产品推荐

