在回答有关将非连续文件块映射到连续内存here的问题时,一位受访者建议我应该使用VirtualAllocEx()和MEM_RESERVE来建立一个MapViewOfFileEx()的最终(lpBaseAddress)参数的'safe'值。
进一步调查显示,此方法导致MapViewofFileEx()失败,错误487:“尝试访问无效地址”。 The MSDN page says:
“在用于映射的区域中不能进行其他内存分配,包括使用VirtualAlloc或VirtualAllocEx函数来保留内存。”
尽管对于有效的调用序列,文档可能被认为是模糊的,但实验表明使用VirtualAllocEx()为MapViewOfFileEx()保留内存是无效的。
在网络上,我找到了带有硬编码值的示例 - example:
#define BASE_MEM (VOID*)0x01000000
...
hMap = MapViewOfFileEx( hFile, FILE_MAP_WRITE, 0, 0, 0, BASE_MEM );
对我而言,这似乎是不充分和不可靠的......我很清楚为什么这个地址是安全的,或者可以安全地在那里映射多少块。考虑到我需要我的解决方案在其他分配的上下文中工作,这似乎更加不稳定......而且我需要我的源代码来编译并在32位和64位上下文中工作。
我想知道的是,是否有任何方式可靠地保留地址空间池,以便 - 随后 - MapViewOfFileEx可以可靠地将其映射到显式内存地址。
答案 0 :(得分:2)
你几乎已经自己解决了这个问题,但没有达到最后一小步。
如您所知,使用VirtualAlloc
(使用MEM_RESERVE
)在您的地址空间中查找空间,但之后(在MapViewOfFileEx
之前)使用VirtualFree
(MEM_RELEASE
})。现在地址范围将再次免费。然后使用相同的内存地址(由VirtualAlloc
返回)和MapViewOfFileEx
。
答案 1 :(得分:0)
如果您提供基地址,该函数将尝试在该地址映射您的文件。如果它不能使用该基地址(因为某些东西已经在使用全部或部分请求的内存区域),那么调用将失败。
对于大多数应用程序来说,尝试自己修复地址并没有真正的意义。如果您是一个复杂的数据库进程,并且为了提高效率,您正试图在具有已知配置的计算机上仔细管理您自己的内存布局,那么这可能是合理的。但是你必须为失败做好准备。
在64位进程中,虚拟地址空间非常开放,因此可以确定地选择基址,但我认为我不会打扰。
来自MSDN:
虽然可以指定现在安全的地址(操作系统不使用),但无法保证地址会随着时间的推移而保持安全。因此,最好让操作系统选择地址。
我认为“随着时间的推移”是指操作系统的未来版本以及您正在使用的任何运行时库(例如,用于内存分配),这可能采用不同的内存布局方法。
此外:
如果lpBaseAddress参数指定了基本偏移量,则如果调用进程尚未使用指定的内存区域,则该函数会成功。系统无法确保在其他32位进程中可用于内存映射文件的相同内存区域。
基本上,你的直觉是正确的:指定基地址是不可靠的。你可以尝试,但你必须为失败做好准备。
所以直接回答你的问题:
我想知道的是,是否有任何方法可靠地保留地址空间池,以便 - 随后 - MapViewOfFileEx可以可靠地将其映射到显式内存地址。
不,没有。并非没有对运行时环境应用许多约束(例如,限制到特定版本的OS,设置所有DLL的基地址,禁止DLL注入等)。
答案 2 :(得分:0)
你要做的事情是不可能的。
从MapViewOfFileEx docs开始,您提供的指针是“指向映射开始的调用进程地址空间中的内存地址的指针。这必须是系统内存分配粒度的倍数,否则函数将失败。 “
memory allocation granularity is 64K,因此您无法将不同的4K页面从文件映射到虚拟内存中的相邻4K页面。