本文是我在学习完《Windows核心编程》一书”内存管理“相关的几个章节后,结合网络资料(主要是微软的文档),经过思考提炼后,阐述我对”Windows内存体系结构“的认识,并与大家分享。可能有不正确的地方,我会不断学习理解,并更正错误。
一、参考资料(除《Windows核心编程》外):
1,RAM,virtual memory, pagefile, and memory management in Windows https://support.microsoft.com/en-us/kb/2160852
来自Mircrosoft的官方文档,内容如题。
2,How to use Performance Monitor
配合上面文章的一个工具软件,win7中叫”Performance Monitor“,win10中改为”Perfmon.exe“。
3,CPU cache
二、概念讲解
1,RAM - Random Access Memory,随机访问存储器。
它就是俗称的内存(main memory),对应PC上的内存条。
2,pagefile - 换页文件。
它实际上是系统为弥补RAM的不足,在Disk(磁盘)上划定一个文件,充当内存用。该文件名叫”pagefile.sys“,一般存在磁盘C盘(磁盘)的root目录。可以通过”我的电脑 -> 属性 -> 高级系统设置 -> 性能 (设置) -> 高级 -> 虚拟内存 (更改)“ 的方法修改pagefile的大小。
由上图可知,”分页文件是硬盘上的一块区域,Windows当作RAM使用“。
此外,可以看到它属于”虚拟内存“设置项。事实上,”虚拟内存“是Windows的一项内存管理机制,它的核心思想就是用pagefile充当RAM用。
三、物理存储器
一般地,计算机的物理存储介质按存取速度分级为:Tape < Disk < RAM < Cache < CUP register。普通文件和pagefile都在Disk上。下图是Memory、Cache和CPU的关系图。
关于Cache存储器,有两个概念:行(line)和块(block)。CPU一个时钟周期读取Cache中的一行,因此内存对齐很重要。而Cache与Main Memory的数据交换是以块为基本单位。因此,拷贝一个数组的时候,memcpy比使用循环去逐个赋值效率要高,同样的,在驱动编程中,对连续物理内存的读写,用”WRITE_REGISTER_BUFFER_ULONG“,比在循环中使用”WRITE_REGISTER_ULONG“效率高很多。
四、虚拟内存基础知识
还是用一张图来演示,操作系统通过MMU(Memory Management Unit)将虚拟地址翻译到实际物理地址,具体涉及到分页内存管理和页表。如下:
五、进程地址空间分区
注:从数学角度来看,X86的32位架构,可以表示2进制数总数为:2 ^ 32 = (2 ^ 2) * (2 ^ 10) * (2 ^ 10),即4G。再看低地址2G空间中的两个小的分区,都是64KB,实际上 64KB = 64 * 1024 = (2 ^ 6) * (2 ^ 10) B= 2 ^ 16 B= 65536 B。它刚好是两个字节(16位)能表示的最大大小,它是基本的分配粒度(allocation granularity,64KB)。同样的,page size,4KB = (2 ^ 2) * (2 ^ 10) B
六、内存区域
Memory Region of Virtual Address Space
region - 虚拟地址空间的区域。它是指在虚拟地址空间上通过VirtualAlloc函数预订一块连续的地址段,它的基地址要是分配粒度(allocation granularity,64KB)的整数倍,它的大小要是page size(4KB)的整数倍。region中不同属性的页面要组成block。
分配区域的操作,称为预订(reserving),预订完后,还需要为虚拟地址调拨(committing)真实的物理存储器。预订和调拨都调用的VirtualAlloc函数,但传递的参数不同;撤销调拨调用那个VirtualFree函数。
内存区域分为四种类型:闲置(Free)、私有(Private)、映像(Image)和已映射(Mapped)。
注意:
1)表格提到的都是后备存储器,实际运行时,还是需要将数据换入到RAM中。该处涉及到”page-in and page-out,换入换出“,后面讲详细解释。
2)映像文件专指.exe或.dll文件,它由系统执行物理地址到虚拟地址的映射;内存映射文件是由用户调用CreateFileMapping、MapViewOfFile等函数将任意指定文件映射到虚拟地址空间。两者有极大的类似点。
3)与内存区域相关的一个最重要的函数是VirtualQuery()(或VirtualQueryEx)。
4)memory region的页面还可以设置”ERWC“四种属性,用于Windows的数据执行保护(Data Execution Protection)。其中,E - Execute,代码执行;R - Read, W - Write ; C - Copy,写时复制,主要用于统一执行文件(.exe)的不同进程之间的数据共享。
七、换入/换出 page-in and page-out
page-in and page-out主要是指数据以page为单位在RAM和pagefile之间的转移。
上图所说的内存,应该就是指的RAM。上图中的Free Page是指RAM上的闲置页面,不是虚拟地址空间的Free页面,需要注意区分。事实上,在上图CPU结束”访问数据“后,载入该页数据的RAM页面,有可能被换出到pagefile。系统运行过程中,就是一直在执行这样的换入换出工作。
值得注意的是,换入操作也可以从Image 文件或内存映射文件,直接载入RAM页。上图为简单起见,没有专门描述这一块的内容。
八、WorKing set工作集
Windows是一个多任务操作系统,多个进程共享有限的RAM和Pagefile,但每个进程都有自己独立的虚拟地址空间,系统通过上面的换入换出机制来保障有限的物理资源平衡地分配给各个进程使用。通过调用”GlobalMemoryStatus“函数和”GetProcessMemoryInfo“函数,可以分别查看整个系统的内存状态和当前调用进程的内存状态。如下图,用横线分割的两部分:
如上图:Memory load表示内存使用率,和任务管理器显示的一致,它是”AvailPhs / TotalPhys“。但是,上图显示的TotalPhys不正确,因为我实验的电脑是64位的,物理内存也是4G,超过了32位机的上限,故上图中显示的实际是上限值——2G。
TotalPhys和AvailPhys表示的是RAM,而TotalPageFile和AvailPageFile表示的是Pagefile相关信息。这四项都是相对整个系统而言。而TotalVirtual表示的总的虚拟内存,它刚好是2GB - 2 * 64KB,参照内存分区表,总虚拟内存对每个进程来说是一样的。AvailVirtual表示当前进程可用虚拟内存。
Working Set(工作集)表示某进程某时刻实际所占RAM的大小。上图中显示的是当前调用进程的工作集,TotalVirtual - AvailVirtual >> Working Set,即当前进程所占虚拟内存远大于它的工作集。Windows操作系统正是应用这种机制才能用有限的RAM供给多进程对内存资源的极大需求。
PrivateBytes表示当前进程的应用程序中调用new, malloc或VirtualAlloc函数,显示地分配的内存大小。