Windows核心编程 第十八章 堆栈
第1 8章 堆 栈
对内存进行操作的第三个机制是使用堆栈。堆栈可以用来分配许多较小的数据块。例如,若要对链接表和链接树进行管理,最好的方法是使用堆栈,而不是第 1 5章介绍的虚拟内存操作方法或第1 7章介绍的内存映射文件操作方法。堆栈的优点是,可以不考虑分配粒度和页面边界之类的问题,集中精力处理手头的任务。堆栈的缺点是,分配和释放内存块的速度比其他机制要慢,并且无法直接控制物理存储器的提交和回收。
从内部来讲,堆栈是保留的地址空间的一个区域。开始时,保留区域中的大多数页面没有被提交物理存储器。当从堆栈中进行越来越多的内存分配时,堆栈管理器将把更多的物理存储器提交给堆栈。物理存储器总是从系统的页文件中分配的,当释放堆栈中的内存块时,堆栈管理器将收回这些物理存储器。
18.1 进程的默认堆栈
当进程初始化时,系统在进程的地址空间中创建一个堆栈。该堆栈称为进程的默认堆栈。
按照默认设置,该堆栈的地址空间区域的大小是 1 MB。但是,系统可以扩大进程的默认堆栈,使它大于其默认值。当创建应用程序时,可以使用 / H E A P链接开关,改变堆栈的1 M B默认区域大小。由于 D L L没有与其相关的堆栈,所以当链接 D L L时,不应该使用 / H E A P链接开关。
/ H E A P链接开关的句法如下:
/ H E A P:reserve[,commit]
许多Wi n d o w s函数要求进程使用其默认堆栈。例如, Windows 2000的核心函数均使用U n i c o d e字符和字符串执行它们的全部操作。如果调用Wi n d o w s函数的A N S I版本,那么该A N S I版本必须将A N S I字符串转换成U n i c o d e字符串,然后调用同一个函数的 U n i c o d e版本。为了进行字符串的转换,A N S I函数必须分配一个内存块,以便放置U n i c o d e版本的字符串。该内存块是从你的进程的默认堆栈中分配的。 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.2.1 保护组件
假如你的应用程序需要保护两个组件,一个是节点结构的链接表,一个是 B R A N C H结构的二进制树。你有两个源代码文件,一个是 L n k L s t . c p p,它包含负责处理
N O D E链接表的各个函数,另一个文件是B i n Tr e e . c p p,它包含负责处理
分支的二进制树的各个函数。
如果节点和分支一道存储在单个堆栈中,那么这个组合堆栈将类似图1 8 - 1所示的样子。
现在假设链接表代码中有一个错误,它使节点 1后面的8个字节不小心被改写了,从而导致分支 3中的数据被破坏。当B i n Tr e e . c p p文件中的代码后来试图遍历二进制树时,它将无法进行这项操作,因为它的内存已经被破坏。当然,这使你认为二进制树代码中存在一个错误,而实际上错误是在链接表代码中。由于不同类型的对象混合放在单个堆栈中,因此跟踪和确定错误将变得非常困难。
通过创建两个独立的堆栈,一个堆栈用于存放节点,另一个堆栈用于存放分支,就能够确定你的问题。你的链接表代码中的一个小错误不会破坏你的二进制树的完整性。反过来,二进制树中的小错误也不会影响链接表代码中的数据完整性。但是,你的代码中的错误仍然
可能导致对堆栈进行杂乱的内存写操作,不过出现这种情况的可能性很小。
18.2.2 更有效的内存管理
通过在堆栈中分配同样大小的对象,就可以更加有效地管理堆栈。例如,假设每个节点结构需要2 4字节,每个分支结构需要3 2字节。所有这些对象均从单个堆栈中分配。图 1 8 - 2显示了单个堆栈中已经分配的若干个节点和分支对象占满了这个堆栈。如果节点 2和节点4被释放,堆栈中的内存将变成许多碎片。这时,如果试图分配分支结构,那么尽管分支只需要3 2个字节,而实际上可以使用的有4 8个字节,但是分配仍将失败。
如果每个堆栈只包含大小相同的对象,那么释放一个对象后,另一个对象就可以恰好放入被释放的对象空间中。
18.2.3 进行本地访问
每当系统必须在R A M与系统的页文件之间进行 R A M页面的交换时,系统的运行性能就会受到很大的影响。如果经常访问局限于一个小范围地址的内存,那么系统就不太可能需要在 R A M与磁盘之间进行页面的交换。
所以,在设计应用程序的时候,如果有些数据将被同时访问,那么最好把它们分配在互相靠近的位置上。让我们回到链接表和二进制树的例子上来,遍历链接表与遍历二进制树之间并无什么关系。如果将所有的节点放在一起(放在一个堆栈中),就可以使这些节点位于相邻的页面上。实际上,若干个节点很可能恰好放入单个物理内存页面上。遍历链接表将不需要 C P U为了访问每个节点而引用若干不同的内存页面。
如果将节点和分支分配在单个页面上,那么节点就不一定会互相靠在一起。在最坏的情况下,每个内存页面上可能只有一个节点,而其余的每个页面则由分支占用。在这种情况下,遍历链接表将可能导致每个节点的页面出错,从而使进程运行得极慢。
18.2.4 减少线程同步的开销
正如下面就要介绍的那样,按照默认设置,堆栈是顺序运行的,这样,如果多个线程试图同时访问堆栈,就不会使数据受到破坏。但是,堆栈函数必须执行额外的代码,以保证堆栈对线程的安全性。如果要进行大量的堆栈分配操作,那么执行这些额外的代码会增加很大的负担,从而降低你的应用程序的运行性能。当你创建一个新堆栈时,可以告诉系统,只有一个线程将访问该堆栈,因此额外的代码将不执行。但是要注意,现在你要负责保证堆栈对线程的安全性。系统将不对此负责。
18.2.5 迅速释放堆栈
最后要说明的是,将专用堆栈用于某些数据结构后,就可以释放整个堆栈,而不必显式释放堆栈中的每个内存块。例如,当Windows Explorer遍历硬盘驱动器的目录层次结构时,它必须在内存中建立一个树状结构。如果你告诉 Windows Explorer刷新它的显示器,它只需要撤消包含这个树状结构的堆栈并且重新运行即可(当然,假定它将专用堆栈用于存放目录树信息)。对于许多应用程序来说,这是非常方便的,并且它们也能更快地运行。
之后的内容就是介绍一些函数,这里我就把函数写出来,具体使用细节可以查看文档。
1.创建辅助堆栈
HANDLE HeapCreate(
DWORD fdwOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize);
2.从堆栈中分配内存块
PVOID HeapAlloc(
HANDLE hHeap,
DWORD fdwFlags,
SIZE_T dwButes);
3.改变内存块大小
PVOID HeapReAlloc(
HANDLE hHeap,
DWORD fdwFlags,
PVOID pvMem,
SIZE_T dwBytes);
4.了解内存块大小
SIZE_T HeapSize(
HANDLE hHeap,
DWORD fdwFlags,
LPCVOID pvMem);
5.释放内存块
BOOL HeapFree(
HANDLE hHeap,
DWORD fdwFlags,
PVOID pvMem);
6.撤销堆栈
BOOL HeapDestroy(HANDLE hHeap);
7.获取现有堆栈信息
DWPRD GetProcessHeaps(
DWORD dwNumHeaps,
PNANDLE pHeaps);
8.验证堆栈完整性
BOOL HeapVa;odate(
HANDLE hHeap,
DWORD fdwFlags,
LPCVOID pvMem);
9.合并地址中的空闲内存块回收不包含已经分配的地址内存块的存储器页面
UINT HeapCompact(
HANDLE hHeap,
DWORD fdwFlags);
10.堆栈上锁
BOOL HeapLock(HANDLE hHeap);
BOOL HeapUnLock(HANDLE hHeap);
11.遍历堆栈内容
BOOL HeapWalk(
HANDLE hHeap,
PPROCESS_HEAP_ENTRY pHeapEntry);
Windows核心编程 第十八章 堆栈的更多相关文章
- 【windows核心编程】 第八章 用户模式下的线程同步
Windows核心编程 第八章 用户模式下的线程同步 1. 线程之间通信发生在以下两种情况: ① 需要让多个线程同时访问一个共享资源,同时不能破坏资源的完整性 ② 一个线程需要通知其他线程 ...
- Windows核心编程 第十九章 DLL基础
第1 9章 D L L基础 这章是介绍基本dll,我就记录一些简单应用,dll的坑点以及扩展后面两章会说,到时候在总结. 自从M i c r o s o f t公司推出第一个版本的Wi n d o w ...
- Windows核心编程 第十五章 在应用程序中使用虚拟内存
第1 5章 在应用程序中使用虚拟内存 Wi n d o w s提供了3种进行内存管理的方法,它们是: • 虚拟内存,最适合用来管理大型对象或结构数组. • 内存映射文件,最适合用来管理大型数据流(通常 ...
- 《Windows核心编程》第八章——用户模式下的线程同步
下面起了两个线程,每个对一个全局变量加500次,不假思索进行回答,会认为最后这个全局变量的值会是1000,然而事实并不是这样: #include<iostream> #include &l ...
- Windows核心编程 第十二章 纤程
第1 2章 纤 程 M i c r o s o f t公司给Wi n d o w s添加了一种纤程,以便能够非常容易地将现有的 U N I X服务器应用程序移植到Wi n d o w s中.U N I ...
- Windows核心编程 第十四章 虚拟内存
第1 4章 虚 拟 内 存 <这一章没啥,是说的几个内存相关的函数 > 14.1 系统信息 许多操作系统的值是根据主机而定的,比如页面的大小,分配粒度的大小等.这些值决不应该用硬编码的形式 ...
- 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。
windows内的各个进程有各自的地址空间.它们相互独立互不干扰保证了系统的安全性.但是windows也为调试器或是其他工具设计了一些函数,这些函数可以让一个进程对另一个进程进行操作.虽然他们是为调试 ...
- windows核心编程---第八章 使用内核对象进行线程同步
使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...
- 《windows核心编程系列》十八谈谈windows钩子
windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功 ...
随机推荐
- There only 10 people use the same phone as you(i春秋CTF题解)
(1)访问网址进行CTF测试,仅出现登陆与注册的页面 (2)进行注册尝试登陆并进行burp抓取数据包: (3)注册成功,进行登陆尝试查看信息是否具有提示,在登录的页面只有两个点击页面,一个为:Ch ...
- 小技巧!CSS 整块文本溢出省略特性探究
今天的文章很有意思,讲一讲整块文本溢出省略打点的一些有意思的细节. 文本超长打点 我们都知道,到今天(2020/03/06),CSS 提供了两种方式便于我们进行文本超长的打点省略. 对于单行文本,使用 ...
- 一文弄懂js的执行上下文与执行上下文栈
目录 执行上下文与执行上下文栈 变量提升与函数提升 变量提升 函数提升 变量提升与函数提升的优先级 变量提升的一道题目引出var关键字与let关键字各自的特性 执行上下文 全局执行上下文 函数(局部) ...
- Caused by: java.lang.RuntimeException: JxBrowser license check failed: No valid license found
使用jxbrower报错,原因时证书检验失败, 解决方案: 1.首先创建证书,下面是我在IDEA maven项目中创建的位置,Java项目中在src/目录下创建, META-INF/teamdev.l ...
- Docker遇到的异常和注意点
Docker遇到的异常和注意点 整理一些使用docker的时候,遇到的问题和解决办法 遇到的一些异常和解决方法 删除镜像时出现: Error response from daemon: conflic ...
- 微信小程序授权登录以及用户信息相关接口调整导致授权框不弹出
前言:4月8号升级了小程序业务后提交了版本并上线.突然一个同事说体验版的点击"登录"按钮无效.当时觉得应该不会呀,这几天一直用手机调试,每天也在不停的登录授权,弹框一直有的呀.然后 ...
- 【数据结构与算法笔记04】对图搜索策略的一些思考(包括DFS和BFS)
图搜索策略 这里的"图搜索策略"应该怎么理解呢? 首先,是"图搜索",所谓图无非就是由节点和边组成的,那么图搜索也就是将这个图中所有的节点和边都访问一遍. 其次 ...
- Qt信号槽源码剖析(一)
大家好,我是IT文艺男,来自一线大厂的一线程序员 大家在使用Qt开发程序时,都知道怎么使用Qt的信号槽,但是Qt信号槽是怎么工作的? 大部分人仍然不知道:也就是说大家只知道怎么使用,却不知道基于什么原 ...
- [Fundamental of Power Electronics]-PART I-5.不连续导电模式-5.1 DCM来源和模式边界
引子: 当使用电流单向和/或电压单向半导体开关实现DC-DC变换器的理想开关时,可能会出现一种或多种被称为不连续导电模式(DCM)的新工作模式.当电感电流或电容电压的纹波大到足以导致所施加的开关电流或 ...
- 热更新解决方案--tolua学习笔记
一.tolua使用准备工作:从GitHub上下载tolua(说明:这篇笔记使用的Unity版本是2019.4.18f1c1,使用的tolua是2021年4月9日从GitHub上Clone的tolua工 ...