第18章 堆栈

由于进程的默认堆栈可供许多Wi n d o w s函数使用,你的应用程序有许多线程同时调用各种Wi n d o w s函数,因此对默认堆栈的访问是顺序进行的。换句话说,系统必须保证在规定的时间内,每次只有一个线程能够分配和释放默认堆栈中的内存块。如果两个线程试图同时分配默认堆栈中的内存块,那么只有一个线程能够分配内存块,另一个线程必须等待第一个线程的内存块分配之后,才能分配它的内存块。一旦第一个线程的内存块分配完,堆栈函数将允许第二个线程分配内存块。这种顺序访问方法对速度有一定的影响。如果你的应用程序只有一个线程,并且你想要以最快的速度访问堆栈,那么应该创建你自己的独立的堆栈,不要使用进程的默认堆栈。不幸的是,你无法告诉Wi n d o w s函数不要使用默认堆栈,因此,它们对堆栈的访问总是顺序进行的。

单个进程可以同时拥有若干个堆栈。这些堆栈可以在进程的寿命期中创建和撤消。但是,默认堆栈是在进程开始执行之前创建的,并且在进程终止运行时自动被撤消。不能撤消进程的默认堆栈。每个堆栈均用它自己的堆栈句柄来标识,用于分配和释放堆栈中的内存块的所有堆栈函数都需要这个堆栈句柄作为其参数。

可以通过调用G e t P r o c e s s H e a p函数获取你的进程默认堆栈的句柄:

HANDLE GetProcessHeap();
18.2 为什么要创建辅助堆栈

除了进程的默认堆栈外,可以在进程的地址空间中创建一些辅助堆栈。由于下列原因,你可能想要在自己的应用程序中创建一些辅助堆栈:

• 保护组件。

• 更加有效地进行内存管理。

• 进行本地访问。

• 减少线程同步的开销。

• 迅速释放。

下面让我们来详细说明每个原因。

18.3 如何创建辅助堆栈

你可以在进程中创建辅助堆栈,方法是让线程调用H e a p C r e a t e函数:

HANDLE HeapCreate(
   DWORD fdwOptions,
   SIZE_T dwInitialSize,
   SIZE_T dwMaximumSize);

18.3.1 从堆栈中分配内存块

若要从堆栈中分配内存块,只需要调用H e a p A l l o c函数:

PVOID HeapAlloc(
   HANDLE hHeap,
   DWORD fdwFlags,
   SIZE_T dwBytes);

18.3.2 改变内存块的大小

常常需要改变内存块的大小。有些应用程序开始时分配的内存块比较大,然后,当所有数据放入内存块后,再缩小内存块的大小。有些应用程序开始时分配的内存块比较小,后来需要将更多的数据拷贝到内存块中去时,再设法扩大它的大小。如果要改变内存块的大小,可以调用H e a p R e A l l o c函数:

PVOID HeapReAlloc(
   HANDLE hHeap,
   DWORD fdwFlags,
   PVOID pvMem,
   SIZE_T dwBytes);

与其他情况一样, h H e a p参数用于指明包含你要改变其大小的内存块的堆栈。f d w F l a g s参数用于设定改变内存块大小时H e a p R e A l l o c函数应该使用的标志。可以使用的标志只有下面4个,即H E A P _ G E N E R AT E _ E X C E P T I O N S、H E A P _ N O _ S E R I A L I Z E、H E A P _ Z E R O _ M E M O RY和H E A P _ R E A L L O C _ I N _ P L A C E _ O N LY。

前面两个标志在用于H e a p A l l o c时,其作用相同。H E A P _ Z E R O _ M E M O RY标志只有在你扩大内存块时才使用。在这种情况下,内存块中增加的字节将被置0。如果内存块已经被缩小,那么该标志不起作用。

H E A P _ R E A L L O C _ I N _ P L A C E _ O N LY标志告诉H e a p R e A l l o c函数,它不能移动堆栈中的内存块。如果内存块在增大, H e a p R e A l l o c函数可能试图移动内存块。如果H e a p R e A l l o c能够扩大内存块而不移动它,那么它将会这样做并且返回内存块的原始地址。另外,如果H e a p R e A l l o c必须移动内存块的内容,则返回新的较大内存块的地址。如果内存块被缩小, H e a p R e A l l o c将返回内存块的原始地址。如果内存块是链接表或二进制树的组成部分,那么可以设定H E A P _ R E A L L O C _ I N _ P L A C E _ O N LY标志。在这种情况下,链接表或二进制树中的其他节点可能拥有该节点的指针,改变堆栈中的节点位置会破坏链接表的完整性。

其余的两个参数p v M e m和d w B y t e s用于设定你要改变其大小的内存块的地址和内存块的新的大小(以字节为计量单位)。H e a p R e A l l o c既可以返回新的改变了大小的内存块的地址,也可以在内存块不能改变大小时返回N U L L。

18.3.3 了解内存块的大小

当内存块分配后,可以调用H e a p S i z e函数来检索内存块的实际大小:

SIZE_T HeapSize(
   HANDLE hHeap,
   DWORD fdwFlags,
   LPCVOID pvMem);
参数h H e a p用于标识堆栈,参数p v M e m用于指明内存块的地址。参数f d w F l a g s既可以是0,也可以是H E A P _ N O _ S E R I A L I Z E。

18.3.4 释放内存块

当不再需要内存块时,可以调用H e a p F r e e函数将它释放:

BOOL HeapFree(
   HANDLE hHeap,
   DWORD fdwFlags,
   PVOID pvMem);
H e a p F r e e函数用于释放内存块,如果它运行成功,便返回T R U E。参数f d w F l a g s既可以是0,也可以是H E A P _ N O _ S E R I A L I Z E。调用这个函数可使堆栈管理器收回某些物理存储器,但是这没有保证。

18.3.5 撤消堆栈

如果应用程序不再需要它创建的堆栈,可以通过调用H e a p D e s t r o y函数将它撤消:

BOOL HeapDestroy(HANDLE hHeap);
调用H e a p D e s t r o y函数可以释放堆栈中包含的所有内存块,也可以将堆栈占用的物理存储器和保留的地址空间区域重新返回给系统。如果该函数运行成功, H e a p D e s t r o y返回T R U E。如果在进程终止运行之前没有显式撤消堆栈,那么系统将为你将它撤消。但是,只有当进程终止运行时,堆栈才能被撤消。如果线程创建了一个堆栈,当线程终止运行时,该堆栈将不会被撤消。

在进程完全终止运行之前,系统不允许进程的默认堆栈被撤消。如果将进程的默认堆栈的句柄传递给H e a p D e s t r o y函数,系统将忽略对该函数的调用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值