内存管理1 - Win32汇编语言054
内存管理1
让编程改变世界
Change the world by program
内存管理基础
Win32 中的内存管理是分层次的,系统提供了几组层次不同的函数来管理内存,它们是标准内存管理函数、堆管理函数、虚拟内存管理函数和内存映射文件函数。 所有的这些函数都是为了让用户能在比较高的层次上方便地管理内存,以便将程序和底层的内存分页机制隔离开来。 图说内存: [caption id="attachment_521" align="aligncenter" width="300"] Windows的内存分层管理[/caption]虚拟内存管理函数
Windows 使用一个以页为基础的虚拟内存系统,与分页有关的概念已经在该系列教程刚开始几个讲中有所介绍,虽然那时候大家还懵懵懂懂。 Windows 充分利用了 80x86 处理器保护模式下的线性寻址机制和分页机制,这些机制是 Win32 内存管理的基础。 Win32 提供了一组虚拟内存管理函数来管理虚拟内存,主要用于保留/提交/释放虚拟内存,在虚拟内存页上改变保护方式,锁定虚拟内存页以及查询一个进程的虚拟内存等操作,这是一组位于底层的函数。堆管理函数
堆管理函数相对比较高级一点,堆的主要功能就是有效地管理内存和进程的地址空间。 DOS 操作系统下的 C 语言中就已经有了”堆”的概念,这时的”堆”是程序初始化时向操作系统申请并预留的大块内存,程序通过 C 函数在这块空间中申请和释放内存。 在Win32中,进程可以使用的整个地址空间就是一个堆。标准内存管理函数
并且”堆”的概念又被引伸了一步:Win32中分两种堆 一种是进程的”默认堆”,默认堆只有一个,指的就是可以使用的整个地址空间; 另一种是”动态堆”,也称为”私有堆”,私有堆类似于 DOS 下 C 语言中使用的那种堆,一个进程可以随意建立多个私有堆,也可以随意将它们释放。 私有堆全部位于默认堆中,从概念上看,它和默认堆并没有什么不同,使用堆管理函数可以对所有的私有堆和默认堆进行操作。 标准内存管理函数总是在默认堆中分配和释放内存,这组函数就是常规意义上的内存管理函数。内存映射文件函数
内存映射文件函数相对比较独立,它是为了文件操作的方便性而设立的。 当对文件进行操作的时候,一般先打开文件,申请一块内存用做缓冲区,再将文件数据循环读入并处理,当文件长度大于缓冲区长度则需要多次 读入,每次读入后处理缓冲区边界位置的数据往往是个麻烦问题。 还记得我们曾经介绍过 Windows 可以使用磁盘文件当做虚拟内存,内存映射文件函数使用同样的办法将一个文件直接映射到进程的地址空间中,这样可以通过内存指针用读写内存的办法直接存取文件内容。不同内存管理函数的操作对象
对比这些函数,可以发现它们涉及的系统资源是各不相同的: [caption id="attachment_522" align="aligncenter" width="300"] 不同内存管理函数的操作对象[/caption]获取内存的当前状态
为什么需要此操作?! 因为我们之前介绍过,一个进程可以寻址的地址空间是 4GB,但用户可以直接管理的地址空间是多大呢? 实际上,高端的 2GB 是供操作系统内核使用的,其中安排了操作系统的代码和数据,可供应用程序使用的地址空间是低端的 2GB。 这 2GB 除去应用程序与用户 DLL 等的代码和静态数据段以后,余下来的才是内存管理函数可以使用的地址空间。 应用程序和用户 DLL 的大小一般只有几兆字节到上百兆字节,所以可以认为能自由使用的地址空间基本上是 2GB。 既然用户可以使用的地址空间大概为 2GB,鱼油们千万不要天真的认为就可以申请 2GB 的内存了。 因为这 2GB 仅是可以使用的”地址”空间,而不是可以使用的”内存”空间(请务必谨慎区分),可分配内存的大小还受制于物理内存和磁盘交换文件的大小。 因为物理内存和磁盘交换文件是供整个系统和所有用户程序使用的,所有系统内核、当前执行的所有用户程序的代码、数据以及分配的内存总量并不能超过物理内存和磁盘交换文件的总和。 当设计一个可能需要申请大量内存的程序时,如何预先得知系统的配置情况呢? 对此可以使用 GlobalMemoryStatus 函数:invoke GlobalMemoryStatus, lpBuffer
lpBuffer 指向一个 MEMORYSTATUS 结构,结构的定义如下: [codesyntax lang="asm"]MEMORYSTATUS STRUCT dwLength DWORD ; 本结构的长度 dwMemoryLoad DWORD ; 已用内存的百分比 dwTotalPhys DWORD ; 物理内存总量 dwAvailPhys DWORD ; 可用物理内存 dwTotalPageFile DWORD ; 交换文件总的大小 dwAvailPageFile DWORD ; 交换文件中空闲部分大小 dwTotalVirtual DWORD ; 用户可用的地址空间 dwAvailVirtual DWORD ; 当前空闲的地址空间 MEMORYSTATUS ENDS[/codesyntax] 在调用之前需要首先将 dwLength 字段设置为 MEMORYSTATUS 结构的长度。 当调用 GlobalMemoryStatus 函数后,函数会在结构中填充对应的数值。 注意:dwTotalPageFile 字段返回的是交换文件的最大值,并不是当前实际建立的交换文件的大小,一般当前的交换文件大小会小于这个数值。 但这个数值的大小也不是确定的,如果需要的话,系统会增加它的大小直到不再有空余的磁盘空间放置交换文件为止。 我们来分析一个不完美的栗子:MemInfo(课件及源代码下载) 关于 GlobalMemoryStatus, MSDN 上有这样一段话:
On computers with more than 4 GB of memory, the MEMORYSTATUS structure can return incorrect information. Windows?2000 reports a value of -1 to indicate an overflow. Earlier versions of Windows NT report a value that is the real amount of memory, modulo 4 GB. If your application is at risk for this behavior, use the GlobalMemoryStatusEx function instead of the GlobalMemoryStatus function.先把视频暂停片刻。。。 自己试着改一下。。。。 小甲鱼接着会讲解支持 4GB 以上的解决方案,但希望大家先自己根据提示进行探索! 支持 4GB 以上资源获取解析:MemInfo2(课件及源代码下载) [buy] 获得所有教学视频、课件、源代码等资源打包 [/buy] [Downlink href='http://kuai.xunlei.com/d/BdsUAwrPHwBnT7JSe4c']视频下载[/Downlink]