调试模式下内存映射向量的读取访问冲突
Posted
技术标签:
【中文标题】调试模式下内存映射向量的读取访问冲突【英文标题】:Read access violation for memory mapped vector in debug mode 【发布时间】:2018-07-14 02:39:43 【问题描述】:在尝试使用boost::interprocess
将std::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”例外吗?