内存管理
===============
内存管理器(memory manager)提供了一系列的系统服务来分配(allocate)和释放(free)虚拟内存, 在进程间共享内存, 将文件映射到内存, 冲洗(flush)虚拟页面到磁盘上, 重新获取关于一系列虚拟页面的信息, 修改虚拟页面的保护设置, 锁定虚拟页面到内存中.
内存管理器的两个主要任务
如同其他的windows可执行服务一样, 内存管理服务允许他们的调用者提供进程句柄, 标识出究竟在操纵那个进程的虚拟内存. 调用者可以操纵他自己的内存, 也可以(在正当权限下)操纵另一个进程的内存. 比如说, 如果一个进程创建了一个子进程, 默认它是有权限来操纵子进程的虚拟内存的. 从那以后, 父进程可以代表子进程分配,释放, 读, 写, 内存, 方法是通过调用虚拟内存服务, 调用时将子进程的句柄当做参数传递. 这个特性被子系统使用来管理客户进程内存, 并且这也是实现debugger的关键技术, 因为debuggers必须能够读和写被debug的内存.
内存管理器同时还提供相当数量的服务, 比如分配和释放物理内存; 为了给其他的内核态的组件执行direct menory access(DMS)访问转换而锁定物理内存中的页面, 这样的锁定如同设备驱动一样. 这些函数都已前缀Mm开头. 另外, 对于内存管理器的某些部分并不是限制的很死的, 执行支持函数(the executive support routines)以前缀Ex开头, 用来从系统堆(分页的和未分页的池)上分配和释放,如同操纵旁侧模式的列表一样.
系统内存池
===============
在系统初始化的时候, 内存管理器创建两种动态大小的内存池, 内核态的组件使用这两种池来分配系统内存.
这两种内存池都存在于系统地址空间内, 并且被映射到任何一个进程的虚拟地址空间中. 执行者(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之间切换. 这两种内存地址空间的布局详见下图.
对于一个地址空间超过2G的进程来说, 镜像文件必须要有IMAGE_FILE_LARGE_ADDRESS_AWARE这个标志位在image header中被设定. 否则, Windows保留额外的地址空间, 所以应用程序不会看到大于0x7FFFFFFF的地址空间. 这个标志是通过在链接应用程序, 构造可执行程序的时候, 设置链接器标志
/LARGEADDRESSAWARE来完成的. 该标志在运行着2G用户地址空间的操作系统上是没有作用的. 另外, 如果你启动了3BG开关, 但是操作系统并不支持3GB用户地址空间, 那么系统空间会变成1GB, 用户地址空间最大还是2G, 即使该设置的标志位都设置了, 也还是没有用, 因为操作系统不支持.
x86系统地址空间布局
==========================
x86架构在系统空间中有如下的组件: