内存管理概念:

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

);

Windows内核 内存管理基本概念的更多相关文章

  1. windows内核 内存管理

    一.几个基本的概念 1.存储器的金字塔结构 存储器从下之上依次是磁盘/flash.DRAM(内存).L2-cache.L1-cache.寄存器,越在上面的存储器访问速度越快,同时价格也越昂贵,每一级都 ...

  2. linux内核--内核内存管理

    如题目所示,为什么要称作“内核内存管理”,因为内核所需要的内存和用户态所需要的内存,这两者在管理上是不一样的. 这篇文章描述内核的内存管理,用户态的内存管理在以后的文章中讲述. 首先简单的说明一下下面 ...

  3. Linux内核内存管理架构

    内存管理子系统可能是linux内核中最为复杂的一个子系统,其支持的功能需求众多,如页面映射.页面分配.页面回收.页面交换.冷热页面.紧急页面.页面碎片管理.页面缓存.页面统计等,而且对性能也有很高的要 ...

  4. Linux内核内存管理算法Buddy和Slab: /proc/meminfo、/proc/buddyinfo、/proc/slabinfo

    slabtop cat /proc/slabinfo # name <active_objs> <num_objs> <objsize> <objpersla ...

  5. Linux内存管理基本概念

    1. 基本概念 1.1 地址 (1)逻辑地址:指由程序产生的与段相关的偏移地址部分.在C语言指针中,读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址.( ...

  6. (笔记)Linux内核学习(九)之内核内存管理方式

    一 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理 地址,通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 32位系统:页大小4KB 64位系统:页 ...

  7. Linux内核学习笔记——内核内存管理方式

    一 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理 地址,通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 32位系统:页大小4KB 64位系统:页 ...

  8. windows内核对象管理学习笔记

    目前正在阅读毛老师的<windows内核情景分析>一书对象管理章节,作此笔记. Win内核中是使用对象概念来描述管理内核中使用到的数据结构.此对象(Object)均是由对象头(Object ...

  9. linux内核 内存管理

    以下内容汇总自网络. 在早期的计算机中,程序是直接运行在物理内存上的.换句话说,就是程序在运行的过程中访问的都是物理地址. 如果这个系统只运行一个程序,那么只要这个程序所需的内存不要超过该机器的物理内 ...

随机推荐

  1. hdu 并查集分类(待续)

    hdu 1829 A Bug's Life 题目大意: 给你n个动物,输入m行a,b,表示a和b应该是异性的,要你判断是否有同性恋. 并查集中,1到n代表应性别,n+1到2n代表一个性别,合并一下,判 ...

  2. Angular JS 学习之过滤器

    1.过滤器可以使用一个管道字符(|)添加到表达式和指令中: 2.AngularJS过滤器可用于转换数据: **currency:格式化数字为货币格式: **filter:从数组项中选择一个子集: ** ...

  3. 解决eclipse报PermGen space内存溢出异常的问题

    异常问题如下所示: 1.点击Eclipse->Window->Preferences,如下所示: 2.点击Server->Runtime Environments,选择Apache ...

  4. 利用Hive实现求两条相邻数据时间差

    1.Hive row_number() 函数的高级用法 row_num 按照某个字段分区显示第几条数据 select imei,ts,fuel_instant,gps_longitude,gps_la ...

  5. Java类加载

    类的生命周期是: 在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM.在程序执行中JVM通过装载,链接,初始化这3个步骤完成. 类的装载是通过类加载器完成的,加载器将.c ...

  6. C#解决从含身份证号码的Excel表格导入数据库的问题

    用C#做从Excel表导入SQL数据库时发现从EXCEL导入的身份证号码会变成科学表示方法. 解决这个问题是比较容易的,首先,打开电子表格,选中“身份证号码”一列,右键选择“设置单元格格式”,进入单元 ...

  7. HD1394 Minimum Inversion Number

    这道题目的意思是:给你一个序列,统计一开始的逆序数的个数,然后依次把第一个元素放到序列末尾,求每次的逆序数个数,求出每次求逆序数里,逆序数最小的那个数 这里需要推一个递推式,就是每次你把第一个元素放到 ...

  8. ural 1147. Shaping Regions

    1147. Shaping Regions Time limit: 0.5 secondMemory limit: 64 MB N opaque rectangles (1 ≤ N ≤ 1000) o ...

  9. UIImage两种初始化的区别

    UIImage可以通过以下两种方式进行初始化: //第一种初始化方式:[注意使用这种初始化的时候如果是png格式的可以不给后缀名,根据屏幕的的分辨率去匹配图片] UIImage *image = [U ...

  10. BZOJ3659 : Which Dreamed It

    首先判断一下是否无解,并剔除孤立点. 根据best theorem,有向图中以$i$为起点的欧拉回路个数为: 以$i$为根的树形图个数$\times\prod_{i=1}^n (deg(i)-1)!$ ...