6

在回答有关将不连续的文件块映射到连续内存的问题时,一位受访者建议我应该使用VirtualAllocEx () 和 MEM_RESERVE,以便为最终 (lpBaseAddress) 参数建立一个“安全”值对于MapViewOfFileEx ()。

进一步调查显示,这种方法会导致 MapViewofFileEx() 失败并出现错误 487:“尝试访问无效地址”。 MSDN 页面说

“在用于映射的区域中不能进行其他内存分配,包括使用 VirtualAlloc 或 VirtualAllocEx 函数来保留内存。”

虽然关于有效调用序列的文档可能被认为是模棱两可的,但实验表明使用 VirtualAllocEx() 为 MapViewOfFileEx() 保留内存是无效的。

在网上,我找到了带有硬编码值的示例 -示例

#define BASE_MEM     (VOID*)0x01000000

...

hMap = MapViewOfFileEx( hFile, FILE_MAP_WRITE, 0, 0, 0, BASE_MEM );

对我来说,这似乎是不够的和不可靠的......我还不清楚为什么这个地址是安全的,或者有多少块可以安全地映射到那里。考虑到我需要我的解决方案在其他分配的上下文中工作……而且我需要我的源代码在 32 位和 64 位上下文中编译和工作,这似乎更加不稳定。

我想知道的是,是否有任何方法可以可靠地保留地址空间池,以便 - 随后 - MapViewOfFileEx 可以可靠地使用它来将块映射到显式内存地址。

4

3 回答 3

2

您几乎自己解决了问题,但没有完成最后一小步。如您所想,使用VirtualAlloc(with MEM_RESERVE) 在您的地址空间中查找空间,但之后(和之前MapViewOfFileEx)使用VirtualFree(with MEM_RELEASE)。现在地址范围将再次空闲。然后使用与 相同的内存地址(由 返回VirtualAllocMapViewOfFileEx

于 2014-11-26T11:38:44.547 回答
0

如果您提供基地址,该函数将尝试将您的文件映射到该地址。如果它不能使用该基地址(因为某些东西已经在使用全部或部分请求的内存区域),那么调用将失败。

对于大多数应用程序,尝试自己修复地址没有任何意义。如果您是一个复杂的数据库进程,并且出于效率原因,您正在尝试在具有已知配置的机器上仔细管理自己的内存布局,那么这可能是合理的。但你必须为失败做好准备。

在 64 位进程中,虚拟地址空间非常开放,因此可以确定地选择基地址,但我认为我不会打扰。

来自MSDN

虽然可以指定一个现在安全的地址(操作系统不使用),但不能保证该地址会随着时间的推移保持安全。因此,最好让操作系统来选择地址。

我相信“随着时间的推移”是指操作系统的未来版本以及您正在使用的任何运行时库(例如,用于内存分配),这可能会采用不同的内存布局方法。

还:

如果 lpBaseAddress 参数指定基本偏移量,则如果指定的内存区域尚未被调用进程使用,则函数成功。系统不确保相同的内存区域可用于其他 32 位进程中的内存映射文件。

所以基本上,你的直觉是对的:指定基地址是不可靠的。你可以尝试,但你必须为失败做好准备。

所以直接回答你的问题:

我想知道的是,是否有任何方法可以可靠地保留地址空间池,以便 - 随后 - MapViewOfFileEx 可以可靠地使用它来将块映射到显式内存地址。

不,没有。并非没有对运行时环境施加许多约束(例如,限制为特定版本的操作系统、为所有 DLL 设置基地址、禁止 DLL 注入等)。

于 2012-08-25T15:59:11.590 回答
0

你试图做的事情是不可能的。

MapViewOfFileEx 文档中,您提供的指针是“指向映射开始的调用进程地址空间中的内存地址的指针。这必须是系统内存分配粒度的倍数,否则函数会失败。”

内存分配粒度为 64K,因此您无法将文件中的不同 4K 页映射到虚拟内存中相邻的 4K 页。

于 2012-08-25T18:38:43.730 回答