所有那些连续的内存都来自哪里?
一个好奇的驱动程序编写人员问道:
“根据 Windows DDK,MmAllocateContiguousMemory 函数从不分页的内存池分配内存,不分页的内存池当前在 32 位系统上限制为 256 MB,在 64 位系统上限制为 128 GB。但是,我的驱动程序在 Windows XP 或 Server 2003 系统上成功地请求到 370 MB 内存。这怎么回事呢?”
这位驱动程序编写人员发现了 MmAllocateContiguousMemory 的一个未记录的内部功能。(虽然这个函数在 Microsoft Windows 的发布版本中如上文所述运行,但是 Microsoft 不保证它将继续这样。了解这种行为,但是不要在未来的 Windows 版本中依赖它。)
MmAllocateContiguousMemory 函数分配未分页的连续内存。这不同于所说的从未分页内存池分配连续内存。
MmAllocateContiguousMemory 首先尝试从未分页内存池分配内存。如果驱动程序请求的连续内存比未分页内存池中当前可用的内存多,那么 MmAllocateContiguousMemory 有时会尝试寻找满足驱动程序请求的连续的物理页。在这种情况下,它使用系统页表项 (PTE) 来映射内存页并将此映射返回给调用方。
MmAllocateContiguousMemory 是否搜索物理页取决于几个条件,这些条件将来有可能改变。例如,当前它只搜索调用方是否以 APC_LEVEL 或更低级别运行,因为搜索之前内存管理器必须在 APC_LEVEL 级别获得同步。
系统 PTE 的数量在操作系统的不同版本中有所不同。在 Windows 2000 中可以达到 460 MB,在 Windows XP 和更高版本中可以超过 1 GB。因此,即使在相同的物理硬件上,在 Windows XP 或 Windows Server 2003 下运行的驱动程序可能能够分配比在 Windows 2000 下多得多的内存。
请记住,内存池标记仅适用于从内存池分配的内存;通过 PTE 映射的物理内存页没有内存池标记。如果 MmAllocateContiguousMemory 通过 PTE 映射物理页,那么系统不会为内存分配加标记,因而您将不能使用 Driver Verifier 和其它监视带标记的内存分配的工具来帮助您进行调试。如果 MmAllocateContiguousMemory 在未分页内存池中找到足够的内存,那么它使用 MmCm 内存池标记为分配加标记(虽然标记名称将来也有可能改变),从而您可以使用跟踪带标记内存池分配的工具来方便地调试驱动程序。
您应该做什么?
| • | 请记住,未分页内存是一种有限的资源。只为除 DMA 以外的操作请求驱动程序所需大小的未分页内存。要为 DMA 分配缓冲区,请调用 AllocateCommonBuffer 而不是 MmAllocateContiguousMemory。 |
| • | 调试期间请注意内存分配和访问中可能出现的错误。启用 Driver Verifier 来跟踪带标记内存分配的使用。 |
| • | 请注意,MmAllocateContiguousMemory 的内部操作将来可能发生变化。不要假设 MmAllocateContiguousMemory 分配的内存比未分页内存池中可用的内存多。 |
更多信息:
Windows DDK:
MmAllocateContiguousMemory
Driver Verifier 选项:内存池跟踪,特殊内存池
PoolMon