当系统创建线程的时候,会为线程栈预订一块地址空间区域,并给该区域调拨一些物理存储器。默认会预订1MB的地址空间并调拨两个页面的存储器。但是在构建 应用程序的时候可以改变这个默认值

在构建应用程序的时候链接器会把栈的大小写入到exe和dll文件的pe文件头中,当创建线程的时候会根据PE文件头中的大小来预订空间区域。在调用CreateThread或_beginthreadex的时候开发人员可以指定需要在一开始就调拨的地址空间大小和存储器大小。

下面显示了一台页面大小为4KB的机器上线程栈的地址空间区域(基址为0x08100000 )。该线程栈的地址空间区域和所调拨给该区域的都具有PAGE_READWRITE保护属性。

在预订地址空间后,系统会给区域顶部的两个页面调拨物理存储器。在线程开始之前系统会把线程栈的指针指向最上面那两个页面的末尾。这个页面就是线程开始使用栈的地方。区域顶部往下的第二个页面被称为防护页面。随着调用的越来越多,调用树也越来越深,线程也需要越来越多的栈空间

当线程试图访问防护页中的内存时,系统会得到通知这时系统会先给防护页面下面的那个页面调拨存储器,接着去除当前防护页面的PAGE_GUARD保护属性标志,然后给刚调拨的存储页指定PAGE_GUARD保护属性标志。该项技术使得系统能够在线程需要的时候才增加栈存储器大小。如果线程的调用树 断加深,那么栈空间区域看起来会像下图这样

假设线程调用树非常深,CPu的栈指针寄存器指向的内存地址为0x08003004现在当线程调用另一个函数时,系统必须调拨更多的物理存储器。但是当系统给0x0800100的页面调拨物理存储器时,它的做法和给区域中的其他部分调拨物理存储器有所不同。

由上图可知,系统会除去0x08001000处的PAGE_GUARD保护属性标志,然后给地址为0x08001000的页面调拨物理存器。区别在于系统不会给刚调拨的物理存储器0x09001000指定防护属性。意味着栈的地址空间已经放满了字所能容纳得下的所有物理存储器。系统永远不会给区域底部的那个页面调拨存储器。

当系统给0x080000000调用物理存储器时,它会执行一个额外的操作:抛出EXCEPTION_STACK_OVERFLOW异常。

可以用函数SetThreadStackGuarante函数来设置抛出前面讲的EXCEPTION_STACK_OVERFLOW异常。

为什么系统始终不给栈地址空间最底部的页面调拨物理存储器。这样做目的是为了保护进程中其它数据。使他们不会因为意外的内存写越界而遭到破坏。如果栈越过了所预订的区域,那么线程就会覆盖进程地址空间中其它数据。

另一种很难找到的缺陷是 栈下溢 看下面代码

int WINAPI WinMain(HINSTANCE hInstExe,HINSTANCE ,PTSTR pszCmdLine,int nCmdShow)
{
BYTE aBytes[];
aBytes[] = ; //stack underflow return ;
}

这段代码试图访问线程栈之外的内存。编译器和链接器无法发现代码中的此类错误。这条语句可能引发访问违规,也可能不会(如果访问的刚好是被调拨的话),发生这种情况程序可能会破坏进程另外的一部分内存,而系统是无法检测到的。下面代码中的栈一溢总会引起内存破坏,因为程序刚好在线程栈的后面分配了一块内存。

DWORD WINAPI ThreadFunc(PVOID pvParam)
{
BYTE aBytes[0x10]; MEMORY_BASIC_INFOMATION mbi;
SIZE_T size = VirtualQuery(aBytes,&mbi,sizeof(mbi));
SIZE_T s = (SIZE_T) mbi.AllocationBase + * ;
PBYTE pAddress = (PBYTE) s; BYTE * pBytes = (BYTE*) VirtualAlloc(pAddress,0x10000,MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE);
aBytes[0x1000] = ; //Write in the allocated block,past the stack
return
}

16.1 C/C++运行库的栈检查函数

c++运行库中有一个栈检查函数。在编译代码时,编译器会在必要时生成代码来调用该函数。这个函数的目的是为了确保已经给线程栈调拨了物理存储器。

《windows核心编程》- 线程栈的更多相关文章

  1. windows核心编程 - 线程同步机制

    线程同步机制 常用的线程同步机制有很多种,主要分为用户模式和内核对象两类:其中 用户模式包括:原子操作.关键代码段 内核对象包括:时间内核对象(Event).等待定时器内核对象(WaitableTim ...

  2. windows核心编程 - 线程基础

    一.基本概念: 一个进程至少需要一个线程. 组成:一个线程包括仅包括一个线程堆栈和一个线程内核对象 线程堆栈:用于维护线程在执行代码时需要的所有函数参数和局部变量 线程内核对象:操作系统用它来对线程实 ...

  3. Windows核心编程&线程

    1. 线程上下文:线程内核对象保存线程上一次执行时的CPU寄存器状态 2. 线程上下文切换 3. windows操作系统为抢占式多线程操作系统,系统可以在任何时刻停止一个线程而另行调度另外一个线程.我 ...

  4. 【windows核心编程】 第六章 线程基础

    Windows核心编程 第六章 线程基础 欢迎转载 转载请注明出处:http://www.cnblogs.com/cuish/p/3145214.html 1. 线程的组成 ①    一个是线程的内核 ...

  5. 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。

    windows内的各个进程有各自的地址空间.它们相互独立互不干扰保证了系统的安全性.但是windows也为调试器或是其他工具设计了一些函数,这些函数可以让一个进程对另一个进程进行操作.虽然他们是为调试 ...

  6. windows核心编程---第八章 使用内核对象进行线程同步

    使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...

  7. 【windows核心编程】 第八章 用户模式下的线程同步

    Windows核心编程 第八章 用户模式下的线程同步 1. 线程之间通信发生在以下两种情况: ①    需要让多个线程同时访问一个共享资源,同时不能破坏资源的完整性 ②    一个线程需要通知其他线程 ...

  8. Windows核心编程学习九:利用内核对象进行线程同步

    注:源码为学习<Windows核心编程>的一些尝试,非原创.若能有助于一二访客,幸甚. 1.程序框架 #include "Queue.h" #include <t ...

  9. 用户模式下的线程同步的分析(Windows核心编程)

    线程同步 同一进程或者同一线程可以生成许多不同的子线程来完成规定的任务,但是多个线程同时运行的情况下可能需要对某个资源进行读写访问,比如以下这个情况:创建两个线程对同一资源进行访问,最后打印出这个资源 ...

  10. CreateThread 线程操作与 _beginthreadex 线程安全(Windows核心编程)

    0x01 线程的创建 线程不同于进程,Windows 中的进程是拥有 '惰性' 的,本身并不执行任何代码,而执行代码的任务转交给主线程,列如使用 CreateProcess 创建一个进程打开 Cmd ...

随机推荐

  1. 将datatable导出为excel的三种方式(转)

    一.使用Microsoft.Office.Interop.Excel.DLL 需要安装Office 代码如下:  2         public static bool ExportExcel(Sy ...

  2. [libGDX游戏开发教程]使用libGDX进行游戏开发(12)-Action动画

    前文章节列表:  使用libGDX进行游戏开发(11)-高级编程技巧   使用libGDX进行游戏开发(10)-音乐音效不求人,程序员也可以DIY   使用libGDX进行游戏开发(9)-场景过渡   ...

  3. MySQL存储IP地址

    mysql没有提供IP类型,常见的存储多为使用varchar类型.其实使用int型更好,主要原因是便于计算IP段. 具体应用时应设置字段类型为unsigned int.否则无法存储128.x.x.x及 ...

  4. [linux]压缩、解压命令

    .tar.gz 和 .tgz 解压:tar zxvf FileName.tar.gz 压缩:tar zcvf FileName.tar.gz DirName tar 解包:tar xvf FileNa ...

  5. Web CI过程中的Security解决方案

    http://www.infoq.com/cn/articles/WebScan-CI 一. 当前Web应用安全现状 随着中国互联网金融的爆发和繁荣,Web应用在其中扮演的地位也越来越重要,比如Web ...

  6. java常见异常归纳

    1.java.lang.NullPointerException(空指针异常)    调用了未经初始化的对象或者是不存在的对象 经常出现在创建图片.调用数组这些操作中,比如图片未经初始化,或者图片创建 ...

  7. [BZOJ 1789] Necklace

    Link: BZOJ 1789 传送门 Solution: 感觉$n\le 50$可以随便乱搞啊…… 这里我是先找到3条链的失配位置,再找到这之后其中2条链最远的失配位置,统计即可 Code: #in ...

  8. 【矩阵乘法】【快速幂】【递推】斐波那契数列&&矩乘优化递推模板

    题目大意: F[0]=0 F[1]=1 F[n+2]=F[n+1]+F[n] 求F[n] mod 104. F[n+2] F[n+1] = 1 1 1 0 * F[n+1] F[n] 记这个矩阵为A, ...

  9. python基础--常用模块与面向对象基础

    1常用模块 1.1 xml xml是实现不同语言或程序之间进行数据交换的协议 xml的格式如下: <?xml version="1.0"?> <data> ...

  10. codevs与noi做题改错本目录

    从2016.2.13开始: 1.  排序超时的问题---------目录:-测试习题 2.  超高精度乘法超时问题-----------目录:高精度计算 算法:快速傅里叶算法. 压位算法 3. 高精度 ...