来自Windows核心编程 – 第十六章

本章主要讲述内容是应用程序中线程栈的相关知识。

有时候系统会在用户进程的地址空间中预定区域。比如系统在分配进程环境块和线程环境块的时候,就会发生这种情况,还有一种情况就是 分配线程栈。

系统在创建线程时,会给线程栈预定一块地址空间区域,并给区域调拨一些物理存储器。

默认情况下,系统会预定 1MB 的地址空间并调拨两个页面的存储器。

构建应用程序时开发人员可以通过两种方式来改变默认值:

  1. 使用 MS C++ 编译器的 /F 选项 /Freserve
  2. 使用 MS C++ 链接器的 /STACK 选项 /STACK:reserve[,commit]

在构建应用程序时,链接器会把想要的栈的大小写入 .exe 或 .dll 文件的PE文件头中。当系统创建线程栈的时候,会根据PE头文件中的大小来预订地址空间区域。

要注意的是,当调用 CreateThread 或者 _beginthreadex 函数来创建线程时,开发人员可以指定需要在一开始就调拨的存储器数量。

可以将栈大小参数传递为 0,这样的话就会使用默认值 1MB,每次调拨一个物理存储器。

我们应该还记得这个知识点:

对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

所以栈顶应该是在高内存地址,预定了地址空间后,系统会给区域顶部(地址最高)的两个页面调拨物理存储器。这两个页面,一个是我们当前正在使用的页面,它的保护属性是 PAGE_READWRITE,下一个页面称为防护页面,它的保护属性标志是 PAGE_GUARD。

如果需要拓展空间时,系统会先给防护页面下面的页面调拨存储器,然后去除防护页面的保护属性标志,然后给新调拨的存储页指定保护属性标志。这样就能够在需要的时候再给线程栈分配物理存储器了。

要注意的是,系统永远不会给区域地址最低的页面调拨物理存储器。

当系统给区域地址最低的那个页面的前一个页面(倒数第二个页面)调拨物理存储器时,会执行一个额外的操作——抛出 EXCEPTION_STACK_OVERFLOW 异常,该异常对应的值为 0xC00000FD.通过使用结构化异常处理,系统会在发生这一情况时通知我们的程序,从而使程序能够以一种合适的方式从这一异常情况下恢复。

如果线程在引发栈溢出异常后继续使用栈,并用尽了倒数第二个页面,试图访问最后一个没有分配物理存储器的页面,那么此时系统会抛出访问违规异常。此时会弹出错误报告并结束进程。

 

然后还有一个栈下溢的概念…

简单来讲就是访问了超出栈的范围的地址。因为栈的分配是从高到低的。数组的头部在低内存地址。这样如果访问超出了数组的范围,那么就会导致栈的下溢。

而且这种情况很难检测出来…因为后一块内存可能有了别的数据结构,恰好对那块数据结构进行了修改,系统无法检测出这种破坏。

 

最后我还发现了一个好玩的事情,那就是int数组的分配是以16byte为单位的…其他的暂时还没有测试。

QQ图片20160828184158 QQ图片20160828184200 QQ图片20160828184203 QQ图片20160828184205

自己看着比较就好了…没用测double的…可能会有点不一样,指针和函数指针应该也会有些不一样。

【Windows核心编程】线程栈 相关
Tagged on:
0 0 vote
Article Rating
订阅
提醒
0 评论
Inline Feedbacks
View all comments