进程的折叠 VMMap 视图显示所有 VAD 条目。可以使用kd
/ windbg
/查看 VAD livekd
。
让我们来看看calc.exe
:
lkd> !process 0n12876
PROCESS fffffa802e058600
SessionId: 1 Cid: 324c Peb: 7fffffdf000 ParentCid: 0bec
DirBase: 22211000 ObjectTable: fffff8a00e9b1310 HandleCount: 85.
Image: calc.exe
VadRoot fffffa8039b76500 Vads 176 Clone 0 Private 1852. Modified 1. Locked 0.
.
.
lkd> !vad fffffa8039b76500
VAD level start end commit
.
.
fffffa803c9da680 ( 6) ff7b0 ff892 6 Mapped Exe EXECUTE_WRITECOPY \Windows\System32\calc.exe
.
.
VAD 只有一个范围,并显示出虚假保护,EXECUTE_WRITECOPY
尽管我认为这确实一般性地描述了所有部分,因此允许其中的部分是 CoW 或只读和可执行的。VMMap 试图提供更多信息,不仅显示图像子部分对象,还显示这些子部分内的不同保护范围。例如,它显示了 1.data
个小节内的所有保护范围。它最初是 5 份在写入页面上的副本,现在 3 份已被读/写 PTE 替换,2 份仍然保持原样。
中有一个私有页面.rdata
,并且有读/写和 CoW.data
页面private
。的私有部分将.rdata
是包含导入地址表(IAT)的页面,因为它是由加载器修改的,并且对于每个进程都是不同的。因此,这也是在写入页面上制作的副本,然后被写入,现在它已被加载器只读。它不需要是可执行的,因为它是使用相对内存间接跳转或调用来访问的。
所有其他部分都有文件支持并在进程之间共享。可共享工作集是可以被其他进程映射入的工作集,共享工作集是至少被其他进程实际映射入的工作集,是可共享WS的子集。
Private
意味着数据是由进程修改的,它只与该进程相关,与其他进程无关,因此不会写回共享映射文件以供其他进程查看。因此,它必须创建一个页面,该页面将存储在页面文件中,而不是在需要分页时写回文件。这方面的一个例子是 IAT,其中每个图像将具有不同的导入地址,具体取决于加载的模块在进程地址空间中的位置,这因进程而异,并且在另一个进程的上下文中没有任何意义。
另一个示例private
可能是在图像未在其首选基址加载并且代码中有绝对地址的情况下需要修复的代码部分的一部分。这些页面必须在写入时分配副本,然后执行/读取 - 在此示例中没有。
还需要注意的是,这size
意味着进程已保留的虚拟内存。例如HeapCreate
为堆保留一些内存,它将有一个 VAD 条目,并将 VAD 条目标记的保留的完整大小添加到size
(并且 VAD 中的块的大小通过其他方式计算,例如分析 PTE他们自己)。然后在您调用时提交此内存HeapAlloc
即实际分配了 PTE,这意味着 PDE 也被分配了一个物理页面,以便 PTE 可以实际更改。对于特定范围,PTE 的需求为零。这些现在是“承诺的”。当您实际写入地址时,PTE 将被分配一个归零的物理页面,并且 PTE 将成为指向该物理页面的有效硬件 PTE,现在该页面是进程工作集的一部分,直到它从工作集。
private
是私有的虚拟提交(在页面中,即 4KiB 粒度),private WS
然后显示由 PTE 表示的私有虚拟提交页面的数量,这些 PTE 实际分配了物理页面作为进程工作集的一部分。它是工作集中物理页面的逻辑分类。total WS
is ,即它是进程的private WS + shareable WS
工作集。
映射图像的大小总是与其提交的大小相同,但映射的文件和部分并不总是——它们可以像堆一样保留块。在映射文件的上下文中保留意味着该区域还没有 PTE。您通常映射整个图像部分的视图,但是对于数据文件,我看到了映射完整文件的情况,但 VAD 条目的保留空间比文件大得多——我不确定如何去做这个。映射的大小在 VAD 中保留,然后分配给它们包含 PTE 的物理页面,以便可以同时填充它们的 PTE 以指向正确的原型 PTE (PPTE),这些原型 PTE (PPTE) 已创建(但未分配物理页面)在图像文件的情况下创建该部分时,. 当文件实际被访问时,给PTE一个物理页来指向,它成为一个有效的硬件PTE。这将只是将指针复制到 PPTE 从 PPTE 指向的物理页面(如果 PPTE 不指向一个,即它是 a MMPTE_SUBSECTION
,那么它会被分配并填充 IO 读取到文件,或者它是如果原型 PTE 是页面文件 PTE,则分页)。
保留只是 VAD 中的保留,但提交意味着现在有软件形式的 PTE(即它们是无效的),这意味着有一些额外的提交费用,因为需要在 PDE 中分配物理页面 / PDPT / PML4 即 PTE 页面,这样 PTE 可以实际写入。这个特定的提交费用不会出现在进程的工作集中(工作集提交费用),分页/非分页池提交费用或修改/备用列表提交费用或页面文件提交费用也不显示。
默认情况下,只读部分是共享部分(除了需要由链接器修改的页面,因为它们暂时变得可写),但图像中启用写入的部分默认情况下不共享,因此它是写入时复制,并且每个进程都有不同的数据段副本,在发生缺页时替换它(发生缺页是因为CoW页面无效),并且在需要分页时将其分页到页面文件中。如果您在图像的节标题特性中将其指定为共享节,则它不会在写入页面上分配为副本,只是读/写,并且所有对它的写入都将直接写入映射的图像,并且会被所有已映射它的进程。我相信这最终会写回图像,即它是文件支持的。
CoW 页面虚拟提交被列为私有的事实很有趣。我认为这个页面本身是共享的和文件支持的,只有替换页面是私有的。在这种情况下,CoW 页面也不在工作集上,但在我的情况下,chrome.exe
它只是在可共享的工作集上,如您所料,尽管虚拟提交仍被列为私有,但至少它是在可共享工作集上而不是在私有工作集上,这将是优化失败,因为它应该并且能够被共享。这是另一个例子:
这就引出了如何确定虚拟提交的隐私性的问题。在这种情况下,它似乎将指向 CoW PPTE 的提交 PTE 分类为私有,因为当页面被写入并交换为替代页面副本时,它最终将是私有的。这是误导性的,尽管不是一个有形的问题(表明它是私有工作集的一部分将是一个有形的问题)。至于.rdata
,它知道第一页在原始提交中不是私有时是私有的(原始提交从提交 PTE 指向的 PPTE 的保护中确定私有性,这些 PTE 是使用图像部分填写的),但奇怪的是是它不包括图像的私有页面中的 4K 页面(它显示 20K 而不是 24K),但在总数中包含 CoW 私有页面。你会认为它会读取 PPTE/PTE 以确定什么是私有提交,什么不是——当前提交与原始提交不同,因为加载程序已将只读(共享)PTE 更改为 CoW PTE和PPTE,然后它写入它并使 PTE 只读(但 PPTE 仍然是 CoW 并保留保护,即使它指向的物理页面因文件备份和只读而被丢弃(现在可能随着引用计数减少),即它不会从图像文件中重新获得保护)。当页面被写入时,新页面将不再涉及或再次指向PPTE,它现在是pagefile backed,物理页面分配的PFN条目不指向 PPTE。出于这个原因,共享部分是文件支持的,因为它与 PPTE 相关联,就像 CoW 页面一样,图像中的只读页面也是如此,但分配的页面与 PPTE 无关(页面 PFN不指向 PPTE)是页面文件支持的,无论它是设置为读/写还是读(在 IAT 的情况下为读)。
在任务管理器中,缓存 = 待机 + 修改,可用 = 空闲 + 待机。提交费用是使用的物理内存量(RAM + 页面文件)(用于提交本身(在工作集、页面文件、已修改、备用),或用于支持实际提交但不属于提交范围的结构本身,或分页/非分页池等)。