使用Boost managed_shared_memory时读取访问违规问题求助
Boost共享内存unordered_set字符串访问越界问题
我使用Boost的managed_shared_memory实现跨进程共享unordered_set,程序A负责创建共享内存并插入数据,程序B负责读取共享内存中的内容。但程序B执行std::cout<<ele.ern_name << std::endl;时触发访问违例异常:
Exception thrown at 0x75181267 (ucrtbase.dll) in tryINsertSPACE.exe: 0xC0000005: Access violation reading location 0x00BFFEB8.
测试发现,当程序A中a.ern_name设为短字符串(如"HANGZHOULMMRW25")时程序B能正常运行,换成更长的字符串(如"HANGZHOULMMRW250")就会触发异常,问题与字符串长度相关,和是否包含空格无关。
程序A(创建共享内存并插入数据)
#include "pch.h" #include <iostream> #include <boost/interprocess/managed_shared_memory.hpp> #include <boost/unordered_set.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include<string> using namespace boost::interprocess; const char enrtNdbShareSet[] = "enrtTableSet"; struct EnrtRecord { std::string ern_id = ""; std::string ern_frequency = ""; std::string ern_name = ""; bool operator==(const EnrtRecord& other) const { return ern_id == other.ern_id && ern_frequency == other.ern_frequency; } bool IsKeyNotEmpty() const { return ern_id != "" && ern_frequency != ""; } }; // hash func struct EnrtRecordHash { size_t operator()(const EnrtRecord& s) const { size_t h1 = boost::hash<std::string>()(s.ern_id); size_t h2 = boost::hash<std::string>()(s.ern_frequency); return h1 ^ (h2 << 1); } }; using EnrtShmemAllocator = boost::interprocess::allocator<EnrtRecord, boost::interprocess::managed_shared_memory::segment_manager>; using EnrtUnorderedSet = boost::unordered_set<EnrtRecord, EnrtRecordHash, std::equal_to<EnrtRecord>, EnrtShmemAllocator>; inline std::ostream& operator<<(std::ostream& os, const EnrtRecord& p) { os << "enrt_ern_id: " << p.ern_id << ", ern_name: " << p.ern_frequency; return os; // 返回ostream对象以支持链式操作 } int main() { 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); auto mSet = segment.construct<EnrtUnorderedSet>("XX")(3, EnrtRecordHash(), std::equal_to<EnrtRecord>(), segment.get_allocator<EnrtUnorderedSet>()); EnrtRecord a; a.ern_name = "HANGZHOULMM RW25"; a.ern_id = "AA"; a.ern_frequency = "444"; mSet->insert(a); while (true) {} }
程序B(读取共享内存)
#include "pch.h" #include <iostream> #include <boost/interprocess/managed_shared_memory.hpp> #include <boost/unordered_set.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include<string> using namespace boost::interprocess; const char enrtNdbShareSet[] = "enrtTableSet"; struct EnrtRecord { std::string ern_id = ""; std::string ern_frequency = ""; //std::string ern_mag_var = ""; std::string ern_name = ""; bool operator==(const EnrtRecord& other) const { return ern_id == other.ern_id && ern_frequency == other.ern_frequency; } bool IsKeyNotEmpty() const { return ern_id != "" && ern_frequency != ""; } }; // hash func struct EnrtRecordHash { size_t operator()(const EnrtRecord& s) const { size_t h1 = boost::hash<std::string>()(s.ern_id); size_t h2 = boost::hash<std::string>()(s.ern_frequency); return h1 ^ (h2 << 1); } }; using EnrtShmemAllocator = boost::interprocess::allocator<EnrtRecord, boost::interprocess::managed_shared_memory::segment_manager>; using EnrtUnorderedSet = boost::unordered_set<EnrtRecord, EnrtRecordHash, std::equal_to<EnrtRecord>, EnrtShmemAllocator>; inline std::ostream& operator<<(std::ostream& os, const EnrtRecord& p) { os << "enrt_ern_id: " << p.ern_id << ", ern_name: " << p.ern_frequency; return os; // 返回ostream对象以支持链式操作 } int main() { managed_shared_memory segment(open_read_only, "MySharedMemory"); EnrtUnorderedSet *myGlsset = segment.find<EnrtUnorderedSet>("XX").first; for (auto& ele : (*myGlsset)) { std::cout << ele << std::endl; std::cout<<ele.ern_name << std::endl; } }
问题原因
核心问题在于EnrtRecord中的std::string使用的是进程私有堆的默认分配器:
- 短字符串触发了SSO(小字符串优化),字符串数据直接存在
std::string对象内部,而对象本身位于共享内存中,所以程序B可以正常访问; - 长字符串会在程序A的私有堆上分配内存存储实际数据,程序B访问时,这个堆地址在B的进程空间中是无效的,因此触发访问违例。
解决方案
将EnrtRecord中的std::string替换为Boost Interprocess提供的basic_string,使用共享内存的分配器来管理字符串内存。
修改后的关键代码
首先定义共享内存版本的字符串类型:
// 定义共享内存分配器的字符串类型 using ShmemStringAllocator = boost::interprocess::allocator<char, boost::interprocess::managed_shared_memory::segment_manager>; using ShmemString = boost::interprocess::basic_string<char, std::char_traits<char>, ShmemStringAllocator>;
然后修改EnrtRecord结构:
struct EnrtRecord { ShmemString ern_id; ShmemString ern_frequency; ShmemString ern_name; // 构造函数需要接收共享内存分配器 EnrtRecord(const ShmemStringAllocator& alloc) : ern_id(alloc), ern_frequency(alloc), ern_name(alloc) {} // 默认构造函数(用于unordered_set的默认构造) EnrtRecord() = default; bool operator==(const EnrtRecord& other) const { return ern_id == other.ern_id && ern_frequency == other.ern_frequency; } bool IsKeyNotEmpty() const { return !ern_id.empty() && !ern_frequency.empty(); } };
同时需要修改哈希函数,适配ShmemString:
struct EnrtRecordHash { size_t operator()(const EnrtRecord& s) const { size_t h1 = boost::hash<ShmemString>()(s.ern_id); size_t h2 = boost::hash<ShmemString>()(s.ern_frequency); return h1 ^ (h2 << 1); } };
程序A中插入数据时,需要传递共享内存分配器给EnrtRecord:
EnrtRecord a(segment.get_allocator<ShmemStringAllocator>()); a.ern_name = "HANGZHOULMM RW25"; a.ern_id = "AA"; a.ern_frequency = "444"; mSet->insert(a);
这样所有字符串数据都会分配在共享内存段中,跨进程访问时就不会出现地址无效的问题。
内容的提问来源于stack exchange,提问作者user25203529
相关产品推荐
相关产品推荐

