按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
上一页 回书目 下一页
第10章 内存管理和文件操作
10。1 内 存 管 理(2)
。data?
hInstance dd ?
hWinMain dd ?
nst
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
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
; 代码段
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
de
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
_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
//》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
#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 内 存 管 理(3)
可以在GlobalReAlloc函数中通过指定不同的uFlags来规定是否允许Windows在必要的时候移动内存块。当uFlags中有GMEM_MOVEABLE选项的时候,如果需要移动内存块,Windows会在别的地方开辟一块新的内存,并把原来内存块中的内容自动复制到新的内存块中,这时函数的返回值是新的指针,原来的指针作废。
如果不指定GMEM_MOVEABLE选项,那么只有当内存块后面扩展所需的空间没有被使用时,函数才会执行成功,否则,函数失败并返回NULL,这时原来的指针继续有效。
为了保证内存块扩大成功,建议总是使用下面的语句来扩大和缩小内存:
invoke GlobalReAlloc,lpMemory,dwBytes,GMEM_ZEROINIT or GMEM_MOVEABLE
。if eax