3.1.6 内核的内存操作
和应用程序一样,Windows驱动程序的局部变量是存放在栈空间中的,但栈空间不会像应用程序一样大,所以驱动程序不适合使用递归调用或者局部变量是大型结构体的情况。Windows驱动程序使用的内存资源非常珍贵,因此分配内存时要尽量节约。如果需要大型结构体,可在堆(heap)中申请。在堆中申请内存的函数原型如下:
PVOID ExAllocatePool(IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes);
PVOID ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,IN ULONG Tag);
PVOID ExAllocatePoolWithQuota(IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes);
PVOID ExAllocatePoolWithQuotaTag(IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,IN ULONG Tag);
上述函数中,参数PoolType是个枚举变量,如果此值为NonPagedPool,则所分配的内存为非分页内存[1],如果此值为PagedPool,则分配的内存为分页内存;参数NumberofBytes表示的是所分配内存的大小,注意,其最好是4的整数倍。
函数返回分配的内存地址,一定是内核模式地址。如果其返回NULL,则代表分配失败。
以上四个函数功能类似,以WithQuota结尾的函数代表分配的时候按配额分配。以WithTag结尾的函数,其功能和ExAllocatePool类似,唯一不同的是多了一个Tag参数,除系统要求分配的内存外又额外为标签分配了4字节。在调试的时候,可以查看标有这个标签的内存是否被释放。
将分配的内存进行回收的是ExFreePool和ExFreePoolWithTag函数,其原型如下:
VOID ExFreePool(IN PVOID P);
VOID ExFreePoolWithTag(IN PVOID P,IN ULONG Tag);
上述函数中,参数P就是要释放的地址。
下面为在WinPcap中使用内核内存操作的代码:
PWCHAR getAdaptersList(void)
{
……
PWCHAR DeviceNames=
(PWCHAR)ExAllocatePoolWithTag(PagedPool,BufLen,'0PWA');
……
ExFreePool(DeviceNames);
}
[1]Windows规定,有些虚拟内存页面是可以交换到文件中的,这类内存称为分页内存。而有些虚拟内存页面永远不会交换到文件中,这类内存称为非分页内存。