分配系统空间内存

重要

本主题中讨论的 ExAllocatePool DDI 已在 Windows 10 版本 2004 中弃用,已替换为 ExAllocatePool2ExAllocatePool3。 有关详细信息,请参阅更新对 ExAllocatePool2 和 ExAllocatePool3 的已弃用 ExAllocatePool 调用

驱动程序可以在其 设备扩展 中使用系统分配的空间作为设备特定信息的全局存储区域。 驱动程序只能使用内核堆栈将少量数据传递给其内部例程。 某些驱动程序必须分配更多、更大的系统空间内存,通常用于 I/O 缓冲区。

若要分配 I/O 缓冲区空间,最佳内存分配例程是 MmAllocateNonCachedMemoryMmAllocateContiguousMemorySpecifyCacheAllocateCommonBuffer ((如果驱动程序的设备使用总线主 DMA 或系统 DMA 控制器的自动初始化模式) )或 ExAllocatePoolWithTag

非分页池通常会随着系统运行而变得碎片化,因此驱动程序的 DriverEntry 例程应调用这些例程来设置驱动程序所需的任何长期 I/O 缓冲区。 除 ExAllocatePoolWithTag 外,每个例程都分配在处理器特定边界上对齐的内存 (由处理器的数据缓存行大小) 确定,以提供最佳性能。

驱动程序应尽可能经济地分配 I/O 缓冲区,因为非分页池内存是有限的系统资源。 通常,驱动程序应避免重复调用这些支持例程来请求小于 PAGE_SIZE 的分配,因为小于 PAGE_SIZE 的每个分配还附带用于在内部管理分配的池标头。

有关以经济方式分配驱动程序缓冲区空间的提示

若要以经济的方式分配 I/O 缓冲区内存,请注意以下事项:

  • 每次调用 MmAllocateNonCachedMemoryMmAllocateContiguousMemorySpecifyCache 始终返回系统的页大小的满倍(非分页系统空间内存),无论请求的分配大小如何。 因此,对小于页的请求将向上舍入为整页,并且页面上的任何剩余字节将浪费;调用 函数的驱动程序无法访问它们,其他内核模式代码无法使用它们。

  • 每次调用 AllocateCommonBuffer 都使用至少一个适配器对象映射寄存器,该寄存器映射至少一个字节,最多映射一个页面。 有关映射寄存器和使用常见缓冲区的详细信息,请参阅 适配器对象和 DMA

使用 ExAllocatePoolWithTag 分配内存

驱动程序还可以调用 ExAllocatePoolWithTag,为 PoolType 参数指定以下系统定义的POOL_TYPE值之一:

  • PoolType = 对于驱动程序在 IRQL > APC_LEVEL运行时可能访问的设备扩展或控制器扩展中未存储的任何对象或资源的 NonPagedPool

    对于此 PoolType 值,如果指定的 NumberOfBytes 小于或等于PAGE_SIZE,ExAllocatePoolWithTag 将分配请求的内存量。 否则,最后分配的页面上的任何剩余字节将浪费:调用方无法访问,其他内核模式代码无法使用。

    例如,在 x86 上,5 KB (KB 的分配请求) 返回两个 4 KB 页。 第二页的最后 3 KB 对调用方或其他调用方不可用。 为了避免浪费非分页池,驱动程序应有效地分配多个页面。 例如,在这种情况下,驱动程序可以进行两个分配,一个用于PAGE_SIZE,另一个用于 1 KB,以分配总共 5 KB。

    注意 从 Windows Vista 开始,系统会自动添加额外的内存,因此不需要两次分配。

  • PoolType = 始终在 IRQL <= APC_LEVEL 访问且不在文件系统的写入路径中的内存的 PagedPool

ExAllocatePoolWithTag 如果无法分配请求的字节数,则返回 NULL 指针。 驱动程序应始终检查返回的指针。 如果其值为 NULL则 DriverEntry 例程 (或任何其他返回 NTSTATUS 值的驱动程序例程) 应返回STATUS_INSUFFICIENT_RESOURCES或处理错误条件(如果可能)。