1. 内存管理概念
1.1 物理内存概念
1.2 虚拟内存
DDK中宏PAGE_SIZE记录分页大小,一般为4KB。
1.3 用户模式地址和内核模式地址
低2G的虚拟地址为用户模式地址,为0~0X7FFFFFFF。
高2G的虚拟地址为内核模式地址,为0X80000000~0XFFFFFFFF。
进程切换时,内核地址完全相同,只改变用户模式地址的映射。
1.4 windows驱动程序和进程的关系
windows驱动程序的不同例程运行在不同的进程中。DriverEntry和AddDevice例程运行在系统(System)进程中。其他一些例程运行在应用程序的上下文。
技巧:用PsGetCurrentProcess()函数可以得到当前进程,打印出来可以显示出当前进程的进程名,用DebugView软件查看log信息。
VOID DisplayItsProcessName()
{
//得到当前进程
PEPROCESSpEProcess = PsGetCurrentProcess();
//等到当前进程名称,0x174偏移位置对应ImageFileName
PTSTRProcessName = (PTR)((ULONG)pEProcess + 0x174;
KdPrint((“%s\n”,ProcessName));
}
1.5 分页与非分页内存
分页内存:虚拟内存页面可以交换到文件中
非分页内存:虚拟内存页面永远不会交换到文件中。
注意:当程序的中断请求级在DISPATCH_LEVEL之上时(包括DISPATCH_LEVEL层),程序只能使用非分页内存,否则将导致蓝屏死机。
在编译DDK提供的例程时,指定某个例程和某个局部变量载入分页内存还是非分页内存,需做如下定义。常放在头文件中。
#define PAGEDCODEcode_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODEcode_seg("INIT")
#define PAGEDDATAdata_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATAdata_seg("INIT")
将某个函数载入到分页内存中:
VOID SomeFunction ()
{
PAGED_CODE();
//Do something
}
PAGED_CODE()是DDK提供的宏,只在check版本中生效。会检验函数是否运行低于DISPATCH_LEVEL的中断请求级别,若高于或等于这个级别将产生一个断言。
将函数载入非分页内存中:
#pragma LOCKEDCODE
VOID SomeFunction ()
{
//Do something
}
例程初始化的时候载入内存,然后就可以从内存卸载掉:
#pragma INITCODE
extern "C" NTSTATUSDriverEntry (
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath )
{
//Do something
}
1.6 分配内核内存
局部变量是放在栈(Stack)空间中的,但栈空间较小,所以驱动程序不适合递归调用或者局部变量是大的结构体。若需要大的结构体,请在堆(Heap)中申请。
堆中申请内存的函数:
PVOID //返回分配的内存地址,返回0表示分配内存失败
ExAllocatePool(
IN POOL_TYPE PoolType, //NonPagedPool或PagedPool
IN SIZE_T NumberOfBytes //最好是4的倍数
);
PVOID
ExAllocatePoolWithTag( //多分配4个字节的标签
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
PagedPool
NonPagedPoolMustSucceed
NonPagedPoolCacheAligned:分配非分页内存,且必须内存对齐
PagedPoolCacheAligned
NonPagedPoolCacheAlignedMustS:必须成功
回收内存:ExFreePool和ExFreePoolWithTag
VOID
ExFreePool(
INPVOID P //要释放的地址
);
NTKERNELAPI
VOID
ExFreePoolWithTag(
INPVOID P,
IN ULONG Tag
);