当前位置 : 主页 > 网络编程 > 其它编程 >

Windows内存管理API的体系结构

来源:互联网 收集:自由互联 发布时间:2023-07-02
Windows内存管理API的体系结构首先上两张图分别阐述1.WindowsAPI与CRTC运行时及C标准库的关系2 Windows内存管理API的体系结构 首先上两张图分别阐述 1.WindowsAPI与CRTC运行时及C标准库的关系 2.
Windows内存管理API的体系结构首先上两张图分别阐述1.WindowsAPI与CRTC运行时及C标准库的关系2

Windows内存管理API的体系结构

首先上两张图分别阐述

1.WindowsAPI与CRTC运行时及C标准库的关系

2.Windows内存管理API的体系结构

 

体系2

图1WindowsAPI、CRT及标准C库之间的关系转自http://www.codeproject.com/Articles/22642/What-Every-Computer-Programmer-Should-Know-About-W. 此篇文章值得仔细阅读.

 

windows内存管理API体系结构

图2Windows内存管理API体系结构转自http://www.cppblog.com/woaidongmao/archive/2011/08/12/153184.html


转自http://www.cppblog.com/woaidongmao/archive/2011/08/12/153184.html

 

1. Windows平台下主要的内存管理途径

申请

释放

new delete malloc free CoTaskMemAlloc CoTaskMemFree IMalloc:alloc IMalloc/free GlobalAlloc GlobalFree LocalAlloc LocalFree HeapAlloc HeapFree VirtualAlloc VirtualFree . 调用关系

clip_image002

第一层Win32 API作为系统的接口提供了一组操作虚拟内存的接口

第二层Heap作为虚拟内存的一部分Win32 API又提供了一组操作Heap内存的接口但是这些接口是建立在操作虚拟内存

的接口的基础上。

第三层Windows平台下的C Run-Time Library 又利用Heap API来实现malloc和free。

由此我们可以看出这些动态内存操作方式之间存有单一的层次关系位于这个层次的最低层的是Virtual Memory API可以

说这些方式都是建立在Virtual Memory API的基础上。

调用关系如下表所示为 : new -> malloc -> HeapAlloc -> VirtualAlloc -> 驱动程序的_PageAlloc.

 

调用者

被调用者

msvcrt.malloc kernel32.HeapAlloc(ntdll.RtlAllocateHeap) kernel32.LocalAlloc ntdll.RtlAllocateHeap kernel32.GlobalAlloc ntdll.RtAllocateHeap kernel32.HeapAlloc ntdll.RtAllocateHeap(映射) kernel32.VirtualAlloc kernel32.VirtualAllocEx kernel32.VirtualAllocEx ntdll.NtAllocateVirtualMemory ntdll.RtlAllocateHeap ntdll.NtAllocateVirtualMemory ntdll.NtAllocateVirtualMemory ntdll.KiFastSystemCall ntdll.KiFastSystemCall sysenter指令0x0F34

3. 方法解析

3.1 Virtual Memory API

作为Windows系统提供的最"核心"的对虚拟内存操作的接口也作为其他几种方式的基础Virtual Memory API应该在几种

方式中是最通用也是功能最强大的一种方式。在Windows里内存管理是分为两部份全局内存是系统管理的内存因而是所

有进程都可以访问的内存而每一个进程又有自己的内存空间这就是虚拟内存空间了而虚拟内存的空间比较大当物理内

存不足时系统会把虚拟内存的数据保存到硬盘里这样只要硬盘的空间足够大每个进程就可以使用3G的内存。虚拟内存分

配可以作为程序里分配内存的主要方式比如大量的数据缓冲区动态分配内存的空间。使用VirtualAlloc函数来分配内存的速

度要比全局内存要快。

1: LPVOID WINAPI VirtualAlloc( __in_opt LPVOID lpAddress,  __in SIZE_T dwSize,  __in DWORD flAllocationType, __in DWORD flProtect );

lpAddress是指定内存开始的地址。

dwSize是分配内存的大小。

flAllocationType是分配内存的类型。

flProtect是访问这块分配内存的权限。

void MemVirtual(void) {

//分配新内存大小。

UINT nNewSize (UINT) ceil(1500 / 1024.0) * 1024;

PBYTE pNewBuffer (PBYTE) VirtualAlloc(NULL,nNewSize,MEM_COMMIT,PAGE_READWRITE);

if (pNewBuffer){

//测试虚拟内存。

ZeroMemory(pNewBuffer,1500);

memcpy(pNewBuffer,_T("分配虚拟内存成功\r\n"),sizeof(_T("分配虚拟内存成功\r\n")));

OutputDebugString((LPWSTR)pNewBuffer);

//释放分配的内存第三个参数一定是MEM_RELEASE

VirtualFree(pNewBuffer,0,MEM_RELEASE);

}

}

3.2 Heap Memory API

在进程私有的内存空间里分配里有两种分配情况一种是基于栈式的内存分配另一种是基于堆内存的分配。使用堆内存分

配是使用HeapAlloc函数来实现的也就是实现new操作符分配内存时会调这个函数。这里的"Heap"指的是进程拥有的一种对

象(Windows中有很多对象例如WINDOWICONBRUSH)当我们创建一个Heap对象的时候我们就可以获得这个对象

的Handle,然后我们就可以使用这个handle来使用动态内存最后销毁这个对象。

1: LPVOID WINAPI HeapAlloc(__in HANDLE hHeap, __in DWORD dwFlags, __in SIZE_T dwBytes);

hHeap是进程堆内存开始位置。

dwFlags是分配堆内存的标志。

dwBytes是分配堆内存的大小。

void MemHeap(void){

const int nHeapSize 1024;

PBYTE pNewHeap (PBYTE) ::HeapAlloc(GetProcessHeap(), 0, nHeapSize);

if (pNewHeap){

//测试分配堆内存。

ZeroMemory(pNewHeap,nHeapSize);

memcpy(pNewHeap,_T("分配堆内存成功\r\n"),sizeof(_T("分配堆内存成功\r\n")));

OutputDebugString((LPWSTR)pNewHeap);

//释放内存

BOOL bRes ::HeapFree(GetProcessHeap(), 0, pNewHeap);

if (bRes ! TRUE){

OutputDebugString(_T("释放内存出错\r\n"));

}

14: }

15: }

3.3 LocalAlloc/GlobalAlloc

这两个函数是Win16 API中遗留下来的两个函数Win32 API为了保持兼容性才包含了这两个函数。这两个函数内部是通过

Heap Memory API来操作一个"特殊"的Heap对象进程的默认堆对象。每一个进程在初始化的时候都会创建一个默认的

Heap对象在进程结束的时候销毁这个默认的Heap对象。LocalAlloc和GlobalAlloc的区别仅表现在Win16环境下在

Win16环境下内存的地址是通过段:段内偏移量 来获取的LocalAlloc()只能在同一段内分配内存而GlobalAlloc可以跨越

段边界访问内存。 在Win32环境下内存访问不存在这样的限制所以他们表现出相同的功能。由于Heap Memory API完全可

以实现他们两个的功能所以在Win32下不推荐使用这两个函数。

在Windows系统里有一项功能非常实用就是剪贴板功能它能够从一个程序里与另一个程序进行数据交换的功能也就是

说两个进程上是可以共享数据。要实现这样的功能Windows系统在底层上有相应的支持就是高端地址的内存是系统内存

这样就可以不同的进程进行共享数据了。因此调用函数GlobalAlloc来分配系统内存让不同的进程实现共享数据也就是剪

贴板功能可以在一个进程内分配内存在另一个进程里访问数据后删除内存。

1: HLOCAL WINAPI LocalAlloc(__in UINT uFlags,__in SIZE_T uBytes);

2: HGLOBAL WINAPI GlobalAlloc (__in UINT uFlags, __in SIZE_T dwBytes);

示例代码

void MemGlobal(void) {

//分配全局内存。

BYTE* pGlobal (BYTE*)::GlobalAlloc(GMEM_FIXED,1024);

if (!pGlobal) {

return;

} else {

//测试全局内存

ZeroMemory(pGlobal,1024);

memcpy(pGlobal,_T("分配内存成功\r\n"),sizeof(_T("分配内存成功\r\n")));

OutputDebugString((LPWSTR)pGlobal);

}

//释放全局内存。

::GlobalFree((HGLOBAL)pGlobal);

}

3.4 malloc/free

这两个函数是使用频率最高的两个函数由于他们是标准C库中的一部分所以具有极高的移植性。这里的"移植性"指的是使用

他们的代码可以在不同的平台下编译通过而不同的平台下的C Run-Time Library的具体实现是平台相关的在Windows平

台的C Run-Time Library中的malloc()和free()是通过调用Heap Memory API来实现的。值得注意的是C Run-Time

Library拥有独立的Heap对象我们知道当一个应用程序初始化的时候首先被初始化的是C Run-Time Library然后才

是应用程序的入口函数而Heap对象就是在C Run-Time Library被初始化的时候被创建的。

对于动态链接的C Run-Time Library,运行库只被初始化一次而对于静态连接的运行库每链接一次就初始化一次所以对

于每个静态链接的运行库都拥有彼此不同的Heap 对象。这样在某种情况下就会出问题导致程序崩溃。例如一个应用程序调

用了多个DLL,除了一个DLL外其他的DLL包括应用程序本身使用动态连接运行库这样他们就使用同一个Heap对象。而有

一个DLL使用静态连接的运行库它就拥有一个和其他DLL不同的Heap 对象当在其他DLL中分配的内存在这个DLL中释放

时问题就出现了(在http://www.codeproject.com/Articles/22642/What-Every-Computer-Programmer-Should-Know-About-W中提到了静态链接的缺点)。

3.5 关键词new/关键词delete

这两个词是C内置的关键词(keyword)。当C编译器看到关键词new的时候例如

CMyObject* pObj new CMyObject;

编译器会执行以下两个任务

a) 在堆上动态分配必要的内存。这个任务是由编译器提供的一个全局函数void* ::operator new(size_t)来完成的。值得注意

的是任何一个类都可以重载这个全局函数。如果类重载了这个函数的化被类重载的那个会被调用。

b) 调用CMyObject的构造函数来初始化刚刚生成的对象。当然如果分配的对象是C中的基本数据类型则不会有构造函数调

用。

如果要深入全局函数void* ::operator new(size_t)的话我们会发现它的具体实现是通过调用malloc来分配内存的而在

windows平台下malloc最终调用的是HeapAlloc方法。

3.6 CoTaskMemAlloc /IMalloc

CoTaskMemAlloc用于COM对象它在进程的缺省堆中分配内存。

IMalloc接口是对 CoTaskMemAlloc/CoTaskMemFree 的再次封装。

网友评论