使用boost::interprocess共享内存时,将std::string转换为MyShmString
用boost::interprocess实现跨进程std::string共享的正确姿势
嘿,我看你在尝试用boost::interprocess来跨进程共享失败测试的名称,刚好对这块有不少实践经验,先给你指出当前代码里的一个明显问题:你每次调用registerFailedTests都执行shared_memory_object::remove,这会直接把之前存在共享内存里的数据全清掉,完全没法实现累加存储的需求。而且还有个核心点:标准库的std::string不能直接放在共享内存里,它的内部缓冲区是在进程私有堆上分配的,其他进程根本访问不到。
下面给你一套完整的可行方案:
1. 先定义共享内存专用的字符串类型
boost给我们提供了适配共享内存的容器和分配器,我们需要用这些来替代标准库的std::string:
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/interprocess/containers/string.hpp> #include <boost/interprocess/sync/named_mutex.hpp> #include <vector> namespace bip = boost::interprocess; // 基于共享内存分配器的字符串类型 using ShmemCharAllocator = bip::allocator<char, bip::managed_shared_memory::segment_manager>; using SharedString = bip::basic_string<char, std::char_traits<char>, ShmemCharAllocator>; // 基于共享内存分配器的字符串列表类型 using SharedStringVec = std::vector<SharedString, bip::allocator<SharedString, bip::managed_shared_memory::segment_manager>>;
2. 改造你的registerFailedTests函数
我们要避免每次调用都删除共享内存,而是先尝试打开已存在的共享段,不存在再创建;同时要加跨进程互斥锁保证线程安全:
void registerFailedTests(std::string failed_test_name) { bip::permissions perm; perm.set_unrestricted(); const char* shm_name = unit_tests_constants::shm_failed_tests_list.c_str(); const char* mutex_name = "FailedTestsMutex"; try { // 尝试打开已有的共享内存段 bip::managed_shared_memory segment(bip::open_only, shm_name); // 查找共享内存里的失败测试列表 auto [p_failed_tests, found] = segment.find<SharedStringVec>("FailedTestsList"); if (!found) { throw bip::interprocess_exception(bip::not_found_error); } // 加锁防止多进程同时修改 bip::named_mutex mutex(bip::open_only, mutex_name); bip::scoped_lock<bip::named_mutex> lock(mutex); // 用共享内存分配器构造字符串并添加到列表 p_failed_tests->emplace_back(failed_test_name.c_str(), segment.get_allocator<char>()); } catch (const bip::interprocess_exception& e) { // 共享内存不存在,创建新的(初始大小设为64KB足够存不少测试名) bip::managed_shared_memory segment(bip::create_only, shm_name, 65536, nullptr, perm); // 在共享内存里构造空的测试列表 segment.construct<SharedStringVec>("FailedTestsList")(segment.get_allocator<SharedString>()); // 创建跨进程互斥锁 bip::named_mutex(bip::create_only, mutex_name); // 再次获取列表并添加第一个测试名 auto p_failed_tests = segment.find<SharedStringVec>("FailedTestsList").first; p_failed_tests->emplace_back(failed_test_name.c_str(), segment.get_allocator<char>()); } }
3. 其他进程读取共享数据的示例
如果另一个进程要读取这些失败测试名,可以用下面的代码:
void printFailedTests() { const char* shm_name = unit_tests_constants::shm_failed_tests_list.c_str(); const char* mutex_name = "FailedTestsMutex"; try { bip::managed_shared_memory segment(bip::open_only, shm_name); auto [p_failed_tests, found] = segment.find<SharedStringVec>("FailedTestsList"); if (!found) { std::cout << "No failed tests recorded yet." << std::endl; return; } bip::named_mutex mutex(bip::open_only, mutex_name); bip::scoped_lock<bip::named_mutex> lock(mutex); std::cout << "Total failed tests: " << p_failed_tests->size() << std::endl; for (const auto& test_name : *p_failed_tests) { std::cout << "- " << test_name << std::endl; } } catch (const bip::interprocess_exception& e) { std::cerr << "Error accessing shared memory: " << e.what() << std::endl; } }
几个关键注意点要记牢
- 绝对不能用标准库容器直接存到共享内存:标准库容器的内存分配在进程私有堆,其他进程访问会直接崩溃或者读垃圾数据,必须用boost提供的适配共享内存的容器和分配器。
- 跨进程一定要加同步锁:多个进程同时读写共享内存肯定会出数据竞争,用
named_mutex是最简单可靠的方式。 - 共享内存大小要预估好:如果测试名很多,初始64KB不够的话,可以调大,或者改用
managed_mapped_file支持动态扩容。 - 清理资源要谨慎:不要随便在程序里调用
shared_memory_object::remove,最好在所有进程都用完之后再清理,比如写个专门的清理函数,或者程序正常退出时处理。
内容的提问来源于stack exchange,提问作者Tony D.
相关产品推荐
相关产品推荐

