频繁申请和回收内存,会导致在内存上产生大量的内存“空洞”,从而导致最终无法申请内存。DDK为程序员提供了Lookaside结构来解决这个问题。
1. 频繁申请内存的弊端
频繁地申请内存,会导致一个问题,就是在内存中产生“空洞”。如图1显示了这种情况,在内存中先后申请三块内存。最开始可用的内存是连续的。当某个时刻内存块2被回收以后,如果系统想分配一块略微大于原先内存块2的内存,这时候原先的内存2就不能被申请成功。因此,频繁地申请、回收内存会导致在内存上产生大量的内存“空洞”。
如果系统中存在大量的内存“空洞”,即使内存中有大量的可用内存,也会导致申请内存失败。在操作系统空闲的时候,系统会整理内存中的“空洞”,将内存中的“空洞”进行合并。
2. 使用Lookaside
如果驱动程序需要频繁地从内存中申请、回收固定大小的内存,DDK提供了一种机制来解决这个问题,这就是使用Lookaside对象。
可以将Lookaside对象想象成一个内存容器。在初始的时候,它先向Windows申请了一块比较大的内存。以后程序员每次申请内存的时候,不是直接向Windows申请内存,而是向Lookaside对象申请内存。Lookaside对象会智能地避免产生内存“空洞”。如果Lookaside对象内部的内存不够用时,它会向操作系统申请更多的内存。当Lookaside对象内部有大量的未使用的内存时,它会自动
让Windows回收一部分内存。总之,Lookaside是一个自动的内存分配容器。通过对Lookaside对象申请内存,效率要高于直接向Windows申请内存。Lookaside一般会在以下情况使用:
(1). 程序员每次申请固定大小的内存。
(2). 申请和回收的操作十分频繁。
如果程序员遇到上述两种情况,可以考虑使用Lookaside对象。驱动程序中的运行效率是程序员必须考虑的问题。如果驱动程序的运行效率低,会严重影响到操作系统的性能。使用Lookaside
对象,首先要初始化Lookaside对象,有以下两个函数可以使用:
VOID ExInitializeNPagedLookasideList (
__out PNPAGED_LOOKASIDE_LIST Lookaside,
__in_opt PALLOCATE_FUNCTION Allocate,
__in_opt PFREE_FUNCTION Free,
__in ULONG Flags,
__in SIZE_T Size,
__in ULONG Tag,
__in USHORT Depth
);
VOID ExInitializePagedLookasideList (
__out PPAGED_LOOKASIDE_LIST Lookaside,
__in_opt PALLOCATE_FUNCTION Allocate,
__in_opt PFREE_FUNCTION Free,
__in ULONG Flags,
__in SIZE_T Size,
__in ULONG Tag,
__in USHORT Depth
);
这两个函数分别是对非分页和分页Lookaside对象进行初始化。
在初始化完Lookaside对象后,可以进行申请内存的操作了,有以下两个函数。
PVOID ExAllocateFromNPagedLookasideList (
__inout PNPAGED_LOOKASIDE_LIST Lookaside
)
PVOID ExAllocateFromPagedLookasideList (
__inout PPAGED_LOOKASIDE_LIST Lookaside
);
这两个函数分别是对非分页内存和分页内存的申请。
对Lookaside对象回收内存的操作,有以下两个函数:
VOID ExFreeToNPagedLookasideList (
__inout PNPAGED_LOOKASIDE_LIST Lookaside,
__in PVOID Entry
);
VOID ExFreeToPagedLookasideList (
__inout PPAGED_LOOKASIDE_LIST Lookaside,
__in PVOID Entry
);
这两个函数分别是对非分页内存和分页内存的回收。
在使用完Lookaside对象后,需要删除Lookaside对象,有以下两个函数:
VOID ExDeleteNPagedLookasideList (
__inout PNPAGED_LOOKASIDE_LIST Lookaside
);
VOID ExDeletePagedLookasideList (
__inout PPAGED_LOOKASIDE_LIST Lookaside
);
这两个函数分别是对非分页和分页Lookaside对象删除。
使用示例:
#define ARRAY_NUMBER 50
#pragram INITCODE
VOID LookasideTest()
{
// 初始化Lookaside对象
PAGED_LOOKASIDE_LIST pageList;
ExInitializePagedLookasideList(&pageList, NULL, NULL, 0, sizeof(MYDATASTRUCT), '1234', 0);
PMYDATASTRUCT MyObjectArray(ARRAY_NUMBER);
// 模拟频繁申请内存
for (int i = 0; i < ARRAY_NUMBER; i++)
{
MyObjectArray[i] = (PMYDATASTRUCT)ExAllocateFromPagedLookasideList(&pageList);
}
// 模拟频繁回收内存
for (int i = 0; i < ARRAY_NUMBER; i++)
{
ExFreeToPagedLookasideList(&pageList, MyObjectArray[i]);
MyObjectArray[i] = NULL;
}
// 删除Lookaside对象
ExDeletePagedLookasideList(&pageList);
}