Windows 95 System Programming SECRENTS学习笔记---第五章(4)

GetProcessHeap

    使用一个Win32 Heap函数,首先你得有一个heap handle。大部分程序都使用KERNEL32在程序产生时给与的一个默认堆(default heap)。你可以调用GetProcessHeap获得其Heap handle。这个函数很简单,它取出KERNEL32的一个全局变量,指向当前进程的process database。其中存放有进程的默认堆的句柄(handle)。

 

HeapAllocIHeapAlloc

    HeapAlloc,如其名称所示,你可以利用它从某个heap中分配一块内存。它其实只是做参数检验的工作,真实分配内存是由IHeapAllocHPAlloc完成。HeapAlloc检查hHeap所代表的Heap的大小是否足够容纳一个Heap表头。虽然它也可以检查其它成员如signaturechecksum,但很奇怪的是它忽略那些成员。如果hHeap通过测试,HeapAlloc就调用IHeapAlloc

 

    IHeapAlloc其实只是HPAlloc的外包函数。后者才是真正的HeapAlloc“主体”。在调用HPAlloc之前,IHeapAllocdwFlags(调用者传入的标志)做了一些处理。唯一可能幸存的是HEAP_ZERO_MEMORYHEAP_GENERATE_EXCEPTIONS两个标志。前者的值(如果幸存的话)比其原值左移三位。

 

HPAlloc

    这才是HeapAlloc的主体。它首先检查,要求的区块大小是否太大。太大意味0x0FFFFF98(接近256MB)。接下来HPAlloc调用hpTakeSem,这使得heap表头中的critical section被取得。从此,进程中就没有起他线程可以调用HPAlloc,直到HPAlloc结束为止。在调试版中,hpTakeSem也会随机检验heap是否被摧毁。hpTakeSem还可以遍历整个heap,检查checksum以及signature。你可以利用HeapSetFlags切换这些能力。HeapSetFlags加入Windows 95的时间太晚,以至于我没有办法把它放到本书中来讲。

 

    HPAlloc接下来去出区块大小参数,调整使它接近4的倍数(也要考虑arena的大小)。最小值是0x18。在减去arena的大小之后,留给使用者的只剩8个位。知道区块大小之后,HPAlloc就能决定搜寻四个自由链表中的那一个。找到正确的链表之后,HPAlloc遍历整个链表(使用自由区块的prev指针)以找出第一个够大的区块。

 

    这个时候,让我们假设HPAlloc找到了一个够大的区块。于是它调用hpCarve(稍候我将介绍)。hpCarve函数检查一个区块,看看它是否刚好够大,或是可以分裂为两块。如果需要分裂,hpCarve处理所有的工作,包括产生新的arena、初始化prevnext等等。分裂出来的其中一块刚好够大,满足HPAlloc的需求。另一块被放到自由链表之中。

 

    hpCarve返回之后,HPAlloc将新的区块的arena成员初始化。除了“取得HPAlloc调用者的EIP”,以及“计算前三个成员的checksum”之外,其余只是一些简单的设定动作。最后,HPAlloc释放heap的临界区对象,返回一个指针,指向arena之后的第一个字节。

 

如果HPAlloc没有发现一个自由区块的话,并且heap允许自动增长(产生堆时,dwMaximumSize被指定为0),HPAlloc则需要产生一个新的sunheap。先前我说过,一个subheap是另一个独立空间,内含一些heap区块。KERNEL32负责跟踪所有的subheaps,作法是把它们统统维护在一个链表之中。如果需要产生subheapKERNEL32会决定其初始大小(通常是4KB),并调用VMM报留一些pages。接下来HPAlloc调用HPInit将新的subheap的表头初始化。初始化之后,HPAlloc把它安插到subheaps所组成的链表中。最后,HPAlloc跳到“搜寻自由链表”的起始处。我想,这一次应该可以找到足够大小的区块了。

 

Windows 95中,Win32堆中的内存最初都处于reserved,并为committed。如果我们有1MB堆并且在还不需要这1MB内存时,并不会提交实际的内存页。当程序触及被reserved而尚未被committedpage时,会引发page fault。因此,堆函数必须确定所有使用中的区块涉及到的pages都作了commit动作。在Windows 95中,系统并不是用结构化异常来做commit动作。

 

HeapSizeIHeapSize

    HeapSize获得一个指针,指向先前分配的一块内存,并返回其大小(不含arena)。

 

HeapFreeIHeapFree

    HeapFree仅负责参数检验。真实的动作发生在IHeapFreex_HeapFree中,除了释放指定的堆内存,该函数还会检查是否有必要进行自由区块的合并。

 

HeapReAllocIHeapReAlloc

    HeapReAlloc对原已存在的一个Win32堆重新配置其大小。HeapReAlloc只负责参数检验,其动作和HeapFree所做的一样。

 

    Windows 95中,IHeapReAlloc有一点诡异,它会重新按排dwFlags参数,能通过的标志如下:

HEAP_GENERATGE_EXCEPTIONS

HEAP_NO_SERIALIZE

HEAP_ZERO_MEMORY

HEAP_REALLOC_IN_PLACE_ONLY

 

在进行重新分配时,HeapReAlloc有四种情况需要考虑:

l         新区块比原区块小

l         新区块和原区块大小相差不多

l         新区块比原区块大。而堆中的下一个区块是自由的,并且可以和原区块合并其来,形成满足要求的新区块。

l         新区块比原区块大。而堆中的下一个区块并不自由,或者虽然它是自由的,但和原区块合并后还是无法满足要求。

 

HeapCreate

    HeapCreate是所有Win32堆函数的根源。每个Win32程序在开始之前都有一个默认的堆。此外,程序还可以调用HeapCreate产生另外的堆。除了被应用程序使用之外,KERNEL32也调用HeapCreate在全局共享内存中产生堆,并用这些堆来对系统数据结构(诸如线程或进程相关资料)

 

    产生一个Win32堆的过程可分为两个部分。第一部分是保留内存并将其连接到进程的堆链表上,另一部分是初始化堆表头。

 

HeapDestroyIHeapDestroy

    与前面介绍的函数类似,HeapDestroy只是做参数检验工作,实际的摧毁Win32堆的动作是由IHeapDestroy完成的。摧毁Win32堆并不像释放堆还给操作系统那么简单。有两件事情使它比较复杂。第一,所有未以HEAP_NO_SERIALIZE属性产生出来的堆,都持有一个临界区对象。IHeapDestroy检查要摧毁的堆是否拥有此对象,并适当的释放之。

 

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值