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驱动开发1(最简单的NT驱动)

VS2013 WDK8.1驱动开发2(最简单的WDM驱动)

VS2013 WDK8.1驱动开发3(手动加载NT驱动程序)

VS2013 WDK8.1驱动开发4(NT式驱动基本结构)

VS2013 WDK8.1驱动开发5(WDM驱动基本结构)

VS2013 WDK8.1驱动开发6(内存管理)

VS2013 WDK8.1驱动开发7(派遣函数)

VS2013 WDK8.1驱动开发8(设备读写操作)

VS2013 WDK8.1驱动开发9(IO设备控制操作)


赞助作者写出更好文章


分享给朋友阅读吧


您还未登录,登录微博账号发表精彩评论

 微博登录


最新评论

    还没有人评论...

 

 

刘杰

28岁, 现居苏州

微博:

微信:

BurnellLIU

邮箱:

burnell_liu@outlook.com

Github:

https://github.com/BurnellLiu

简介:

努力做一个快乐的程序员, good good study, day day up!

友情链接: Will Mao

苏ICP备16059872号. Copyright © 2017. http://www.coderjie.com. All rights reserved.