内存管理的概念
进程和内存空间
进程: 一旦程序正在运行,它就叫进程,进程拥有它自己的内存,文件句柄及其他系统资源. Windows任务栏显示的是主窗口而不是进程,单个进程可能有几个主窗口,每个窗口都由它自己的线程支持.
每个进程都具有它自己”私有”的4GB虚拟地址空间, 它包括:程序的EXE映像,所加载的任何非系统的DLL(包括MFC DLL),程序的全局数据,内丰映射文件等等.
Windows95的进程地址空间
在95中,只有地址空间最底部的2GB(0–0X7FFFFFFF)才是真正私有的,顶部的2GB对于所有的进程都是相同的,被所有的进程共享,它顶部的1GB包括Windows95内核,可执行程序,虚拟设备驱动程序(VxDs)和文件系统代码等,另外1GB存放Windows DLL, 内存映射文件.
Windows NT进程地址空间
NT进程只能访问其底部2GB地址空间,且其中最低的和最高的64KB不可访问.NT内核,执行程序及设备程序都驻留在顶部2GB之中.
虚拟内存的工作方式
一般分页存储,每一页为4KB,当使用一页时,占用物理内存,但物理地址你永远看不到,Intel微处理器可以有效地把一个32位虚拟地址映射为物理页以及在该页内的偏移量.每个进程都有它自己的分页表,芯片的CR3寄存器就保持指向当前运行的进程的目录页的指针,进程之间的切换只需要更新CR3即可.
当我们试图访问一个不在当前RAM之中的页,将触发一个中断,Windows通过检查,如果内存引用是假的,就会得到我们常见的”页面错误”,程序退出.否则就把该页从磁盘文件读入RAM中.
内存分配函数
malloc
在内存中分配一块指定大小的空间, 返回的类型为void *型, 可以强制转换为其他类型, 如果内存空间已经不足, 则会返回NULL. 注意: 实际分配的空间可能大于指定的大小, 因为内存块还需要保存队列或其他相关的信息.
free
释放由malloc,calloc,realloc所分配的内存空间, 如果释放不是由这些函数所分配的内存空间会发生错误.
new
为变量初始化内存空间, 例如:
double *pdoub = new double(20.4);
delete
与new对应,释放由new分配的变量所拥有的内存空间.
HeapAlloc, HeapFree
GlobalAlloc
此函数从堆里面分配一个指定大小的内存空间, 此函数仅仅是为了与16位版本兼容而设.
uFlags: 分配的内存属性, 它可以有以下几种属性值:
如果此参数为0,缺省为GMEM_FIXED. 它分配一个固定的内存空间, 返回的值为一个指针
GMEM_MOVEABLE分配一个活动的内存, 在WIN32里,内存块在物理内存里是从不移动的,但它们可以在缺省的堆里面移动. 此参数不能与GMEM_FIXED联合. 它的返回值是一个内存对象句柄,如果要把它转变为一个指针,需使用GlobalLock函数.
GPTR与GMEM_FIXED和GMEM_ZEROINIT联合
GHND与GMEM_MOVEABLE和GMEM_ZEROINIT联合
GMEM_DDESHARE, GMEM_SHARE, 这两个属性首先是为了与16位兼容的,然后使用它可以提高应用程序执行DDE操作的效率,所以如果内存被用于DDE, 可以指定它.
GMEM_DISCARDABLE, 被忽略,仅是为了与16位兼容.在WIN32里,你必须使用GlobalDiscard函数释放掉一个内存块.它不能与GMEM_FIXED联合.
GMEM_LOWER,GMEM_NOCOMPACT,GMEM_NODISCARD,GMEM_NOT_BANKED,GMEM_NOTIFY 被忽略,仅为了与16位兼容
GMEM_ZEROINIT, 将内存块初始化为0.
dwBytes: 内存块大小
如果置为0, 且uFlags置为GMEM_MOVEABLE的话, 函数将返回一个内存对话句柄, 此内存对象被标记为已丢弃.
如果函数调用失败, 将返回NULL, 得到错误信息,调用GetLastError
释放内存使用GlobalFree函数.
下面的代码示例使用一块内存:
HGLOBAL hMem;
char *pStartBuffer=NULL;
#define MAX_BUFFER_SIZE 102400
if ( (hMem = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, MAX_BUFFER_SIZE))==NULL )
return FALSE;
pStartBuffer = (char *)GlobalLock(hMem);
GlobalUnlock(hMem);
GlobalFree(hMem);
内存映射文件
假定程序需要阅读一个DIB文件, 一种方法是分配大小合适的一个缓冲区,打开文件,然后调用一个读函数将整个磁盘文件复制进缓冲区; 还有一种更有效的方法, 就是将一个地址范围直接映射到该文件中即可, 当进程访问一个内存页的时候,Windows将分配RAM并从磁盘中读取数据. 代码如下:
HANDLE hFile = ::CreateFile(strPathname,GENERIC_READ,
FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
ASSERT(hFile!=NULL);
HANDLE hMap=
::CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,NULL);
ASSERT(hMap != NULL);
LPVOID lpvFile=::MapViewOfFile(hMap,FILE_MAP_READ,0,0,0);//map whole file
DWORD dwFileSize = ::GetFileSize(hFile,NULL);
// Use the file
::UnmapViewOfFile(lpvFile);
::CloseHandle(hMap);
::CloseHandle(hFile);
lpvFile是起始地址,hMap变量包含有文件映射对象的句柄,该对象可以在进程之间共享.
访问资源
资源包括在EXE文件和DLL文件里面,因此它们所占用的虚拟地址空间在进程生存期间不发生改变.例如: 需要访问一个位图:
LPVOID lpvResource=
(LPVOID)::LoadResource(NULL,::FindResource(NULL,MAKEINTRESOURCE(IDB_REDBLOCKS),RT_BITMAP));
>> 本文固定链接: http://www.vcgood.com/archives/1158