0
点赞
收藏
分享

微信扫一扫

XpSp3(未开启PAE模式)内存管理之系统PTE区域 下


    前面< ​​XpSp3(未开启PAE模式)内存管理之系统PTE区域 上​​>的结尾部分留了一个疑问:系统空闲PTE链表的结尾部分和全局变量nt!MmSystemPtesEnd中保存的值不同。

这篇尝试解释是什么导致了这样的差异。

    系统启动阶段时会调用nt!MiInitializeSystemPtes来初始化系统PTE区域。就如神话故事中天地是混在一起的一样,初始时,MmFirstFreeSystemPte指向的空闲链表中只有一个PTE节点,节点中共包含0xa244个系统PTE:

kd> bu nt!MiReserveAlignedSystemPtes ;内核调试时windbg第一次中断,此时系统还没有加载,因此只能下延迟断点。
kd> g ;系统调用nt!MiInitializeSystemPtes初始化系统PTE区域时会调用nt!MiReserveAlignedSystemPtes 参见<windows内核原理与实现>
Breakpoint 1 hit
nt!MiReserveAlignedSystemPtes:
8054bf66 8bff mov edi,edi
kd> kb ;回溯函数调用栈
ChildEBP RetAddr Args to Child
f7c55768 8054c040 --->00000020<---请求PTE的数量为0x20 00000000 00000000 nt!MiReserveAlignedSystemPtes
f7c5578c 80509e17 00000020 00000000 00000400 nt!MiReserveSystemPtes+0xab
*** ERROR: Symbol file could not be found. Defaulted to export symbols for BOOTVID.dll -
f7c557f0 f7c4691b 000a0000 00000000 00020000 nt!MmMapIoSpace+0xb1
WARNING: Stack unwind information not available. Following frames may be wrong.
f7c55824 806ab5a7 806a2c01 00000000 806a2c01 BOOTVID!VidInitialize+0xe7
f7c55838 806a23be 80087000 00000013 00000000 nt!InbvDriverInitialize+0x6c
f7c55dac 8057beff 80087000 00000000 00000000 nt!Phase1Initialization+0xcb <----2 系统初始化阶段
f7c55ddc 804f98ea 806a22fa 80087000 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16 <---------1
kd> x nt!MmFirstFreeSystemPte ;查看此时系统空闲PTE链表
805609c0 nt!MmFirstFreeSystemPte = <no type information>
kd> dd 805609c0 L1
805609c0 ed400000 ;这个节点是空闲链表中的第一个节点
kd> ?? 0xc0000000+4*(0xed400000>>0x0c)
unsigned int 0xc03b5000
kd> dt ntoskrnl!_MMPTE_LIST 0xc03b5000
+0x000 Valid : 0y0
+0x000 OneEntry : 0y0 ;这个系统PTE的OneEntry位域==0,因此下一个PTE的NextEntry包含了整个PTE簇的数量
+0x000 filler0 : 0y00000000 (0)
+0x000 Prototype : 0y0
+0x000 filler1 : 0y0
+0x000 NextEntry : 0y11111111111111111111 (0xfffff) ;下一个节点为-1,即空
kd> dt ntoskrnl!_MMPTE_LIST 0xc03b5004 ;
+0x000 Valid : 0y0
+0x000 OneEntry : 0y0
+0x000 filler0 : 0y00000000 (0)
+0x000 Prototype : 0y0
+0x000 filler1 : 0y0
+0x000 NextEntry : 0y00001010001001000100 (0xa244) ;整个PTE簇的数量是0xa244

    我们再来看看当前系统PTE区域中PTE的数量和0xC03B5004中显示的数值是否一致:

kd> x nt!MmTotalFreeSystemPtes
8055bdf0 nt!MmTotalFreeSystemPtes = <no type information>
kd> dd 8055bdf0
8055bdf0 0000a244 <----

    两者一致,这充分说明了当前系统中只有一个系统PTE簇。


    正如书上讲的那样,在我调试过程中发现每次调用nt!MiReserveAlignedSystemPtes函数都会从系统PTE区域的尾部抽取足够数量的PTE来满足调用者的请求。

kd> gu ;执行直到退出函数nt!MiReserveAlignedSystemPtes
nt!MiReserveSystemPtes+0xab:
8054c040 85c0 test eax,eax
kd> dd nt!MmSystemPtesStart L1 ;系统PTE区域的起始地址并没有改变还是0xc03b5000
8055bde8 c03b5000
kd> x nt!MmFirstFreeSystemPte
805609c0 nt!MmFirstFreeSystemPte = <no type information>
kd> dd 805609c0 L1 ;空闲链表仍指向ed400000
805609c0 ed400000
kd> dt ntoskrnl!_MMPTE_LIST 0xc03b5000
+0x000 Valid : 0y0
+0x000 OneEntry : 0y0
+0x000 filler0 : 0y00000000 (0)
+0x000 Prototype : 0y0
+0x000 filler1 : 0y0
+0x000 NextEntry : 0y11111111111111111111 (0xfffff) ;下一个空闲PTE簇为-1,即为空
;这表明系统中仍只有一个系统PTE簇
kd> dd nt!MmTotalFreeSystemPtes L1
8055bdf0 0000a224 ;之前是0xa244,之前调用MiReserveAlignedSystemPtes请求0x20个PTE,现在还结余0xa224

     这样看可能并没有觉得这是从尾部抽取PTE来满足请求,但你可以仔细想想:之前从0xC03b5000~0xC03b5000+0xa244这段范围都是系统PTE;现在起始位置不变,范围缩小到0xa224,不就是尾巴被截掉了?

    好奇的你现在可能会想:在上一篇中我们看到MmFirstFreeSystemPte指向的链表中有多个PTE簇节点,但现在只有一个PTE簇节点,这是什么情况?这不得不提到MiSystemPteNBHead数组(参见<windows内核原理与实现> P226),它为每种连续页面定义了一个队列。当客户请求释放内存块时,如果对应大小的队列尚有空间可以插入,则直接插入到队列中;否则归还到MmFirstFreeSystemPte指向的链表中。回到我们现在的情形中,现在系统还在引导阶段,会多次请求系统PTE,相应的,释放系统PTE的次数会减少,因此MmFirstFreeSystemPte链表中的节点数量并没有增多;当初始化阶段过去,系统会释放一部分占用的系统PTE,因此空闲PTE链表中的节点会增多。

    上面提到一个名词MiSystemPteNBHead数组,其实,里面存放的也是从MmFirstFreeSystemPte申请的系统PTE。它的存在,只不过是为了加速系统中其他组件请求少量系统PTE时的响应速度而增加的一个缓存机制,没有什么特别的地方。但不得不说,wrk把MiSystemPteNBHead实现的太繁琐了,实在不便于理解它的设计本意。

举报

相关推荐

0 条评论