内存分配
内存分配与回收
内存分配
ExAllocatePoolWithTag(内存分配函数)
PVOID ExAllocatePoolWithTag( |
PoolType 为 POOL_TYPE 枚举类型,表示需要申请何种类型的内存。
常用的值是 NonPagedPool 与 PagedPool,NonPagedPool表示非分页内存,PagedPool表示分页内存。
注意: 非分页内存是指这块内存的内容不会被置换到磁盘上,非分页内存非常宝贵,一般用于高IRQL(大于等于DISPATCH_LEVEL)的代码中。
两个类型:NonPagedPoolExecute 与 NonPagedPoolNx
NonPagedPoolNx类型是指分配出来的非分页内存不具备“可执行”属性。对非分页内存进行读写而不需要进行代码或指令执行,那么可以使用NonPagedPoolNx类型的内存
NonPagedPoolExecute 类型与 NonPagedPool 类型等价。
NumberOfBytes参数表示需要申请内存大小,单位是字节。
Tag参数是一个4个字节的标志,一般每个驱动程序定义一个自己的内存标记。也可以在每个模块中定义单独的内存标记。内存标记是随意的32位数字。即使冲突也不会有什么问题,这个Tag一般用于问题排查,如内存泄露,系统蓝屏等。
注意: ExAllocatePoolWithTag 函数成功执行后返回分配内存的首地址,失败返回NULL。使用过程中务必对该函数返回值进行判断。
下的例子,是把一个字符串src拷贝到字符串dst。
// 定义一个内存分配标记 |
内存释放
ExFreePoolWithTag(内存释放函数)
ExAllocatePool这个函数已经废弃不用了,所以内核中最常见的分配内存的方法就是调用ExAllocatePoolWithTag函数。ExAllocatePoolWithTag的原型如下:
VOID ExFreePoolWithTag(PVOID P,ULONG Tag); |
P 参数表示需要释放的内存块地址
Tag 参数对应内存申请时的标记
注意:
- 内存分配和释放函数要配套使用,分配内存使用ExAllocatePool函数,释放使用相应的ExFreePool函数。
- ExAllocatePoolWithTag分配的内存可以使用ExFreePool来释放。如果不释放,则永远泄漏。并不像用户进程关闭后自动释放所有分配的空间。即使驱动程序动态卸载,也不能释放空间。唯一的办法是重启计算机。
旁视列表
旁视列表的原理:
开发者首先初始化一个“旁视列表”对象,初始化时设置“旁视列表”中内存块的大小,在需要使用内存的时候,直接向“旁视列表”对象申请内存(申请的内存的大小为初始值申请值),在内存使用完毕后,需要通过“旁视列表”对象来回收这些内存;最后,当不再需要使用“旁视列表”对象时将其删除。
旁视列表内存机制:
旁视列表 对象内部会维护内存的使用状态,一块内存使用结束后,会释放回“旁视列表”对象内,但这块内存不会马上被释放到操作系统的Pool中。如果这个时候开发者向“旁视列表”对象申请内存,“旁视列表”对象会把刚才回收的内存块返回给申请者。
这种类似“缓存”的机制,“旁视列表”对象对内存进行了二次管理,减少了向系统Pool申请或释放的次数,提高了性能。
“旁视列表” 初始化
void ExinitializeNPagedLookadideList( |
Lookaside 参数表示被初始化的“旁视列表”对象的指针,在64位系统下,这个指针必须以16字节对齐。
Allocate 参数是一个函数指针,从已初始化的“旁视列表”对象分配内存时,系统会调用开发者设置的 Allocate 函数。
PVOID XxxAllocate( |
设置Allocate参数为NULL,系统则使用默认的内存分配函数
**Free **参数是一个函数指针,删除从“旁视列表”对象中申请出来的内存块时,系统就会调用 Free 参数指向的函数
VOID XxxFree(PVOID Buffer); |
设置 Free 参数为NULL,在这种情况下,系统使用默认的释放函数。
Flags 参数控制“旁视列表”对象的内存分配行为,这个参数只有在Windows 8以及后续 系统中才有意义。
POOL_NX_ALLOCATION:表示分配的非分页内存的属性为“不可执行”,类似上一节 介 绍的NonPagedPoolNx标志。
POOL_RAISE_IF_ALLOCATION_FAILURE:表示如果内存失败,将抛出一个异常。
如果没有特殊要求,可以把Flags参数设置为0。
Size 参数表示每次从“旁视列表”对象中申请内存的固定大小,单位是字节,这个值不能 小于LOOKASIDE_MINIMUM_BLOCK_SIZE。
LOOKASIDE_MINIMUM_BLOCK_SIZE是WDK定义的一个宏,定义如下:
RTL_SIZEOF_THROUGH_FIELD定义如下:
|
RTL_SIZEOF_THROUGH_FIELD宏计算的是type结构体中field成员距离结构体首地址的偏移大小,加上field成员本身的大小。
LOOKASIDE_MINIMUM_BLOCK_SIZE宏来说,计算的是Next成员与SLIST_ENTRY首地址的距离加上Next成员自身的大小。
SLIST_ENTRY定义:
typedef struct _SLIST_ENTRY{ |
在64位系统下,LOOKASIDE_MINIMUM_BLOCK_SIZE宏的值为8;
Tag 参数表示分配内存时所使用的标记,与ExAllocatePoolWithTag函数中的Tag参数函数一样。
Depth 参数是一个保留参数,没有意义,传递0即可;
内存申请与释放
内存申请函数
ExAllocateFormNPageLookasideList(PNPAGED_LOOKASIDE_LIST Lookaside); |
只需要传入“旁氏列表” 对象的地址传入上面的函数,就可以分配函数指定大小的内存。执行成功的话就会返回相应的内存块,否则返回NULL。
内存释放函数
ExFreeToNPagedLookasideList( |
Lookaside为“旁视列表”对象指针,Entry指针表示需要释放的内存块,也就是ExAllocateFromNPagedLookasideList的返回值。
旁氏列表删除
删除函数
ExDeleteNPageLookasideList(PNPAGED_LOOKASIDE_LIST Lookaside) |
Lookaside 参数表示需要删除的“旁视列表”对象。