按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
CAPTION 〃内存状态〃
FONT 9; 〃宋体〃
{
LTEXT 〃〃; IDC_INFO; 6; 6; 135; 65
}
//》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
程序设置了一个定时器,以周期为1秒来刷新当前内存的使用情况,在WM_TIMER消息中用GlobalMemoryStatus获取内存状态并用wsprintf将数据转换成字符串,然后显示在对话框的IDC_INFO文本框中。
在笔者的计算机中,程序运行的结果如图10。2所示,计算机的物理内存配置为128 MB,这个数值和物理内存总数(dwTotalPhys字段)符合,dwMemoryLoad字段显示的72%等于空闲物理内存(dwAvailPhys字段)除以物理内存总数,计算机上当前虚拟内存交换文件大小为192 MB,小于最大限制dwTotalPageFile字段。
在与地址空间相关的数值上,dwTotalVirtual字段的显示结果是2 147 352 576,等于2 GB减去128 KB,这是因为这2 GB的最低端的和最高端的两个64 KB是系统保留的(00000000h~0000ffffh,7fff0000h~7fffffffh)。
图10。2 Meminfo程序的运行结果
Windows可以根据内存使用的需求自动调整交换文件的大小,比如Meminfo程序运行显示的当前磁盘交换文件可用空间(dwAvailPageFile字段)为168 MB,但是如果尝试申请一个高达300 MB的内存块,会发现仍然可以申请成功,这时dwTotalPageFile字段的大小会自动增长到500多MB,把内存块释放掉的话,dwTotalPageFile字段会恢复到原来的数值。虚拟内存的使用给我们带来了很多的方便,我们可以使用超过物理内存好几倍的内存空间,但是如果所需的内存大大高于物理内存的大小,那么申请内存还是会失败,因为这会引起物理内存和交换文件之间的数据频繁交换,大量的磁盘请求将使系统性能降低到没有实际使用的意义,读者可以尝试在128 MB物理内存的计算机上申请一个1 GB的内存块,即使拥有远远大于1 GB的磁盘剩余空间供交换文件使用,也是不会成功的!如果读者需要使用大大高于物理内存的内存空间,可以尝试自己进行磁盘交换工作。
10。1。3 标准内存管理函数
标准内存管理函数的功能是在进程的默认堆中申请和释放内存块,它由下面一些函数组成:GlobalAlloc,GlobalFree和GlobalReAlloc分别用来申请、释放和修改内存大小;GlobalLock和GlobalUnlock用来进行锁定操作;而GlobalDiscard,GlobalFlags,GlobalHandle和GlobalSize等用来丢弃内存或获取已分配内存的一些信息。
在Win16中,内存管理函数有“全局”或“本地”之分,它们的区别在于返回的指针是远指针还是近指针,全局内存管理函数名是以“Global”开头的,而“本地”内存管理函数名是以“Local”开头的。在Win32中,指针并没有远近之分,只有一种32位的指针,但为了保持向下兼容,这些函数名仍然沿用了下来,上面列出的这些函数名都是以“Global”开头的,同样,Win32中也存在以“Local”开头的内存管理函数名,只要这些函数名中的“Global”全部换成“Local”就可以了。这两组函数在Win32中是完全相同的,读者可以自由使用名字以Global或Local为前缀的函数。
用标准内存管理函数可以分配的内存有两种:固定地址的内存块和可移动的内存块,而可移动的内存块又可以进一步定义为可丢弃的,让我们逐步来讨论它们的不同。
1。 固定的内存块
常规意义上的内存就是固定的内存块,因为申请到内存后,这块内存的线性地址是固定不变的。要申请一块固定的内存,可以使用函数:
invoke GlobalAlloc,GMEM_FIXED or GMEM_ZEROINIT,dwBytes
。if eax
mov lpMemory,eax
。endif
第一个参数是标志,GMEM_FIXED表示申请的是固定的内存块,GMEM_ZEROINIT表示需要将内存块中的所有字节预先初始化为0,也可以简单地使用GPTR标志,它就相当于是GMEM_FIXED or GMEM_ZEROINIT;第2个参数dwBytes指出了需要申请的是以字节为单位的内存大小。如果内存申请失败,eax中返回NULL,否则返回值是一个指向内存块起始地址的指针,用户需要保存这个指针,在使用内存或者释放内存的时候还要用到它。
如果要释放一个先前申请的固定内存块,可以使用GlobalFree函数:
invoke GlobalFree,lpMemory
如果释放成功,函数返回NULL,否则函数返回的值就是输入的lpMemory。程序在不再使用内存块的时候应该使用这个函数将内存释放,即使程序在退出的时候忘记了释放内存,Windows也会自动将它们释放。
在实际使用中往往需要改变一个内存块的大小,这时候就要用到GlobalReAlloc函数,这个函数可以缩小或者扩大一块已经申请到的内存:
invoke GlobalReAlloc,lpMemory,dwBytes,uFlags
。if e ax
mov lpNewMemory,eax
。endif
lpMemory是先前申请的内存块指针,dwBytes是新的大小,如果这个数值比原来申请的时候要小,也就是需要缩小内存块,那么uFlags标志参数可以是NULL,如果缩小内存块的操作不成功,那么函数的返回值为0,否则是新的缩小了的内存块指针,当然,这个指针和原来的指针肯定是一样的。
但是需要扩大一个内存块的时候,情况就稍微有些复杂了。让我们做一个实验来模拟这样一种情况:首先申请两个1000h大小的固定内存块,得到两个指针,读者可以发现第二块几乎紧接第一块内存,一般情况下如果第一块内存的地址是X,那么第二块内存的地址几乎就是X+1000h,如果需要将第一个内存块扩大到2000h字节,那么只能在别的地方开辟一个2000h大小的内存块,因为原来位置后面的1000h已经被第二块内存占用了,这就意味着新的指针可能和原来的不一样。
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第10章 内存管理和文件操作
10。1 内 存 管 理(3)
可以在GlobalReAlloc函数中通过指定不同的uFlags来规定是否允许Windows在必要的时候移动内存块。当uFlags中有GMEM_MOVEABLE选项的时候,如果需要移动内存块,Windows会在别的地方开辟一块新的内存,并把原来内存块中的内容自动复制到新的内存块中,这时函数的返回值是新的指针,原来的指针作废。
如果不指定GMEM_MOVEABLE选项,那么只有当内存块后面扩展所需的空间没有被使用时,函数才会执行成功,否则,函数失败并返回NULL,这时原来的指针继续有效。
为了保证内存块扩大成功,建议总是使用下面的语句来扩大和缩小内存:
invoke GlobalReAlloc,lpMemory,dwBytes,GMEM_ZEROINIT or GMEM_MOVEABLE
。if eax
mov lpMemory,eax
。endif
指定GMEM_ZEROINIT选项可以使内存块扩大的部分自动被初始化为0,然后程序判断返回值,如果改变大小成功的话,则用新的指针替换原来的指针,其他和原来指针有关的值也不要忘了同时更新。
2。 可移动的内存块
可移动的内存块在不使用的时候允许Windows改变它的线性地址,为什么要使用可移动的内存块呢?惟一的理由是防止内存的碎片化,这里的碎片化指的是用户程序自己地址空间的碎片化,而不是指整个操作系统。读者可能有个疑问:与DOS操作系统相比,Win32用户程序可用的地址空间要大得多,整整2 GB的地址空间难道还怕用完吗?让我们先用一个例子来演示一下,并由此引伸出可移动内存块的使用方法。
在这个例子中,让我们来设计一个“阴谋”,用一个极端的方法“谋杀”掉所有的地址空间:程序首先申请一个1 MB大小的固定内存块,然后继续申请内存并把前面申请的内存块大小改为100 B,由此循环,因为缩小内存块释放出来的空间大小为999 900 B,新申请的内存块无法使用这些地址空间,只能继续使用后面大块的地址空间,如果没有算错的话,经过2 000次左右的循环就会把全部的地址空间分割成2 000个999 900 B大小的空间(2GB等于2 000个1 MB),到时候虽然只保留了近200 KB大小的内存(2 000个100 B),但是这2 000个100 B均匀分布在2 GB的地址空间内,以至于接下来任何大于999 900 B的内存申请操作都无法成功。
这个程序位于所附光盘的Chapter10Fragment目录内,Fragment。asm的源代码如下:
。386
。model flat; stdcall
option casemap :none
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
; Include 文件定义
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
include windows。inc
include user32。inc
includelib user32。lib
include kernel32。inc
includelib kernel32。lib
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
; Equ 等值定义
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
ICO_MAIN equ 1000
DLG_MAIN equ 100
IDC_MEMORY equ 101
IDC_COUNT equ 102
IDC_INFO equ 103
;》》》》》》》》》》》》》》