背景:在我的工作中(运行 Windows XP Embedded 的工业 CNC 机器),我们有一个进程通过使用 VirtualAlloc 和 VirtualLock 系列函数将其所有内存锁定到物理 RAM 中。
我知道在这一点上,将内存锁定到物理 RAM 可能不会带来它过去的好处(在以前版本的 Windows 中,如 NT),但我们快到最后期限了,所以虽然我们可以调查将来删除它,目前这是一个硬性要求。
因此,最近我对该进程使用的 dll 进行了更改,这反过来又引入了更多的 dll,其中一些创建了自己的堆(除了已经在进程加载的其他库中创建的堆)。
代码(伪代码)的基本结构是:
foreach( heap in GetProcessHeaps() )
{
HeapLock(heap)
if(!HeapValidate(heap)) { error/assert }
while(HeapWalk(heap, &heap_entry))
{
size = GetProcessWorkingSetSize()
SetProcessWorkingSetSize(size,size)
VirtualQuery(heap_entry, page_info)
if( page_info.State != MEM_RESERVE || page_info.State != MEM_COMMIT )
{
VirtualAlloc(heap_entry.ptr, heap_entry.data_size, MEM_RESERVE)
}
VirtualAlloc(heap_entry.ptr, heap_entry.data_size, MEM_COMMIT)
if( !HeapValidate(heap, heap_entry.ptr) ) { error/assert }
VirtualLock(heap_entry.ptr, heap_entry.data_size)
}
HeapUnlock(heap)
}
所以,这是伪代码,实现是用 C 语言编写的(我在每次调用 Windows 函数时都使用 GetLastError 和 FormatMessage 进行了适当的错误检查)。
好的,所以问题是以前,被锁定的大小是数据块大小和块开销的总和,但是正在使用的 ptr 是块中数据的 ptr,所以有些情况下 VirtualAlloc 和VirtualLock 会通过堆重叠到下一个堆中,并因无效的内存访问而失败。
修复该问题后,现在我有一个块在第二个 VirtualAlloc(使用 MEM_COMMIT)期间失败,并出现相同的“无效内存访问”错误。
它在“heap_entry”结构中给出的地址是 0x00b00020。奇怪的是,根据 SysInternals 工具 VMMap(到目前为止,它在帮助我诊断上一个问题时一直精确到字节)报告说堆本身从 0xA40000 变为 0xA50000,但是从 HeapWalk 返回的地址函数是0xb00020,这是完全错误的。
事实上,最后几个块超出了 0xA50000 范围:
ppi1: LockInMemory: VirtualAlloc: Successfully Commit memory (00a46f50->00a47163|560 bytes) of heap 5 (532 byte block, 28 bytes overhead)
ppi1: LockInMemory: VirtualLock: Successfully Locked memory (00a46f50->00a47163|560 bytes) of heap 5
Heap 5 handle 10747904 start address 00a47188 size 3704 overhead 16
ppi1: LockInMemory: VirtualAlloc: Successfully Commit memory (00a47188->00a47fff|3720 bytes) of heap 5 (3704 byte block, 16 bytes overhead)
ppi1: LockInMemory: VirtualLock: Successfully Locked memory (00a47188->00a47fff|3720 bytes) of heap 5
Heap 5 handle 10747904 start address 00a48000 size 32768 overhead 0
ppi1: LockInMemory: VirtualAlloc: Successfully Commit memory (00a48000->00a4ffff|32768 bytes) of heap 5 (32768 byte block, 0 bytes overhead)
ppi1: LockInMemory: VirtualLock: Successfully Locked memory (00a48000->00a4ffff|32768 bytes) of heap 5
Heap 5 handle 10747904 start address 00a60020 size 589856 overhead 0
ppi1: LockInMemory: VirtualAlloc: Successfully Commit memory (00a60020->00af003f|589856 bytes) of heap 5 (589856 byte block, 0 bytes overhead)
ppi1: LockInMemory: VirtualLock: Successfully Locked memory (00a60020->00af003f|589856 bytes) of heap 5
Heap 5 handle 10747904 start address 00b00020 size 589862 overhead 250
ppi1: LockInMemory: VirtualAlloc: Failed to Commit memory (00b00020->00b90045|590112 bytes) of heap 5: (Windows Error 487) Attempt to access invalid address. (589862 byte block, 250 bytes overhead, flags: 0x00000004)
如您所见,它停留在 0xA40000 范围内,直到由于某种原因到达 0xA60000->0xAF0000 范围,然后转到 0xB00020 地址。
我很困惑。无论我是否在块枚举期间锁定堆,这个问题都存在。并且该标志已设置意味着该块已分配。
好吧,我单独锁定每个。也许我应该一次锁定它们?这种物理内存锁定仅在应用程序初始化期间发生。
有谁知道这里会发生什么?我很困惑,我想不出另一种方法来获取更多信息。