FSD首次读取/写入文件时调用CcInitializeCacheMap
,这将导致创建专用缓存映射,节对象,控制区域,段,子节(如果尚不存在) 。当缓存管理器创建一个节对象时,它将在NtCreateSection
的sectionattributes参数中指定它是一个数据节SEC_DATA,这意味着PPTE最初并未配置,并且该段中的基为空白。使用CcCopyRead
完成实际读取,该操作将首先分配VACB并映射文件视图,然后从VACB复制到缓冲区。每次分配VACB时,都会将265kb的视图映射到缓存管理器虚拟空间。当进行这种映射时,它需要初始化一个PPTE,但是当它初始化一个PPTE时,由于它们需要在设计上是连续的,因此最好对整个文件进行初始化。无论是否分配内存,该内存都将保留。
Windows内部人员声称,它会推迟为数据文件创建PPTE,直到映射第一个视图为止,但是对于图像文件,它会在创建section对象时创建它们。
对于支持页面文件的节,当首次创建节对象时,将创建原型PTE数组。对于映射文件,当映射每个视图时,将按需创建数组的一部分。
另一个消息来源指出:
在映射数据文件时,MiCreateDataFileMap的主要目的是设置子节对象。通常情况下,只创建一个小节,但在某些特殊条件下,会使用多个小节,例如如果文件很大。对于数据文件,小节字段SubsectionBase留为空白。这将推迟PPTE的创建,直到将该部分映射到内存中并最终首次访问为止。其背后的原因是避免在映射非常大的数据文件时浪费内存。相反,必须正确设置细分对象的SegmentPteTemplate字段,如有必要,可用于随后创建PPTE。
我只是不同意这一点,因为如果映射了一个4GB的数据文件,则只需要2MB的PPTE页面,这没有太多空间,因此我看不到这样做的好处。真正的空间节省来自VACB,以及整个文件不必驻留在物理内存中的事实。
为什么他们声称需要按需初始化PPTE的“部分”仍然没有意义,就好像在暗示它节省了空间,因为这样做并不意味着因为一旦映射了一个PPTE便保留了整个区域,并且{在该段中设置了{1}},因为整个文件的PPTE必须是连续的。
可以按需初始化“部分”,但是无论是否存在PPTE,都不会改变保留空间的事实。例如,当分配VACB时,它可以初始化覆盖256kb的所有PPTE。当发生页面错误时,PTE指向PPTE,并且PPTE将无效,因此它可以以256kb粒度执行256kb集群IO,这意味着任何其他页面错误都将是软页面错误。 (我相信,当首次分配PPTE时,在为VACB视图分配PTE时,使PTE指向PPTE。Windows内部对出现的错误虚拟地址使用了一些错误提示,这些错误的虚拟地址用于搜索VTE的VAD。 PPTE的开始和结束过程在第411页,但系统缓存是内核内存的一部分,VAD不会跟踪它,因此是错误的。此方法仅用于用户空间映射)。一个问题是页面可能会被修改,因此它不一定必须进行256kb集群优化(1个硬页面错误,63个软页面错误)并将页面锁定到内存中以执行IO。它必须知道所有PPTE都是新分配的,而故障不仅仅是因为没有单个帧。最好的办法是在映射范围内的视图并分配PPTE时执行IO,以便在发生读取时不会发生页面错误。否则,它将不得不处理64个硬页面错误。
那有什么意义呢?最好在创建数据文件部分后立即初始化PPTE数组,因为在上述情况下只能立即读取它。我无法想到一个过程,该过程会将文件的负载映射到其地址空间中,然后再不触摸它们。即使映射了40GB,在创建数据部分时初始化的PPTE仍仅占用20MB。
答案 0 :(得分:0)
我认为这样做的好处是,尽管虚拟空间中的PPTE的整个范围将为每个进程占用,但实际上包含PPTE的保留虚拟范围实际上并未映射到物理内存。映射VACB时,将填充该范围的PPTE部分,这在物理内存中会产生一定的成本。但是,一个40GB的文件仍然只有20MB。虚拟内存中将保留20MB,而物理内存中将保留0MB。每个256KB的视图占用的物理空间量将增加64 * 8字节。