windows核心编程18

 
对内存进行操作的第三个机制是使用堆栈。 堆栈可以用来分配许多较小的数据块。例如,若要对链接表和链接树进行管理,最好的方法是使用堆栈,而不是第1 5章介绍的虚拟内存操作方法或第1 7章介绍的内存映射文件操作方法。 堆栈的优点是,可以不考虑分配粒度和页面边界之类的问题,集中精力处理手头的任务。堆栈的缺点是,分配和释放内存块的速度比其他机制要慢,并且无法直接控制物理存储器的提交和回收。
 
从内部来讲,堆栈是保留的地址空间的一个区域。开始时,保留区域中的大多数页面没有被提交物理存储器。当从堆栈中进行越来越多的内存分配时,堆栈管理器将把更多的物理存储器提交给堆栈。 物理存储器总是从系统的页文件中分配的,当释放堆栈中的内存块时,堆栈管理器将收回这些物理存储器。
 
M i c r o s o f t并没有以文档的形式来规定堆栈释放和收回存储器时应该遵循的具体规则,Windows 98 与Windows 2000的规则是不同的。可以这样说,Windows 98 更加注重内存的使用,因此只要可能,它就收回堆栈。Windows 2000更加注重速度,因此它往往较长时间占用物理存储器,只有在一段时间后页面不再使用时,才将它返回给页文件。M i c r o s o f t常常进行适应性测试并运行各种不同的条件,以确定在大部分时间内最适合的规则。随着使用这些规则的应用程序和硬件的变更,这些规则也会有所变化。如果了解这些规则对你的应用程序非常关键,那么请不要使用堆栈。相反,可以使用虚拟内存函数(即Vi r t u a l A l l o c和Vi r t u a l F r e e),这样,就能够控制这些规则。
 
18.1 进程的默认堆栈
当进程初始化时,系统在进程的地址空间中创建一个堆栈。该堆栈称为进程的默认堆栈。按照默认设置,该堆栈的地址空间区域的大小是1 MB。
 
由于进程的默认堆栈可供许多Wi n d o w s函数使用,你的应用程序有许多线程同时调用各种Wi n d o w s函数,因此对默认堆栈的访问是顺序进行的。换句话说,系统必须保证在规定的时间内,每次只有一个线程能够分配和释放默认堆栈中的内存块。如果两个线程试图同时分配默认堆栈中的内存块,那么只有一个线程能够分配内存块,另一个线程必须等待第一个线程的内存块分配之后,才能分配它的内存块。一旦第一个线程的内存块分配完,堆栈函数将允许第二个线程分配内存块。这种顺序访问方法对速度有一定的影响。如果你的应用程序只有一个线程,并且你想要以最快的速度访问堆栈,那么应该创建你自己的独立的堆栈,不要使用进程的默认堆栈。不幸的是,你无法告诉Wi n d o w s函数不要使用默认堆栈,因此,它们对堆栈的访问总是顺序进行的。
 
单个进程可以同时拥有若干个堆栈。这些堆栈可以在进程的寿命期中创建和撤消。但是,默认堆栈是在进程开始执行之前创建的,并且在进程终止运行时自动被撤消。不能撤消进程的默认堆栈。每个堆栈均用它自己的堆栈句柄来标识,用于分配和释放堆栈中的内存块的所有堆栈函数都需要这个堆栈句柄作为其参数。
 
18.2 为什么要创建辅助堆栈
除了进程的默认堆栈外,可以在进程的地址空间中创建一些辅助堆栈。由于下列原因,你可能想要在自己的应用程序中创建一些辅助堆栈:
? 保护组件。
? 更加有效地进行内存管理。
? 进行本地访问。
? 减少线程同步的开销。
? 迅速释放。
 
18.3 如何创建辅助堆栈
你可以在进程中创建辅助堆栈,方法是让线程调用H e a p C r e a t e函数:
HANDLE    HeapCreate (
       DWORD    fdwOptions,
       SIZE_T    dwInitialSize,
      SIZE_T    dwMaximumSize );
 
按照默认设置,堆栈将顺序访问它自己,这样,多个线程就能够分配和释放堆栈中的内存块而不至于破坏堆栈。当试图从堆栈分配一个内存块时, H e a p A l l o c函数(下面将要介绍)必须执行下列操作:
1) 遍历分配的和释放的内存块的链接表。
2) 寻找一个空闲内存块的地址。
3) 通过将空闲内存块标记为“已分配”分配新内存块。
4) 将新内存块添加给内存块链接表。
 
18.3.1 从堆栈中分配内存块
若要从堆栈中分配内存块,只需要调用H e a p A l l o c函数:
PVOID   HeapAlloc (
       HANDLE    hHeap,
       DWORD     fdwFlags,
       SIZE_T        dwBytes );
第一个参数h H e a p用于标识分配的内存块来自的堆栈的句柄。d w B y t e s参数用于设定从堆栈中分配的内存块的字节数。参数f d w F l a g s用于设定影响分配的各个标志。
 
如果内存块已经成功地分配, H e a p A l l o c返回内存块的地址。如果内存不能分配并且没有设定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 A l l o c函数返回N U L L。
 
注意:当你分配较大的内存块(大约1 MB 或者更大)时,最好使用Vi r t u a l A l l o c 函数,应该避免使用堆栈函数。
 
18.3.2 改变内存块的大小
常常需要改变内存块的大小。有些应用程序开始时分配的内存块比较大,然后,当所有数据放入内存块后,再缩小内存块的大小。有些应用程序开始时分配的内存块比较小,后来需要将更多的数据拷贝到内存块中去时,再设法扩大它的大小。如果要改变内存块的大小,可以调用H e a p R e A l l o c函数:
PVOID    HheapReAlloc (
       HANDLE   hHeap,
       DWORD    fdwFlags,
       PVOID    pvMem,
       SIZE_T   dwbytes );
 
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函数,系统将忽略对该函数的调用。
 
18.3.6 用C + +程序来使用堆栈
使用堆栈的最好方法之一是将堆栈纳入现有的C + +程序。在C + +中,调用n e w操作符,而不是调用通常的C运行期例程m a l l o c,就可以执行类对象的分配操作。然后,当我们不再需要这个类对象时,调用d e l e t e操作符,而不是调用通常的C运行期例程f r e e将它释放。
CSomeCLass*    pSomeClass = new   CSomeClass;
 
当C + +编译器查看这一行代码时,它首先查看C S o m e C l a s s类是否包含n e w操作符的成员函数。如果包含,那么编译器就生成调用该函数的代码。如果编译器没有找到重载n e w操作符的函数,那么编译器将生成调用标准C + +的n e w操作符函数的代码。
 
当完成对已分配对象的使用后,可以通过调用d e l e t e操作符将它撤消:
delete    pSomeClass;
 
通过为我们的C + +类重载n e w和d e l e t e操作符,就能够很容易地利用堆栈函数。
 
n e w 操作符函数的第一个任务是创建一个堆栈,如果这样的堆栈尚未创建的话。一旦堆栈创建完成,就可以使用 H e a p A l l o c 函数从该堆栈中分配新的对象。
 
18.4 其他堆栈函数
除了上面介绍的堆栈函数外,Wi n d o w s还提供了若干个别的函数。
 
 

你可能感兴趣的:(多线程,c,windows,存储,任务,编译器)