作为开发Windows驱动程序的程序员,需要比普通程序员更多了解Windows内部的内存管理机制,并在驱动程序中有效地使用内存。在驱动程序的编写中,分配和管理内存不能使用熟知的Win32 API函数,取而代之的是DDK提供的高效内核函数。C语言和C++中大多数关于内存操作的运行时函数,大多在内核模式下是无法使用的。例如,C语言中的malloc函数和C++中的new操作符等。
内存管理的概念:
1.物理内存:
PC上有三条总线,分别是数据总线,地址总线和控制总线。32位的CPU寻址能力是4GB。用户最多可以使用4GB的真实物理内存。PC中会拥有许多设备,其中很多设备都拥有自己的设备内存,这部分的设备内存会映射到PC机的物理内存上,读写这段物理地址其实会读写设备内存地址。
2虚拟内存:
.虽然可以寻址4GB的内存,而PC里往往没有如此多的真实物理内存。操作系统和硬件为使用者提供了虚拟内存的概念。虚拟内存和物理内存之间的转换暂不讨论。
3.用户模式地址和内核模式地址:
虚拟地址在0~0X7FFFFFFF范围内的虚拟内存,即低2GB的虚拟地址,被称为用户模式地址。而0X80000000~0XFFFFFFFF范围内的虚拟内存,即高2GB的虚拟内存,被称为内核模式地址。
4.Windows驱动程序和进程的关系:
驱动程序可以看成一个特殊的DLL文件被应用程序加载到虚拟内存中,只不过加载地址是内核模式地址,而不是用户模式地址。
5.分页和非分页内存:
Windows规定有些虚拟内存页面是可以交换到文件中的,这类内存成为分页内存。而有些虚拟内存页永远不会交换到文件中,这些内存被称为非分页内存。
当程序的中断请求级在DISPATCH_LEVEL之上时,程序只能使用非分页内存,否则会导致蓝屏死机。
在编译DDK提供的例程时,可以指定某个例程和某个全局变量是载入分页内存还是非分页内存,需要做如下定义:
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")
#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")
如果将某个函数在入到分页内存,我们需要在函数的实现中加入如下代码:
#pragma INITCODE
VOID SomeFunction()
{
PAGED_CODE();
//do something
}
如果要让程序加载到非分页内存,需要在函数的实现中加入如下代码:
#pragma LOCKEDCODE
VOID SomeFunction()
{
//do something
}
还有一种特殊情况,就是某个例程初始化的时候载入内存,然后就可以从内存中卸载掉,例如DriverEntry
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath
)
{
}