调试模式下内存映射向量的读取访问冲突

Posted

技术标签:

【中文标题】调试模式下内存映射向量的读取访问冲突【英文标题】:Read access violation for memory mapped vector in debug mode 【发布时间】:2018-07-14 02:39:43 【问题描述】:

在尝试使用boost::interprocessstd::vector 存储在内存映射文件中时,当我尝试推回加载的向量时遇到异常Exception thrown: read access violation.,但仅在调试模式下。

这个最小的示例代码(由@sehe 编写)是从https://***.com/a/29602884/2741329 检索到的,它在调试模式下在MSVC14 上崩溃并且执行了多次:

#include <boost/interprocess/managed_mapped_file.hpp>

namespace bi = boost::interprocess;

int main() 
    std::string vecFile = "vector.dat";
    bi::managed_mapped_file file_vec(bi::open_or_create,vecFile.c_str(), 1000);

    typedef bi::allocator<int, bi::managed_mapped_file::segment_manager> int_alloc;
    typedef std::vector<int, int_alloc>  MyVec;

    MyVec * vecptr = file_vec.find_or_construct<MyVec>("myvector")(file_vec.get_segment_manager());

    vecptr->push_back(rand());

编辑:

这是 Visual Studio 错误消息:

这里是异常发生的地方:

这是调用堆栈(点击图片放大):

【问题讨论】:

在崩溃时显示堆栈跟踪会很有帮助。 @john-zwinck 添加了有关崩溃的更多详细信息。 将 C++ 标准模板类型放入共享内存是一项非常冒险的尝试。您对编译器/库版本和构建设置有强烈的依赖性​​。触发此崩溃的一种简单方法是在一侧使用调试设置构建代码,在另一侧使用发布设置构建代码。它轰炸了实现迭代器调试功能的库代码,它改变了对象布局。 Sayonara 当数据是由关闭迭代器调试的代码生成时。保持简单。 bi::allocator不管理mmf上的元素数据分配吗? @HansPassant 恕我直言,这是在传播 FUD。我知道您根本不知道 Boost Interprocess 库,但是这里的用法非常好(您对围绕open_or_create 进行比赛有一个有效的直觉,但这显然不是这里的问题,与问题无关)。对于 OP:当我在电脑旁时,我会查看这一层。 (您是否有机会同时运行多个副本?) 【参考方案1】:

作为一个脑电波,disable MSVC debug iterators。

我不确定如何(因为迭代器没有持久化?)但不知何故迭代器调试可能会在 std::vector 的内存布局中添加原始指针 - 违反了关于分配器使用的标准库假设。

测试结果

仅出于此目的在 azure 上创建 VM,使用以下稍作修改的代码进行测试以更好地了解崩溃原因:

#include <boost/interprocess/managed_mapped_file.hpp>
#include <iostream>

namespace bi = boost::interprocess;

int main() 
    std::string vecFile = "vector.dat";
    //std::remove(vecFile.c_str());
    std::cout << __LINE__ << "\n";
    
        bi::managed_mapped_file file_vec(bi::open_or_create, vecFile.c_str(), 100000);

        typedef bi::allocator<int, bi::managed_mapped_file::segment_manager> int_alloc;
        typedef std::vector<int, int_alloc>  MyVec;

        MyVec * vecptr = file_vec.find_or_construct<MyVec>("myvector")(file_vec.get_segment_manager());

        vecptr->push_back(rand());
        std::cout << "size: " << vecptr->size() << "\n";
    
    std::cout << __LINE__ << "\n";
    
        bi::managed_mapped_file file_vec(bi::open_or_create, vecFile.c_str(), 100000);

        typedef bi::allocator<int, bi::managed_mapped_file::segment_manager> int_alloc;
        typedef std::vector<int, int_alloc>  MyVec;

        MyVec * vecptr = file_vec.find_or_construct<MyVec>("myvector")(file_vec.get_segment_manager());

        vecptr->push_back(rand());
        std::cout << "size: " << vecptr->size() << "\n";
    
    std::cout << __LINE__ << "\n";

重现问题。第一次运行:

后续运行(std::remove 行注释如图所示):

解决方法演示

放置后

#define _ITERATOR_DEBUG_LEVEL 0

在最顶部并删除 vector.dat 文件,因为更改会改变二进制布局

注意:在您的实际项目中,您可能需要将 #define 放在多个翻译单元中(尤其要考虑 stdafx.cpp)。将它包含在项目属性表中可能会更好,因此它适用于所有(未来的)翻译单元!

【讨论】:

我添加了#define _ITERATOR_DEBUG_LEVEL 0,但我仍然得到相同的异常和调用堆栈。 我完全忘记了 Windows 上的一切有多烦人...i.imgur.com/HEF95FD.png 这需要很多时间 我自己检查过。确实是迭代器调试,并且确实将定义(在所有适当的地方!)消除了问题。更改 buld 标志/配置时,不要忘记在两次运行之间实际删除 vector.dat 文件! 用实际证据修改了答案,并提供了各种提示,说明可能出现的问题以及为什么需要删除vector.dat。对于不赞成投票的人,如果您重新考虑,将不胜感激。

以上是关于调试模式下内存映射向量的读取访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

在发布模式下崩溃但在调试模式下不崩溃的 ActiveX 控件

为什么调试器会抛出“读取访问冲突。这是nullptr”例外吗?

WinCE compact 7 VS2008 调试模式启动应用错误

是否可以在调试模式下运行 ms 访问

处于释放模式时,C ++向量未初始化为空

如何在生产 Vue.js 中禁用源映射或调试模式 - Webpack