zoukankan      html  css  js  c++  java
  • Windows内核 内存管理基本概念

    内存管理概念:

    1)物理内存

    PC上有三条总线:数据总线、地址总线和控制总线。32位CPU的寻址能力是4GB个字节,用户最多可以使用4GB的真实物理内存。PC中很多设备都提供了自己的设备内存,例如显卡就提供了自己的显存。这部分内存会映射到PC的物理内存上,也就是读写这段物理地址,其实会读写的设备内存地址,而不会读写物理内存地址。

    2)虚拟内存地址

    Windows所有程序(包括Ring0层和Ring3层的程序)可以操作的都是虚拟内存。之所以称为虚拟内存,是因为对它的所有操作最终都会变成一系列对真实物理内存的操作。

    CPU中有一个重要的寄存器CR0,它是32位的寄存器,其中的一个位(PG位)是负责告诉系统是否分页的。Windows在启动前会将它的PG位设为1,即允许分页。DDK中有个宏PAGE_SIZE记录着分页大小,一般为4KB。4GB的虚拟内存会被分割成1M个分页单元。

    其中,有一部分单元会和物理内存对应起来,即虚拟内存中第N个分页单元对应着物理内存第M个分页单元,这是一种多对一的映射,多个虚拟内存页可以映射到同一个物理内存页。还有一部分单元会被映射成磁盘上的文件,并标记为脏的(Dirty)。当读取这段虚拟内存时,系统会发出一个异常,此时会触发异常处理函数,异常处理函数会将这个页的磁盘文件读入内存,并标记为不脏。

    Windows使用虚拟内存是基于以下原因:

    1、虚拟的增加了内存的大小;

    2、使不同进程的虚拟内存互不干扰,为了让系统可以同时运行不同的进程,Windows让每个进程看到的虚拟内存都不相同。

    3)用户模式地址和内核模式地址

    虚拟地址0~0x7FFFFFFF范围内的虚拟内存,即低2GB的虚拟地址被称为用户模式地址;而0x80000000~0xFFFFFFFF范围内的虚拟内存,即高2GB的虚拟内存被称为内核模式地址。Windows规定运行在用户态(Ring3层)的程序只能访问用户模式地址,而运行在核心态(Ring0层)的程序可以访问整个4GB的虚拟内存。

    Windows的核心代码和Windows的驱动程序加载的位置都在高2GB的内核地址里。Windows在进程切换时,保持内核模式地址是完全相同的,只改变用户模式地址的映射。

    4)Windows驱动程序和进程的关系

    驱动程序可以看成是一个特殊的DLL文件被应用程序加载到虚拟内存中,只不过加载地址是内核模式地址而不是用户模式地址。Windows驱动程序里的不同例程运行在不同的进程中,DriverEntry例程和AddDevice例程是运行在系统进程System中的。System进程是Windows第一个运行的进程。当需要加载驱动程序时,System进程中会有一个线程将驱动程序加载到内核模式地址空间中,并调用DriverEntry例程。而其他一些例程,如IRP_MJ_READ的派遣函数会运行于应用程序的上下文中。所谓运行在进程的上下文,指的是运行于某个进程的环境中,所能访问的虚拟地址是这个进程的虚拟地址。

    下面的函数可以显示出当前进程的进程名:

    VOID DisplayProcessName()

    {

             //得到当前进程

             PEPROCESS process = PsGetCurrentProcess();

             //得到当前进程名称

             PTSTR ProcessName = (PTSTR)((ULONG)process + 0x174);

             KdPrint(("%s/n", processName));

    }

    5)分页与非分页内存

    Windows规定有些虚拟内存页面可以交换到文件中,这类内存被称为分页内存;而有些虚拟内存永远不会交换到文件中,这些内存被称为非分页内存。

    当程序的中断请求级在DISPATCH_LEVEL之上时(包括DISPATCH_LEVEL层),程序只能使用非分页内存,否则将导致蓝屏死机。在编译DDK提供的例程时,可以指定某个例程和某个全局变量是载入分页内存还是非分页内存:

    #define PAGEDCODE code_seg("PAGE")

    #define LOCKEDCODE code_seg()

    #define INITCODE code_seg("INIT")

    #define PAGEDDATA data_seg("PAGE")

    #define LOCKEDCODE code_seg()

    #define INITDATA data_seg("INIT")

    如果将某个函数载入到分页内存,我们这样使用:

    #pragma PAGEDCODE

    VOID ASCEFunction()

    {

             PAGED_CODE();

             //do something

    }

    其中,PAGED_CODE()是DDK提供的宏,只在Checked版本中生效。它检验这个函数是否运行低于DISPATCH_LEVEL的中断请求级,如果等于或高于这个中断请求级,将产生一个断言。

    如果让函数加载到非分页内存中,我们这样使用:

    #pragma LOCKEDCODE

    VOID ASCEFunction()

    {

             //do something

    }

    如果某个例程需要在初始化时载入内存,然后就可以从内存中卸载掉(这种情况指出现在DriverEntry情况下,尤其是NT式驱动,DriverEntry会很长,占据很大的空间,为了节省内存,需要及时地从内存中卸载掉),我们这样使用:

    #pragma INITCODE

    extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,

                                                                                                                    IN PUNICODE_STRING RegistryPath)

    {

             //do something

    }

    6)分配内核内存

    Windows驱动程序使用的内存资源非常珍贵,分配内存时要尽量节约。和应用程序一样,局部变量存放在栈空间中,但栈空间不像应用程序那么大,所以驱动程序不适合递归调用或这局部变量是大型结构体。若需要大型结构体,应该在堆上申请。

    堆中申请内存的函数如下:

    PVOID ExAllocatePoolWithTag(

      __in  POOL_TYPE PoolType,

      __in  SIZE_T NumberOfBytes,

      __in  ULONG Tag

    );

    PVOID ExAllocatePoolWithQuotaTag(

      __in  POOL_TYPE PoolType,

      __in  SIZE_T NumberOfBytes,

      __in  ULONG Tag

    );

    其中,PoolType是个枚举变量:

    NonPagedPool       //指定要求分配非分页内存

    PagedPool      //指定要求分配分页内存

    NonPagedPoolMustSucceed        //指定分配非分页内存,必须成功

    DontUseThisType //未指定

    NonPagedPoolCacheAligned       //指定要求分配非分页内存,而且必须内存对齐

    PagedPoolCacheAligned      //指定分配分页内存,且必须内存对齐

    NonPagedPoolCacheAlignedMustS    //指定分配非分页内存,且必须内存对齐,且必须成功

    NumberOfBytes是分配内存的大小,最好是4的倍数;

    返回值分配的内存地址,一定是内核模式地址;如果返回0,表示分配失败。

    函数以WithQuota结尾代表分配时按配额分配;以WithTag结尾的,多出的Tag参数,用在调试时,可以找出是否有标有这个Tag的内存没有被释放。

    将分配的内存进行回收的函数是ExFreePool和ExFreePoolWithTag:

    VOID ExFreePool(

      __in  PVOID P

    );

    VOID ExFreePoolWithTag(

      __in  PVOID P,

      __in  ULONG Tag

    );

  • 相关阅读:
    记一次VS2010和VS2015自定义颜色的过程
    Git使用笔记
    VS winsock.h和ws2def.h大量重定义报错的问题
    在ASP.NET MVC 3中使用日志记录组件Elmah和NLog
    Entity Framework 6新特性:全局性地自定义Code First约定
    在ASP.NET MVC 3 中自定义AuthorizeAttribute时需要注意的页面缓存问题
    SSH开发记录
    iOS开发知识要点
    (收藏)在 iPhone/iPad 中随意修改数字键盘按钮
    iPhone开发 – 数据持久化
  • 原文地址:https://www.cnblogs.com/alsofly/p/3624009.html
Copyright © 2011-2022 走看看