WIN32汇编语言教程:第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的内存申请操作都无法成功。
这个程序位于所附光盘的Chapter10\Fragment目录内,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 ;#################################################################### ; 数据段 ;#################################################################### .data? hInstance dd ? hWinMain dd ? dwTotalMemory dd ? dwCount dd ? ifCanQuit dd ? .const szInfo db '无法继续申请 1MB 大小的内存!',0 ;#################################################################### ; 代码段 ;#################################################################### .code ;#################################################################### _ProcThread proc uses ebx ecx edx esi edi,lParam local @lpLastMem invoke GlobalAlloc,GPTR,1000000 mov @lpLastMem,eax inc dwCount add dwTotalMemory,1000000 .repeat push @lpLastMem invoke GlobalAlloc,GPTR,1000000 mov @lpLastMem,eax .if eax add dwTotalMemory,1000000 inc dwCount .endif pop eax invoke GlobalReAlloc,eax,100,GMEM_ZEROINIT sub dwTotalMemory,1000000 - 100 invoke SetDlgItemInt,hWinMain,IDC_MEMORY,\ dwTotalMemory,FALSE invoke SetDlgItemInt,hWinMain,IDC_COUNT,\ dwCount,FALSE .until ! @lpLastMem invoke SetDlgItemText,hWinMain,IDC_INFO,addr szInfo mov ifCanQuit,1 ret _ProcThread endp ;#################################################################### _ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam local @dwTemp mov eax,wMsg .if eax == WM_CLOSE .if ifCanQuit invoke EndDialog,hWnd,NULL .endif ;******************************************************************** .elseif eax == WM_INITDIALOG push hWnd pop hWinMain invoke LoadIcon,hInstance,ICO_MAIN invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax invoke CreateThread,NULL,0,offset _ProcThread,NULL,\ NULL,addr @dwTemp invoke CloseHandle,eax ;******************************************************************** .else mov eax,FALSE ret .endif mov eax,TRUE ret _ProcDlgMain endp ;#################################################################### start: invoke GetModuleHandle,NULL mov hInstance,eax invoke DialogBoxParam,hInstance,DLG_MAIN,\ NULL,offset _ProcDlgMain,NULL invoke ExitProcess,NULL ;#################################################################### end start
上页:第10章 内存管理和文件操作 · 10.1 内存管理(2) 下页:第10章 内存管理和文件操作 · 10.1 内存管理(4)