我需要确保一个内存缓冲区全部在物理内存中,并且在后续函数中访问它时不会发生页面错误,由于某些时序问题,该函数无法容忍页面错误。 缓冲区通常由许多保护页组成。
我想出了以下代码来解决这个问题:
#define PAGE_SIZE 4096
for (char* p = pbBuffer; (p < pbBuffer + cbBufferSize); *(volatile char*)p, p += PAGE_SIZE);
SubsequentFn(pBuffer, cbBufferSize); // Does not tolerate page faults.
这似乎有效,并且
volatile
关键字可以防止 C 编译器优化“几乎不执行任何操作”的 for 循环。 请参阅下面编译的机器代码:
000000013F3F1AA7 c7 c1 78 56 34 12 mov rcx, pbBuffer
000000013F3F1AAE 48 8D 91 00 00 10 00 lea rdx,[rcx+cbBufferSize]
000000013F3F1AB6 48 3B CA cmp rcx,rdx
000000013F3F1AB9 73 14 jae Main+4Fh (013F3F1ACFh)
000000013F3F1ABB 0F 1F 44 00 00 nop dword ptr [rax+rax]
000000013F3F1AC0 0F B6 01 movzx eax,byte ptr [rcx]
000000013F3F1AC3 48 81 C1 00 10 00 00 add rcx,1000h
000000013F3F1ACA 48 3B CA cmp rcx,rdx
000000013F3F1ACD 72 F1 jb Main+40h (013F3F1AC0h)
000000013F3F1ACF ...
如您所见,指令
movzx eax,byte ptr [rcx]
触及内存,但寄存器 eax
在循环内不用于任何其他用途 - 这是预期的效果。
问:这是在缓冲区中触发
Guard Pages
的最佳方法还是有更好的方法? 该代码在某些情况下会崩溃吗?
如果没有操作系统级别的支持,您无法保证缓冲区将保留在物理内存中 - 如果操作系统遇到内存压力,它完全可以决定将缓冲区调出页面。
相反,请考虑使用特定于操作系统的 API 将缓冲区锁定到物理内存中。在 Linux 上,使用
mlock
;在 Windows 上,使用 VirtualLock
。这些函数将锁定指定的内存区域,防止其被调出。