WIN32汇编语言教程:第10章 内存管理和文件操作 · 10.1 内存管理(2)

                  .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_INFO          equ       101
;####################################################################
; 数据段
;####################################################################
               .data?
hInstance      dd    ?
hWinMain        dd    ?
 
                .const
szInfo          db    '物理内存总数    %lu 字节',0dh,0ah
                db    '空闲物理内存    %lu 字节',0dh,0ah
                db    '虚拟内存总数    %lu 字节',0dh,0ah
                db    '空闲虚拟内存    %lu 字节',0dh,0ah
                db    '已用内存比例    %d%%',0dh,0ah
                db    '————————————————',0dh,0ah
                db    '用户地址空间总数 %lu 字节',0dh,0ah
                db    '用户可用地址空间 %lu 字节',0dh,0ah,0
;####################################################################
; 代码段
;####################################################################
 
                  .code
;####################################################################
_GetMemInfo    proc
                  local  @stMemInfo:MEMORYSTATUS
                  local  @szBuffer[1024]:byte
 
                  mov    @stMemInfo.dwLength,sizeof @stMemInfo
                  invoke GlobalMemoryStatus,addr @stMemInfo
                  invoke wsprintf,addr @szBuffer,addr szInfo,\
                          @stMemInfo.dwTotalPhys,@stMemInfo.dwAvailPhys,\
                         @stMemInfo.dwTotalPageFile,\
                          @stMemInfo.dwAvailPageFile,\
                          @stMemInfo.dwMemoryLoad,\
                          @stMemInfo.dwTotalVirtual,@stMemInfo.dwAvailVirtual
                  invoke SetDlgItemText,hWinMain,IDC_INFO,addr @szBuffer
                  ret
 
_GetMemInfo    endp
;####################################################################
_ProcDlgMain      proc      uses ebx edi esi hWnd,wMsg,wParam,lParam
 
                  mov    eax,wMsg
                  .if    eax == WM_TIMER
                         call      _GetMemInfo
                  .elseif eax == WM_CLOSE
                          invoke KillTimer,hWnd,1
                         invoke EndDialog,hWnd,NULL
;********************************************************************
             .elseif eax == WM_INITDIALOG
                      push      hWnd
                      pop    hWinMain
                      invoke LoadIcon,hInstance,ICO_MAIN
                      invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax
                      invoke SetTimer,hWnd,1,1000,NULL
                      call      _GetMemInfo
;********************************************************************
              .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

MemInfo.rc文件如下:

 
//##################################################################
#include          <resource.h>
//##################################################################
#define ICO_MAIN              1000
#define DLG_MAIN              100
#define IDC_INFO              101
//##################################################################
ICO_MAIN      ICON              "Main.ico"
//##################################################################
DLG_MAIN DIALOG 188, 193, 140, 75
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
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 内存管理(1) 下页:第10章 内存管理和文件操作 · 10.1 内存管理(3)

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

版权所有 © 中山市飞娥软件工作室 证书:粤ICP备09170368号