1.1.6 内核的内存操作
Windows
驱动程序使用的内存资源非常珍贵,分配内存时要尽量节约。和应用程序一样,局部变量是存放在栈
( Stack)
空间中的。但栈空间不会像应用程序那么大,所以驱动程序不适台递归调用或者局部变量是大型结构体。如果需要大型结构体,请在堆
( Heap)
中申请。
堆中申请内存的函数有以下几个,原型如下:
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
参数
PooIType
是个枚举变量,如果此值为
NonPagedPool
,则分配非分页内存。如果此值为
PagedPool
,则分配内存为分页内存。
Windows
规定有些虚拟内存页面是可以交换到文件中的,这类内存被称为分页内存。而有些虚拟内存页永远不会交换到文件中,这些内存被称为非分页内存。
参数
NumbelofBytes
是分配内存的大小,注意最好是
4
的倍数。
函数返回分配的内存地址,一定是内核模式地址。如果返回
NULL
,则代表分配失败。
以上四个函数功能类似,函数以
WithQuota
结尾的代表分配的时候按配额分配。函数以
WithTag
结尾的函数,和
ExAllocatePool
功能类似,唯一不同的是多了一个
Tag
参数,系统在要求的内存外又额外地多分配了
4
个字节的标签。在调试的时候,可以找出是否有标有这个标签的内存没有被释放。
将分配的内存,进行回收的函数是
ExFreePool
和
ExFreePoolWithTag
,它们的原型是:
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.1.7 内存操作的运行时函数
运行时函数是程序运行时必不可少的,由编译器提供。针对不同的操作系统,运行时函数的实现方法不同,但接口基本保持一致。例如,
malloc
函数就是典型的运行时函数,所有编译器厂商都必须提供这个函数,但在不同操作系统上的实现方法就不尽相同了。
1.1.7.1 内存间复制(非重叠的情况)
在驱动程序开发中,经常用到内存的复制。
DDK
为程序员提供了
RtlCopyMemory
函数。
IN VOID UNALIGNED *Destination,
IN CONST VOID UNALIGNED *Source,
IN SIZE_T Length
参数
Destination
表示要复制内存的目标地址。参数
Source
表示要复制内存的源地址。参数
Length
表示要复制的内存的长度,单位是字节。
1.1.7.2 内存间复制(可重叠的情况)
用
RtICopyMemory
可以复制内存,但其内部没有考虑目标内存与源内存重叠的情况。对可重叠的情况
DDK
提供了
RtIMoveMemory
函数。
RtlMoveMemory(
IN VOID UNALIGNED *Destination,
IN CONST VOID UNALIGNED *Source,
IN SIZE_T Length
参数
Destination
表示要复制内存的目标地址。参数
Source
表示要复制内存的源地址。参数
Length
表示要复制的内存的长度,单位是字节。
1.1.7.3 填充内存
驱动程序开发中,迹经常用到对某段内存区域用固定字节填充。
DDK
为程序员提供了
RtIFillMemory
函数。
RtlFillMemory(
IN VOID UNALIGNED *Destination,
IN SIZE_T Length,
IN UCHAR Fill
参数
Destination
为目标内存的地址。参数
Length
为待填的长度。参数
Fill
为需要填充的字节。
在驱动程序开发中,还经常要对某段内存填零,
DDK
提供了
RtlZeroMemory
函数。
RtlZeroMemory(
IN VOID UNALIGNED *Destination,
IN SIZE_T Length
参数
Destination
为目标内存的地址。参数
Length
为待填的长度。
1.1.7.4 内存比较
驱动程序开发中,还会用到比较两块内存是否一致,可采用
RtICompareMemory
函数。
RtlCompareMemory(
IN CONST VOID *Source1,
IN CONST VOID *Source2,
IN SIZE_T Length
参数
Sourcel
为比较的第一个内存地址。参数
Sou:rce2
为比较的第二个内存地址。参数
Length
为比较的长度,单位为字节。
函数返回相等的字节数。
函数
RtIEquaIMemory
通过判断返回值和
Length
是否相等,来判断两块内存是否完全一致。同时
DDK
还提供了
RtlEqualMemory
函数,直接判断两块内存是否一致。
RtlEqualMemory(
CONST VOID *Source1,
CONST VOID *Source2,
SIZE_T Length
函数
RtlEquaIMemory
在两块内存一致的情况下返回非零值,在不一致的情况下返回零。