意外的页面处理(同样,VirtualLock =没有操作?)

Dam*_*mon 5 windows virtual-memory

今天早上我偶然发现了一些令人惊讶的页面错误,我没想到它们.是的,我可能不应该担心,但它仍然让我感到奇怪,因为在我的理解中它们不应该发生.并且,如果他们不这样做,我会更好.

该应用程序(在WinXP Pro 32位下)保留较大的区域(1GB)的地址空间,VirtualAlloc(MEM_RESERVE)然后分配中等大块(20-50MB)的内存VirtualAlloc(MEM_COMMIT).这是在工人提前完成的,目的是尽可能少地停止主线程.显然,除非内存区域当前被锁定,否则您无法确保不会发生页面错误,但其中一些肯定是可以容忍的(并且是不可避免的).令人惊讶的是每一页都有错误.总是.

因此,假设系统在分配页面之后只是懒惰地创建页面,这在某种程度上也是有意义的(尽管文档提出了不同的东西).很公平,我的坏.
因此,显而易见的解决方法是VirtualLock/ VirtualUnlock,它强制系统创建这些页面,因为它们必须VirtualLock返回后存在.令人惊讶的是,仍然每一页都有故障.

所以我写了一个小测试程序,按顺序执行上述所有步骤,在每个步骤之间休息5秒,以排除其他代码中的错误.结果是:

  • MEM_RESERVE 1GB --->成功,零CPU,零时间,没有任何反应
  • MEM_COMMIT 1 GB --->成功,零CPU,零时间,工作集增加2MB,512页错误(每页用户空间分配8个字节的元数据)
  • for(... += 128kB) { VirtualLock(128kB); VirtualUnlock(128kB); } --->成功,零CPU,零时间,没有任何反应
  • for(... += 4096) *addr = 0;---> 262144页面错误,大约0.25秒(内核时间约为95%).Process Explorer内的"工作集"和"物理"增加1GB
  • VirtualFree --->零CPU,零时间,"工作集"和"物理"瞬间变为*poof*.

我的期望是,由于每个页面都被锁定一次,因此至少在此之后它必须存在.当超出配额时,它当然可以移入和移出WS(只要有足够的RAM可用,只需更改一个引用).然而,执行时间,工作集和物理内存指标似乎都不支持这一点.相反,正如它所看到的那样,每个单个访问页面都是在故障时创建的,即使它之前已被锁定.当然我可以在工作线程中手动触摸每一页,但是必须有更清洁的方法吗?

我是否VirtualLock 应该做一个错误的假设,或者我不了解虚拟内存的某些内容?任何关于如何以"干净,合法,有效"的方式告诉操作系统我想要记忆的想法,我会想要它真实吗?

更新:
为了回应Harry Johnston的建议,我尝试了实际调用VirtualLock一块GB内存的有点问题的方法.对于这个成功,你必须首先设置进程的相应工作集大小,因为默认配额是200K/1M,这意味着VirtualLock不可能锁定大于200K(或者更确切地说,它不能超过200K锁定一个区域产品总数,以及减去I/O已锁定的内容或其他原因).

设置最小工作集大小为1GB且最大为2GB后,所有页面错误都会在VirtualAlloc(MEM_COMMIT)调用时发生.Process Explorer中的"虚拟大小"立即跳起1GB.到目前为止,它看起来非常非常好.
然而,仔细观察,"物理"仍然保持原样,实际记忆实际上只在您触摸它的那一刻使用.

VirtualLock仍然是一个无操作(故障明智),但提高最低工作集大小了更接近球门.

但是,篡改WS大小有两个问题.首先,你通常不打算在一个进程中拥有一个千兆字节的最小工作集,因为操作系统会努力保持锁定的内存量.这在我的情况下是可以接受的(它实际上或多或少只是我要求的).
更大的问题是SetProcessWorkingSetSize需要PROCESS_SET_QUOTA访问权限,作为"管理员"没有问题,但是当您以受限制的用户身份运行程序时(由于一个充分的理由)它会失败,并且它会触发"允许可能有害的程序?" 一些着名的俄罗斯反病毒软件的警报(没有充分的理由,但唉,你不能把它关掉).

Sec*_*att 4

从技术上讲,VirtualLock 是一个提示,因此操作系统可以忽略它。它由 NtLockVirtualMemory 系统调用支持,该系统调用在 Reactos/Wine 上实现为无操作,但 Windows 确实通过实际工作 (MiLockVadRange) 支持系统调用。

VirtualLock 不保证成功。调用此函数需要 SE_LOCK_MEMORY_PRIVILEGE 才能工作,并且地址必须满足安全和配额限制。此外,在 VirtualUnlock 之后,内核不再有义务将页面保留在内存中,因此此后的页面错误是有效的操作。

正如 Raymond Chen 指出的那样,当您解锁内存时,它可以正式释放页面。这意味着下一页上的下一个 VirtualLock 可能会再次获取同一页面,因此当您触摸原始页面时,您仍然会遇到页面错误。