当程序访问一个在虚拟地址空间中映射但未加载到物理内存中的页面时,硬件引发软件陷阱,这就是页错误。(我强调)
好的,这很有道理。
但如果是这样的话,为什么每当Process Hacker中的进程信息刷新时,我都会看到大约15个页错误?
换句话说,为什么会有任何内存被分页出去呢?(我不知道是用户内存还是内核内存。)我没有页面文件,而且在干净重启后,RAM使用量约为4GB中的1.2GB。没有任何资源短缺,为什么会有东西被分页出去呢?
(我是Process Hacker的作者。)
首先:
页面错误是由硬件引发的陷阱,当程序访问映射在虚拟地址空间中但未加载到物理内存中的页面时。
那并不完全正确,正如同一篇文章中稍后解释的那样(次要页面故障)。存在软页面错误,此时内核所需做的仅是将页面添加到进程的工作集中。以下是Windows Internals书中的一张表格(我排除了导致访问冲突的部分):
Page Fault原因 | 结果 |
---|---|
访问未在内存中但在磁盘中的页面文件或映射文件上的页面 | 分配物理页面,并将所需页面从磁盘读入相关的工作集中 |
访问待机列表或修改列表上的页面 | 将页面转换至相关进程、会话或系统工作集 |
访问需求为零的页面 | 将一个填充为零的页面添加到相关的工作集 |
写入一个写时复制页面 | 生成进程专用(或会话专用)页面副本,并将原始页面替换为进程或系统的工作集中 |
如上所示,页面错误可能出现各种不同的原因。只有其中之一与从磁盘读取相关。如果尝试从堆中分配块并且堆管理器分配新页面,然后访问这些页面,则会发生需求为零的页面错误。如果尝试通过写入kernel32的页面来挂钩kernel32中的函数,则会发生写时复制错误,因为这些页面正在被默默地复制,以使更改不影响其他进程。
现在更具体地回答你的问题:当更新其服务信息时,Process Hacker似乎只会出现页面故障 - 也就是说,当它调用EnumServicesStatusEx时,它会远程过程调用到SCM(services.exe)。我猜测,在这个过程中,大量的内存被分配,导致需要零页故障(如果我没记错,服务信息需要几个页面来存储)。
假设您映射了一个日志文件并不断添加内容。每次超过已提交内存的末尾时,都会发生一个页面错误,操作系统将为您提供一个新的空页面并调整文件长度。
当内存在进程之间共享时,您会看到软页错误。基本上,如果您有一个在两个进程之间共享的内存映射文件,当第二个进程加载内存映射文件时,将生成软页错误 - 内存已经在物理RAM中,但操作系统需要修复内存管理器的表,以便您进程中的虚拟内存地址指向正确的物理页面。
特别是对于像Process Hacker这样的东西,它可能会向每个运行的进程注入代码(以收集信息),因此很可能会大量使用共享内存来进行IPC。
资源分配是在保持主存储器可用的同时尽可能减少需要使用辅助存储器之间的微妙平衡。如果一个进程试图分配内存并且无法分配,那通常是一个异常情况,有时甚至是致命的异常情况。
基本上,你不能将所有东西都保存在没有可用资源的RAM中,因为当程序启动或请求更多资源时,它会崩溃。