Windows内存机制解析(二)

by leezy_2000

2003-10-8 17:01

三、浅谈一下Heap

(鉴于Matt Pietrek在它的《Windows 95 系统程式设计大奥秘》对9x系统的heap做了非常详细的讲解,此处涉及的内容将仅限于Win2000)

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

HeapStack正好相反,你需要手动来管理每一块内存的申请和释放(在没有垃圾收集机制的情况下),而对于C/C++程序员来说,操作Heap的方式实在是太多了点。下面是几乎所有可以操作堆内存的方法的列表:

malloc/free

new/delete

GlobalAlloc/GlobalFree

LocalAlloc/LocalFree

HeapAlloc/HeapFree

其中malloc/free由运行时库提供,new/deleteC++内置的操作符。他们都使用运行时库的自己的堆。运行时库的在2000win9x下都有自己独立的堆。这也就意味着只要你一启动进程,你将至少有两个堆,一个作为进程缺省,一个给C/C++运行时库。

GlobalAlloc/GlobalFreeLocalAlloc/LocalFree现在已失去原有的含义,统统从进程缺省堆中分配内存。

HeapAlloc/HeapFree则从指定的堆中分配内存。

单就分配内存而言(new/delete还要管构造和析构),所有这些方式最终要归结到一点200098下都是是HeapAlloc。所以微软才会强调GlobalAlloc/GlobalFreeLocalAlloc/LocalFree会比较慢,推荐使用HeapAlloc,但由于Global**Local**具有较简单的使用界面,因此即使在微软所提供的源代码中他们仍被大量使用。必须指出的是HeapAlloc并不在kernel32.dll中拥有自己的实现,而是把所有调用转发到ntdll.RtlAllocateHeap。下面这张从msdn中截取的图(图2),应该有助于我们理解同堆相关的API

堆内部的运作同SGI STL的分配器有些类似,大体上是这样,OS为每个堆维护几个链表,每个链表上存放指定大小范围的区块。当你分配内存时,操作系统根据你所提供的尺寸,先确定从那个链表中进行分配,接下来从那个链表中找到合适的块,并把其线性地址返还给你。如果你所要求的尺寸,在现存的区块中找不到,那么就新分配一块较大的内存(使用VirtualAlloc),再对他进行切割,而后向你返还某一区块的线性地址。这只是一个大致的情形,操作系统总在不停的更新自己的堆算法,以提高堆操作的速度。

堆本身的信息(包括标志位和链表头等)被存放在Heap Header中,而堆句柄正是指向Heap Header的指针,Heap Header的结构没有公开,稍后我们将试着做些分析。非常有趣的是微软一再强调只对toolhelp API有效的HeapID其实就是堆句柄。

原来是准备分析一下堆内部的一些结构的,可后来一想这么做实用价值并不是很大,所需力气却不小。因此也就没具体进行操作。但这里把实现监测堆中各种变化的小程序的实现思路公开一下,希望对大家有所帮助。这个小程序非常的简单,主要完成的任务就是枚举进程内所有的堆的变化情况。由于涉及到比较两个链表的不同,这里使用了STLvector容器和某些算法来减少编码。同时为了使STL的内存使用不对我们要监测的对象产生干扰,我们需要建立自己的分配器,以使用我们单独创建的堆。此外还需要特别注意的一点是由于toolhelp API Heap32Next在运行过程中不允许对任何堆进行扰动(否则他总返回TRUE,导致我们只能使用vector,并预先保留足够的空间。(访问堆内部某些信息的另一种方式是使用HeapWalk API,看个人喜好了)。

程序的运行过程是这样的,先对当前进程中存在的堆进行枚举,并把结果存入一个set类型的变量heapid1,接下来创建自己的堆给分配器使用,并对进程中存在的堆再次进行枚举并把结果存入另一个set类型的变量heapid2,这样就可以调用set_difference求出我们新建堆的ID,再以后列举队内部的信息时将排除这个ID所表示的堆。接下来就可以在两点之间分别把堆内部的信息存入相应的vector,比较这两个vector,就可以得到对应于分配内存操作,堆内部的变化情况了。

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 252.75pt; HEIGHT: 258pt" type="#_x0000_t75"><img o:title="heap" src="/Develop/ArticleImages/21/21436/CSDN_Dev_Image_2003-10-81710470.gif"><font size="3"></font></shape>

(2 from msdn by Murali R. Krishnan)

下面是一些悬而未决的问题,那位感兴趣可以自己探索。

Heap Header结构是什么样的?

堆内部对内存块的组织方式?(是链表么)

每一个小块的描述信息在那里?(如果是链表,那就应该有指针使这些小块彼此相连。)

你可能感兴趣的:(windows)