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)

第10章 内存管理和文件操作

版权所有 © 云南伯恩科技 证书:粤ICP备09170368号