windows虚拟内存机制
在windows系统中,每个进程拥有自己独立的虚拟地址空间(Virtual Address Space)。这一地址空间的大小与计算机硬件、操作系统以及应用程序都有关系。
对于32位程序来说,最多能使用2GB空间(0x00010000-0x7FFEFFFF)。为了获得3GB的地址空间,在不同的windows系统中可以按照如下方法来进行扩充。
1.操作系统方面
① 32位windowsXP
② 32位win7 -- 管理员权限执行命令:bcdedit /set increaseuserva 3072来开启
③ 64位win7 -- 对32位程序默认开启3GB,无需额外设置
2.应用程序方面
无论是32位还是64位windows若要让32位程序能使用3GB内存,必须在链接时加上参数: /LARGEADDRESSAWARE
进程地址空间区段
注:进程地址空间在低地址,操作系统内核在高地址
进程地址空间分布
以2GB为例
Windows系统在进程空间中专门划出一块0x70000000 - 0x80000000(共256MB)区域,用于映射这些常用的系统DLL(如kernel32.dll、ntdll.dll等)
对系统DLL的默认基地址进行调整,防止加载时冲突,触发ReBasing(重定基地址)
注:基地址必须对齐到分配粒度(64KB)
Win7下,exe在PE文件中基地址为0x400000,DllPrj.dll的基地址为0x10000000且该地址未被其他dll占用;但实际exe被映射到0xEC0000,DllPrj.dll被映射到0x535A0000
生成exe和dll模块时,链接时使用了参数/DYNAMICBASE(启用动态基地址)
注:地址空间布局随机化, Address space layout randomization (ASLR):防范恶意程序对已知地址进行攻击
windows内存分配过程可细化为以下3个要点:
① 保留一段虚拟内存地址空间:从进程的4GB中保留一段地址空间。// 带MEM_RESERVE参数的VirtualAlloc函数
起始地址必须是系统分配粒度的整数倍(64KB),大小必须是系统页面大小的整数倍(4KB)。
② 提交一段虚拟内存地址空间:将进程已保留的一段地址空间映射机器的虚拟内存上。// 带MEM_COMMIT参数的VirtualAlloc函数
起始地址和大小都必须是页面大小的整数倍(4KB)。
③ 将虚拟内存地址空间映射到物理内存页(RAM):在访问进程提交的页面被访问时,通过缺页中断(又名页缺失、页面错误, PageFault)机制来真正分配物理内存页,同时修改对应页面的地址空间映射关系。
注1:在程序中所访问的地址都必须是保留并提交的虚拟内存地址
注2:可以使用VirtualFree来释放保留或提交的虚拟内存地址空间
内存指标概念
Total = Image + Mapped File + Shareable + Heap + Managed Heap + Stack + Private Data + Unusable
Image:exe、dll等可执行模块的代码段、数据段等
Mapped File:作为数据载入的内存映射文件
Shareable:进程间共享内存、消息等
Heap:malloc()、new、HeapAlloc()、LocalAlloc()创建出来的私有内存,由用户态堆管理器统一管理
Managed Heap:由GC管理的私有内存
Stack:线程栈
Private Data:由VirtualAlloc()创建出来的私有内存
Page Table:分配在内核态的页表
Unusable:起始地址以64KB对齐,导致一些无用的空闲内存地址空间
Free:空闲内存地址空间
-------------------------
Blocks:拥有内存块的个数
Largest:所有内存块中最大一个的size
虚拟内存:
Private Bytes // 进程Committed的虚拟内存字节数 对应VMMap的Private、win7任务管理器中的【提交大小】,资源管理器中的【提交】
Peak Private Bytes // 进程Committed的虚拟内存的最高峰字节数
Virtual Size // 进程Reserved的虚拟地址空间字节数
Page Faults // 发生过的缺页中断次数 对应win7任务管理器中的【页面错误】
物理内存:
Working Set = WS Private + WS Shareable // 进程占用物理内存总字节数 对应win7任务管理器中的【工作设置(内存)】,资源管理器中的【工作集】
WS Private // 进程独享的物理内存字节数(如:堆内存+栈内存+cow机制创建的内存) 对应win7任务管理器中的【内存(专用工作集)】,资源管理器中的【专用】
WS Shareable // 进程可与其他进程共享的物理内存字节数(如:exe及dll代码段、数据段等) 对应win7资源管理器中的【可共享】
WS Shared // 进程已与其他进程共享的物理内存字节数,WS Shared<=WS Shareable
// 若只启动一个exe实例,那么exe的代码段、数据段等不会被共享,因而就不统计在WS Shared中
Peak Working Set // 物理内存的最高峰字节数 对应win7任务管理器中的【峰值工作设置(内存)】
注:无论是虚拟内存还是物理内存下的各个指标,都是通过统计用户态的那部分占用
整个系统的物理内存和虚拟内存占用:
内存计数条(Counter)的整个高度代表了当前电脑整个物理内存RAM的大小(16GB),绿条的高度代表了所有进程所消耗的物理内存总和(7.36GB)
-- 物理内存(MB)的区域 --
总数:当前电脑整个物理内存RAM的大小(16312MB)
已缓存:Windows系统将没有占用的物理内存来做缓存的部分(8786MB),这块绝大部分可被释放出来以供使用
可用:可被使用的物理内存(8771MB),当再起一个进程,可以占用的物理内存
空闲:完全空闲的物理内存(45MB)
进程消耗的物理内存(7.36GB) + 已缓存(8786MB) + 空闲(45MB) = 总数(16312MB)
-- 系统的区域 --
提交(GB):10 / 31
31GB为物理内存和分页文件的总大小
10GB为所有进程占用的Committed虚拟内存大小
页交换文件
页交换文件(Page File):一般被用作可写物理内存页的后备存储器。Windows下该文件名为pagefile.sys,位于各盘的根目录中。
可以根据机器的软硬件状况来设置页交换文件的大小,甚至关闭页交换文件的使用。
页出(Page Out):当物理内存不够时,系统会将一些不经常使用且有后备的物理内存页释放,并将虚拟地址映射关系指向后备。
①以页交换文件(如:堆、栈等)为后备:在页交换文件中分配空间,并拷贝内容到其中后再释放
②以内存映射文件(如:exe、dll等)为后备:直接释放
页入(Page In):当系统读取某个虚拟内存地址,而该地址所在的页不在物理内存页中时,将产生一个缺页中断,
告诉系统从页交换文件或者内存映射文件中取回包含该地址的虚拟内存页(即:将内容拷回到物理内存页,并建立新的虚拟地址映射到物理内存页上,然后释放页交换文件中对应部分的空间) 。
写时复制机制
写时复制机制(copy on write, COW):当WRITECOPY属性内存页面被修改时,会触发内存页拷贝,以此来节省物理内存和页交换文件的占用。
注:系统在映射exe或dll文件时会把数据页指定为PAGE_WRITECOPY属性,代码页指定为PAGE_EXECUTE_WRITECOPY属性
具体过程:
① 当进程对内存页执行修改操作时,系统会找一个闲置的物理内存页,并拷贝所有内容到新页上,然后标记新页的后备存储器为页交换文件,最后将进程的虚拟内存页指向新的物理内存页。
② 经过上述步骤,进程就可以使用自己副本了,修改在新的物理页上进行,而不对原来的内存页产生任何影响。
重定基地址
重定基地址(Rebasing):模块装载时,如果目标地址被占用或基于安全考虑,系统会根据模块的所需地址空间的大小为其分配一个新的基地址,并将模块装载到该基地址处。
问题:
① 一旦发生了Rebasing,当模块映射时,要对重定位表中所有页进行地址修正。
② 系统修正这些地址的页面时,会触发写时复制机制。
地址空间布局随机化
微软在Vista系统中引入了名为地址空间布局随机化(Address space layout randomization,ASLR)的技术,模块每次会被加载到随机位置(伪随机),防范恶意程序对已知地址进行攻击。
ASLR不仅对模块地址做了随机处理,还对堆、栈、进程环境块(Process Environment Block, PEB)、线程环境块(Thread Environment Block, TEB)的地址也进行了随机化。
ASLR技术将Rebasing放到内核中进行处理,意味着可以在系统范围上(原来只能在进程范围内),最大程度上减少Rebasing的发生,从而节省物理内存和页交换文件的使用。
PE文件装载
注:映射必须以页面(4KB)为单位,并按照页边界进行对齐
执行完映射后,绝大部分指令和数据都还没有被装入物理内存中。装载过程是随着程序的执行动态进行的。
具体过程:cpu在访问指令和数据时,发现该地址所在的页不在物理内存页中时,会触发缺页中断,此时系统会找一个闲置的物理内存页,并将内容从后备中(映像文件或页交换文件中)载入到该物理内存页中。