使用后备列表

必须动态分配固定大小缓冲区以执行按需 I/O 操作的驱动程序可以使用 ExXxxLookasideListExExXxxLookasideList 支持例程。 此类驱动程序初始化其 lookaside 列表后,操作系统将在驱动程序的 lookaside 列表中保留一些给定大小的动态分配缓冲区,从而有效地为驱动程序保留一组可重用的固定大小的缓冲区。 驱动程序固定大小缓冲区的格式和内容 (在其旁观列表中也称为 条目) 由驱动程序确定。

例如,必须为基础 SCSI 端口/微型端口驱动程序设置 SCSI 请求块的存储类驱动程序 (SDB) 使用 lookaside 列表。 此类驱动程序根据需要从其 lookaside 列表中为 SRB 分配缓冲区,并将每个 SRB 缓冲区释放回 lookaside 列表,以便每当 SRB 返回到已完成的 IRP 中的类驱动程序时,该查找列表即可重复使用。 由于存储类驱动程序无法预先确定它在任何时候必须使用多少 SRB,因为驱动程序上的 I/O 需求增加和下降,因此查看列表是管理此类驱动程序中固定大小 SRB 的缓冲区分配和解除分配的方便且经济的方法。

操作系统维护当前使用的所有分页和非分页查看列表的状态,动态跟踪所有列表中条目的分配和解除分配需求,以及新条目的可用系统池。 当对分配的需求很高时,操作系统会增加它在每个旁观列表中保留的条目数。 当需求再次下降时,它将多余的旁观条目释放回系统池。

查看列表是线程安全的。 查看列表具有内置同步功能,使驱动程序中多个并发运行的线程能够共享查看列表。 这些线程可以安全地分配共享旁视列表中的缓冲区,并将这些缓冲区释放到列表中,而无需驱动程序显式同步这些操作。 但是,为了避免可能的泄漏和数据损坏,共享查看列表的一组线程必须明确同步列表的初始化和删除。

Lookaside 列表接口

从 Windows Vista 开始, LOOKASIDE_LIST_EX 结构描述可包含分页缓冲区或非分页缓冲区的旁观列表。 如果驱动程序为此旁观列表提供自定义 的“分配”“免费 ”例程,则这些例程将接收专用上下文作为输入参数。 驱动程序可以使用此上下文收集查找列表的私有数据。 例如,上下文可用于计算列表动态分配和释放的列表条目数。 有关演示如何以这种方式使用上下文的代码示例,请参阅 ExInitializeLookasideListEx

以下系统提供的例程支持 LOOKASIDE_LIST_EX 结构描述的旁观列表:

ExAllocateFromLookasideListEx

ExDeleteLookasideListEx

ExFlushLookasideListEx

ExFreeToLookasideListEx

ExInitializeLookasideListEx

从 Windows 2000 开始, PAGED_LOOKASIDE_LIST 结构描述包含分页缓冲区的旁观列表。 如果驱动程序为此查找列表提供自定义 的“分配” 和“ 免费 ”例程,则这些例程不会接收专用上下文作为输入参数。 出于此原因,如果你的驱动程序只打算在 Windows Vista 和更高版本的 Windows 上运行,请考虑使用 LOOKASIDE_LIST_EX 结构而不是 PAGED_LOOKASIDE_LIST 结构作为查看列表。 以下系统提供的例程支持 PAGED_LOOKASIDE_LIST 结构描述的旁观列表:

ExAllocateFromPagedLookasideList

ExDeletePagedLookasideList

ExFreeToPagedLookasideList

ExInitializePagedLookasideList

从 Windows 2000 开始, NPAGED_LOOKASIDE_LIST 结构描述包含非分页缓冲区的旁观列表。 如果驱动程序为此查找列表提供自定义 的“分配” 和“ 免费 ”例程,则这些例程不会接收专用上下文作为输入参数。 同样,如果你的驱动程序只打算在 Windows Vista 和更高版本的 Windows 上运行,请考虑使用 LOOKASIDE_LIST_EX 结构,而不是对 lookaside 列表使用 NPAGED_LOOKASIDE_LIST 结构。 以下系统提供的例程支持 NPAGED_LOOKASIDE_LIST 结构描述的旁观列表:

ExAllocateFromNPagedLookasideList

ExDeleteNPagedLookasideList

ExFreeToNPagedLookasideList

ExInitializeNPagedLookasideList

实现准则

若要实现使用 LOOKASIDE_LIST_EX 结构的 lookaside 列表,请遵循以下设计准则:

  • 调用 ExInitializeLookasideListEx 以设置 lookaside 列表。 在此调用中,指定查看列表中的条目是分页缓冲区还是非分页缓冲区。 如果驱动程序本身或其向其传递查看列表条目的任何基础驱动程序可能访问 IRQL >= DISPATCH_LEVEL的这些条目,请使用非分页缓冲区。 仅当始终在 IRQL <= APC_LEVEL 访问驱动程序的 lookaside 列表条目时,才使用分页缓冲区。

  • 无论列表中的条目是分页还是非分页,查找列表的 LOOKASIDE_LIST_EX 结构都必须始终驻留在非分页系统内存中。

  • 为了提高性能,请将 AllocateFree 参数的 NULL 指针传递给 ExInitializeLookasideListEx,除非分配和解除分配例程必须执行更多操作,而不仅仅是为 lookaside 列表条目分配和释放内存。 例如,这些例程可能会记录有关驱动程序使用动态分配缓冲区的信息。

  • 驱动程序提供的 Allocate 例程可以将输入参数 (PoolTypeTagSize) 直接传递给 ExAllocatePoolWithTagExAllocatePoolWithQuotaTag 例程,以分配新缓冲区。

  • 对于每次调用 ExAllocateFromLookasideListEx,只要不再使用以前分配的条目,就尽快对 ExFreeToLookasideListEx 进行倒数调用。

提供仅调用 ExAllocatePoolWithTagExFreePool分配免费例程会浪费处理器周期。 当驱动程序将 NULLAllocateFree 指针传递给 ExInitializeLookasideListEx 时,ExAllocateFromLookasideListEx 会自动对 ExAllocatePoolWithTagExFreePool 进行必要的调用。

任何驱动程序提供的 分配 例程都不得为分页池中的条目分配内存,该条目要保存在非分页查看列表中,反之亦然。 它还必须分配固定大小的条目,因为对 ExAllocateFromLookasideListEx 的任何后续驱动程序调用将返回 lookaside 列表中当前保留的第一个条目,除非列表为空。 也就是说,仅当给定的 lookaside 列表当前为空时,调用 ExAllocateFromLookasideListEx 才会调用驱动程序提供的 Allocate 例程。 因此,每次调用 ExAllocateFromLookasideListEx 时,仅当 lookaside 列表中的所有条目都处于固定大小时,返回的条目将正好是驱动程序所需的大小。 驱动程序提供的分配例程也不应更改驱动程序最初传递给 ExInitializeLookasideListExTag 值,因为池标记值的更改会使调试和跟踪驱动程序的内存使用情况更加困难。

调用 ExFreeToLookasideListEx 存储之前在 lookaside 列表中分配的条目,除非列表已 (即,列表包含系统确定的最大条目数) 。 为了提高性能,驱动程序应尽可能快地对 ExFreeToLookasideListEx 进行对 ExAllocateFromLookasideListEx 的每次调用进行互惠调用。 当驱动程序将条目快速释放回其旁观列表时,该驱动程序的下一次调用 ExAllocateFromLookasideListEx 不太可能产生性能损失,即动态分配新条目的内存。

类似的准则适用于使用 PAGED_LOOKASIDE_LISTNPAGED_LOOKASIDE_LIST 结构的旁观列表。