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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 13:02:34