Windows Debugging之六

内存管理

===============

内存管理器(memory manager)提供了一系列的系统服务来分配(allocate)和释放(free)虚拟内存, 在进程间共享内存, 将文件映射到内存, 冲洗(flush)虚拟页面到磁盘上, 重新获取关于一系列虚拟页面的信息, 修改虚拟页面的保护设置, 锁定虚拟页面到内存中.

内存管理器的两个主要任务

  • 转换, 映射进程的虚拟地址空间到物理内存上. 这样, 当一个该进程中的线程在运行时读写虚拟地址空间, 正确的物理内存会被引用到. 进程的虚拟地址空间的, 存在于物理内存上的子集, 叫做working set.
  • 把内存中的某些过量使用的内容以页的形式写会到磁盘中. 所谓过量使用, 即运行中的线程或者系统代码视图更多的使用物理内存而不是当前可用的内存内容. 

如同其他的windows可执行服务一样, 内存管理服务允许他们的调用者提供进程句柄, 标识出究竟在操纵那个进程的虚拟内存. 调用者可以操纵他自己的内存, 也可以(在正当权限下)操纵另一个进程的内存. 比如说, 如果一个进程创建了一个子进程, 默认它是有权限来操纵子进程的虚拟内存的. 从那以后, 父进程可以代表子进程分配,释放, 读, 写, 内存, 方法是通过调用虚拟内存服务, 调用时将子进程的句柄当做参数传递. 这个特性被子系统使用来管理客户进程内存, 并且这也是实现debugger的关键技术, 因为debuggers必须能够读和写被debug的内存.

内存管理器同时还提供相当数量的服务, 比如分配和释放物理内存; 为了给其他的内核态的组件执行direct menory access(DMS)访问转换而锁定物理内存中的页面, 这样的锁定如同设备驱动一样. 这些函数都已前缀Mm开头. 另外, 对于内存管理器的某些部分并不是限制的很死的, 执行支持函数(the executive support routines)以前缀Ex开头, 用来从系统堆(分页的和未分页的池)上分配和释放,如同操纵旁侧模式的列表一样.

 2009-11-10 8-43-41

系统内存池

===============

在系统初始化的时候, 内存管理器创建两种动态大小的内存池, 内核态的组件使用这两种池来分配系统内存.

  • 非分页池---包含系统范围的虚拟内存地址空间, 系统保证该空间内的数据是一直存在物理内存中的, 并且保证可以在任意时刻访问(任何IRQL等级, 任何进程上下文)而不需要遭受页面错误(page fault). 需要非分页内存是因为有一条规则: 页面错误不能满足DPC/dispatch水平, 或更高水平的要求.
  • 分页池--- 在系统空间中可以被分页, 可以被置换进入和置换出系统的虚拟内存区域. 设备驱动程序不需要从DPC/dispatch水平或者更高的水平上来使用, 所以他们可以使用paged pool(分页池). 分页池的内存是任意进程上下文都可以访问的.

这两种内存池都存在于系统地址空间内, 并且被映射到任何一个进程的虚拟地址空间中. 执行者(the executive)提供函数去从这两种池中分配,释放内存. ExAllocatePool函数就是这些函数之一.

有两种非分页池: 一种是通用的, 另一种稍微小一些(4个page), 是系统为紧急情况而保留下来的, 所谓紧急情况是指非分页内存满了, 并且调用者不能容许分配错误的时候. (后一种内存池已经不再使用了, 设备驱动程序可以在低内存的情况下, 恰当的写入. 驱动分析器(driver verifier)让这种测试很容易.) 单处理器系统有三种分页池: 多处理器系统有五种. 拥有多余一种的内存池减小了系统代码阻塞并发的内存池请求的概率, 分页的和非分页的内存池根据物理内存的大小而拥有不同的初始值, 然后慢慢增长. 如果必要的话, 在系统启动的时候涨到最大值. 你可以覆盖这些池的初始值, 方法是修改注册表中的键值NonPagedPoolSize 和PagedPoolSize, 位置在HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management. 0指的让系统自动计算尺寸, 或者指定一个尺寸, 单位是byte.

地址空间布局

================

默认情况下, 32位Windows 每一个用户进程可以拥有高达2GB的私有地址空间, 操作系统拥有剩下的2GB. Windows服务器系列支持启动时的一个选项, 该选项允许3GB的用户地址空间. Windows XP和Windows Server 2003包括一个开关(/USERVA), 能允许用户地址空间在2G和3G之间切换. 这两种内存地址空间的布局详见下图.

2009-11-9 20-19-59

对于一个地址空间超过2G的进程来说, 镜像文件必须要有IMAGE_FILE_LARGE_ADDRESS_AWARE这个标志位在image header中被设定. 否则, Windows保留额外的地址空间, 所以应用程序不会看到大于0x7FFFFFFF的地址空间. 这个标志是通过在链接应用程序, 构造可执行程序的时候, 设置链接器标志

/LARGEADDRESSAWARE来完成的. 该标志在运行着2G用户地址空间的操作系统上是没有作用的. 另外, 如果你启动了3BG开关, 但是操作系统并不支持3GB用户地址空间, 那么系统空间会变成1GB, 用户地址空间最大还是2G, 即使该设置的标志位都设置了, 也还是没有用, 因为操作系统不支持.

x86系统地址空间布局

==========================

x86架构在系统空间中有如下的组件:

  • 系统代码--- 包括操作系统镜像, HAL, 和启动操作系统时的设备驱动程序
  • 系统映射视图--- 用来映射Win32k.sys, 这个内核态Win32子系统的可加载的部分, 还有它使用的内核态图形驱动程序.
  • 会话空间--- 用来映射一个用户会话的具体信息. 在终端服务安装了的情况下, Windows server支持多用户会话. Session working set list描述了会话空间中贮存的和正在使用的部分.
  • 进程页表和分页目录--- 描述虚拟地址映射的数据结构
  • Hyperspace--- 一个特殊的区域用来映射进程的working set list和临时映射到其他物理内存页, 映射到其他物理内存页的目的是执行诸如将一个free list上的也清零, 或者是使其他页表的页表入口无效(比如一个页被从standy list中移除), 或者在进程创建时建立一个新的进程地址空间.
  • system working set list--- 描述system working set
  • System cache--- 虚拟地址空间被用来映射在系统缓存(system cache)中打开的文件
  • Paged Pool--- 可分页的系统内存堆
  • System page table entries(PTEs)- system PTE使用的pool, 用来映射系统页(诸如IO space, kernel stacks, memory descriptor lists). 你可以看到有多少系统PTE是available的, 通过检查内存的值: 在性能工具中的Free System Page Table Entries 标签(counter )
  • Nonpaged Pool --- 非分页的系统内存堆, 通常有两部分, 一种在系统地地址端, 另一种在系统地址高端.
  • Crash dump information--- 保留下来用来保存系统崩溃的信息.
  • HAL usage--- 保留下来的系统内存用来存放HAL相关的结构.
原文地址:https://www.cnblogs.com/awpatp/p/1599269.html