如同C里面的malloc一样,内核模式下的ExAllocatePool也是非常重要的.但是一说到ExAllocatePool函数就不得不提ExAllocatePoolWithTag函数.对比一下两个函数的调用方式:
PVOID p = ExAllocatePool(Pool_Type, Size);
PVOID p = ExAllocatePoolWithTag(Pool_Type, Size, Tag);
在调用ExAllocatePoolWithTag的时候,系统会在要求的内存大小的基础上再额外多分配4个字节的标签.这个标签占用了开始的4个字节,位于返回指针所指向地址的前面.这样,当调试时这个标签可以帮助你识别有问题的内存块.
在WDM.H(NTDDK.H)中声明了,内存分配函数无条件受预处理宏POOL_TAGGING控制(POOL_TAGGING被无条件的定义).因此,即便是调用的ExAllocatePool函数,实际执行的却是:ExAllocatePoolWithTag,其加入的标签为"mdW",指明是WDM的内存块.
又或者你强行关闭POOL_TAGGING宏再去调用ExAllocatePool,实际执行的还是ExAllocatePoolWithTag,并带标签"enoN".
因此,建议在分配内存时,直接调用ExAllocatePoolWithTag并加上一个自定义的标签.
================================================================================================================
内核模式中的基本堆分配函数是ExAllocatePool。
1.调用方式如下:
PVOID p = ExAllocatePool(type, nbytes);
2.参数
type参数是常量,如下表所示。
POOL_TYPE 描述
NonPagedPool 从非分页内存池中分配内存
PagedPool 从分页内存池中分配内存
NonPagedPoolMustSucceed 从非分页内存池中分配内存,如果不能分配则产生bugcheck
NonPagedPoolCacheAligned 从非分页内存池中分配内存,并确保内存与CPU cache对齐
NonPagedPoolCacheAlignedMustS 与NonPagedPoolCacheAligned类似,但如果不能分配则产生bugcheck
PagedPoolCacheAligned 从分页内存池中分配内存,并确保内存与CPU cache对齐
nbytes是要分配的字节数。
返回值是一个内核模式虚拟地址指针,指向已分配的内存块。如果内存不足,则返回一个NULL指针。
3.使用原则
调用ExAllocatePool时的最基本原则是被分配内存块是否可以交换出内存。
这取决于驱动程序的哪一部分需要访问这块内存。如果在大于或等于DISPATCH_LEVEL级上使用该内存块,那么必须从非分页池中分配内存。如果你总是在低于DISPATCH_LEVEL级上使用内存块,那么既可以从非分页池中分配内存也可以从分页池中分配内存。
4.对齐
你获得的内存块至少是按8字节边界对齐的。如果把某结构的实例放到分配的内存中,那么编译器赋予结构成员的4或8字节偏移在新内存中也将是4或8字节偏移。但在某些RISC平台上,结构成员可能以双字和四字对齐。出于性能上的考虑,希望内存块能适合处理器cache行的最少可能数,使用XxxCacheAligned类型代码可以达到这个要求。如果请求的内存多于一页,那么内存块将从页的边界开始。
5.内存释放
调用ExFreePool可以释放由ExAllocatePool分配的内存块:
ExFreePool((PVOID) p);
6.ExAllocatePoolWithTag函数
PVOID p = ExAllocatePoolWithTag(Pool_Type, Size, Tag);
在调用ExAllocatePoolWithTag的时候,系统会在要求的内存大小的基础上再额外多分配4个字节的标签.这个标签占用了开始的4个字节,位于返回指针所指向地址的前面。调试时,如果你查看分配的内存块会看到这个标签,它帮助你识别有问题的内存块。例如:
PVOID p = ExAllocatePoolWithTag(PagedPool, 42, 'KNUJ'); //使用了一个32位整数常量作为标签值
7.预处理宏POOL_TAGGING
WDM.H(NTDDK.H中也是)中无条件地定义了POOL_TAGGING,结果,无标签的函数实际上是宏,它真正执行的是有标签函数并加入标签‘ mdW’(指明为WDM的内存块)。
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'frPD')
#endif
由于POOL_TAGGING宏的存在,当你在程序中调用ExAllocatePool时,最终被调用的将是ExAllocatePoolWithTag。如果你关闭了该宏,自己去调用ExAllocatePool,但ExAllocatePool内部仍旧调用ExAllocatePoolWithTag并带一个‘enoN’(即None)的标签。因此你无法避免产生内存标签。所以你应该明确地调用ExAllocatePoolWithTag并加上一个你认为有意义的标签。实际上,Microsoft强烈鼓励你这样做。