VS2013 WDK8.1驱动开发6(内存管理)
本系列博客为学习《Windows驱动开发技术详解》一书的学习笔记。
前言
编写Windows驱动,需要了解Windows如何管理和使用内存。我们只讨论32位平台下Windows操作系统的相关知识。
物理内存
32位CPU的寻址能力为4GB个字节,用户最多可以使用4GB的真实的物理内存。除了主存(内存)之外,PC中的很多设备都提供了自己的设备内存,如显卡就会提供自己的显存,这部分内存会映射到PC的物理内存上,也就是读写这段物理内存地址,其实就是在读写显存。广义上的说物理内存包括主存和设备内存。
虚拟内存
操作系统和硬件为使用者提供虚拟内存。Windows的所有程序(包括Ring0层)可以操作的都是虚拟内存。对虚拟内存的操作最终变为对真实物理内存的操作。每个进程都有一个4GB的虚拟内存地址空间,虚拟内存被分为一个个页,每个页4KB。虚拟内存的有些页面会对应到物理内存,有些页面会被映射到磁盘上的文件。 设计虚拟内存的原因:
- 增加了内存的大小。不管PC是否有足够的4GB的物理内存,操作系统总会有4GB的虚拟内存。
- 不同进程的虚拟内存互不干扰。。
用户模式地址和内核模式地址
虚拟地址在0~0X7FFFFFFF范围内,被称为用户模式地址。0X80000000~0XFFFFFFFF范围内的虚拟地址,被称为内核模式地址。Windows规定用户态(Ring3层)的程序只能访问用户模式地址,运行在核心态(Ring0层)的程序,可以访问整个4GB的虚拟内存。Windows的核心代码和Windows的驱动程序都加载在内核地址空间中。所有进程共享一个内核地址空间。
驱动程序和进程的关系
驱动程序被加载在内核地址空间中,Windows驱动程序里的不同例程运行在不同的进程中。DriverEntry和AddDevice例程运行在系统进程中。IRP_MJ_READ和IRP_MJ_WRITE的派遣函数运行于应用程序的上下文中。 我们在驱动程序中可以添加如下函数,该函数可以打印当前进程的进程名:
/// @brief PsGetProcessImageFileName可以获得进程名,微软没有归档,但导出了,声明一下就可以使用
/// @param[in] pEProcess 进程指针
/// @return 进程名
NTKERNELAPI UCHAR* PsGetProcessImageFileName(__in PEPROCESS pEProcess);
/// @brief 显示当前进程名称
void DisplayProcessName()
{
PEPROCESS pEProcess = PsGetCurrentProcess();
PTSTR processName = PsGetProcessImageFileName(pEProcess);
KdPrint(("Process Name: %s\n", processName));
}
我们在NT驱动程序的DriverEntry、IRP_MJ_CREATE和IRP_MJ_CLOSE的派遣例程中使用该函数。
要观察到IRP_MJ_CREATE和IRP_MJ_CLOSE的派遣例程的运行,我们还需要创建一个Win32程序(chapter05-win32.exe)来打开我们创建的设备对象:
#include <cstdio>
#include <Windows.h>
int main()
{
// 触发IRP_MJ_CREATE
HANDLE hDevice = CreateFileA(
"\\\\.\\HelloNTDriver1",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE == hDevice ||
NULL == hDevice)
{
printf("Open Fail\n");
}
if (INVALID_HANDLE_VALUE != hDevice &&
NULL != hDevice)
{
// 触发IRP_MJ_CLEANUP, IRP_MJ_CLOSE
CloseHandle(hDevice);
hDevice = NULL;
}
system("pause");
return 0;
}
加载NT驱动后,运行chapter05-win32.exe我们可以使用DebugView观察到如下结果:
DriverEntry运行在System进程中,HelloNTDriverDispatchRoutine当处理IRP_MJ_CREATE和IRP_MJ_CLOSE时运行在chapter05-win32.exe进程中。
分页和非分页内存
Winows规定有些虚拟内存页面是可以交换到文件中的,这类内存被称为分页内存。而有些虚拟内存永远不会被交换到文件中,这些内存被称为非分页内存。当程序的中断请求级在DISPAT_LEVEL之上(包括DISPATCH_LEVEL之上)时,程序只能使用非分页内存,否则将导致蓝屏。
后话
本文完整工程和代码托管在GitHub上猛戳我。
其他章节链接
VS2013 WDK8.1驱动开发3(手动加载NT驱动程序)
VS2013 WDK8.1驱动开发6(内存管理)
还没有人评论...