谈谈windows线程栈。

当系统创建线程时会为线程预订一块地址空间区域,注意仅仅是预订。默认情况下预定的这块区域的大小是1MB,虽然预订这么多,但是系统并不会给全部区域调拨物理存储器。默认情况下,仅仅为两个页面挑拨。x86系统下每个页面是4KB.其他页面会在访问的时候由系统调拨。这仅仅是在创建线程时,程序员指定CreateThread的第二个参数StackSize为0时才会发挥作用。如果程序员传入的是非零值,那么调拨的物理存储器的数量就是这个非零值。

这两个默认的页面是从哪里来的呢?原来是在链接的时候,系统会将当前编译器中指定的大小写入PE文件中。(PE文件即为exe可执行文件),如果StackSize被指定为0,系统就将PE文件中读出的值作为预订和调拨的页面数。在编译器将预定和挑拨的数量写入PE文件之前,我们可以用两种方法,改变编译器写入的大小。一种是使用/F选项,另一种是使用/STACK选项。

预定空间后,系统会为线程栈的最上方的两个页面调拨物理存储器,esp指向第一个挑拨页面的起始位置。第二个页面也被调拨了页面,它被称为防护页面,具有PAGE_GUARD属性,当线程试图访问防护页面时,系统会得到通知,会为防护页面下方的页面调拨物理存储器,同时,将原来防护页面的PAGE_GUARD的属性去掉,为他下方刚刚调拨的页面指定PAGE_GUARD属性,他就变成了防护页面,此操作,会使防护页面不断下移.不断为页面调拨物理存储器。

由于栈空间默认是1M,(即使再大他也是有限的)当调用函数时,或是局部变量增多时,被调拨的页面越来越多,

使防护页面不断下移,最终他会到达这样一个状态:(3000,2000,1000仅仅起标记作用并不代表实际情况)

此时,线程访问地址为3000的页面,由于它具有保护属性,因此系统会为地址为2000的页面调拨物理存储器,系统会去除页面地址为3000的PAGE_GUARD属性,然后给地址为2000的页面调拨物理存储器。但此时会与原来不同。

原来为页面调拨物理存储器后,会将他上方的页面的保护属性去掉,而把他设为保护属性,现在区别在于,新调拨的页面并没有被设为保护属性。与此同时,系统会抛出EXCEPTION_STACK_OVERFLOW异常,此异常会被系统捕捉,进而通知我们的程序,至于如何响应这个通知,则有程序员自己定义。另外由于系统是在线程访问具有保护属性的页面时,才会为他下方的页面调拨物理存储器,地址2000的页面没有保护属性,所以地址为1000的页面永远也不会被调拨物理存储器。

之所以不为地址为1000的页面,(即栈底)调拨物理存储器,是为了保护进程内的其他数据。因为当栈增长到超过预定的区域后,他会覆盖进程地址空间的其他数据。如果线程在引发堆栈溢出异常后继续使用栈,即访问地址为1000的页面,由于此页面永远不会被调拨物理存储区,而访问未被调拨的页面会引发违规访问异常,此时进程将会被终止。

系统故意不为栈底调拨物理存储器,用来检测程序溢出的情况。但这样仍然不能完全阻止这种情况。如:

DWORD WINAPI threadpro(PVOID)

{

int array[10];

array[2000]=100;    //假设此时array+2000的地址在栈外

return 0;

}

请问此时会发生访问违规,导致进程被终止吗?假设此时array+2000的地址在栈外。

答案是不确定。首先得明确什么情况下会导致访问违规。

前面我们提到,当访问没有被调拨物理存储器的页面时会发生访问违规。此处,array[2000]所处的页面有没有被调拨物理页面我们是不清楚的。当它所处的位置已经被调拨物理存储器,线程会用100覆盖此处的值,导致的结果不确定。如果没有被调拨,就会引发访问违规,导致进程被结束。 通过这个例子可以知道,即使操作系统采取了不为栈底挑拨物理存储器的方法,但仍不能解决此类问题。

再看一个例子:

DWORD WINAPI threadpro(PVOID)

{

int array[1000];

array[900]=100;    //假设array+900位于防护页面之下。

return 0;

}

此例中,线程需要1000*4个字节的栈空间,假设此时没有发生栈溢出的情况,也就是说array+900仍然指向栈内,此时会发生什么情况呢??由于需要大量的栈空间,在开始运行时,系统会为1000*4个字节预订空间,不会为他们全部调拨物理存储器。只有当程序真正访问时才会为他调拨物理存储器。此时为存在一个问题。如果此时程序要访问位于防护页面之下的地址会发生什么情况?

由于位于防护页面之下的栈空间都没有被调拨物理存储器,如果仍然去访问的话,将会造成访问违规。

如何解决这个问题呢?原来是编译器编译程序的时候,编译器会获得系统的页面大小。当它算出线程执行中需要的栈空间大于系统的页面大小时他会自动插入代码来调用检查函数,检查函数的作用就是:为程序要访问的栈空间之前的所有页面调拨物理存储器,确保程序去访问时发生违规访问的情况。如此例,当程序试图访问array+900的栈地址时,系统会为他和他之前的所有栈空间调拨物理存储器。

检查函数一般由编译器厂商用汇编来实现。

《windows核心编程系列》十五谈谈windows线程栈的更多相关文章

  1. 《windows核心编程系列》五谈谈线程基础

    线程基础 与前面介绍的进程一样,线程也有两部分组成.一个是线程内核对象.它是一个数据结构,操作系统用它来管理线程以及用它来存储线程的一些统计信息.另一个是线程栈,用于维护线程执行时所需的所有函数参数和 ...

  2. 《Windows核心编程系列》十一谈谈Windows线程池

    Windows线程池 上一篇博文我们介绍了IO完成端口.得知IO完成端口可以非常智能的分派线程.但是IO完成端口仅对等待它的线程进行分派,创建和销毁线程的工作仍然需要我们自己来做. 我们自己也可以创建 ...

  3. 《windows核心编程系列》一谈谈windows中的错误处理机制

    错误处理 我们写的函数会用返回值表示程序执行的正确与否,使用void,就意味着程序一定不会出错.Bool类型标识true时为真,false时为假.其他类型根据需要可以定义成不同意义. Windows除 ...

  4. 《windows核心编程系列》十七谈谈dll

    DLL全称dynamic linking library.即动态链接库.广泛应用与windows及其他系统中.因此对dll的深刻了解,对计算机软件开发专业人员来说非常重要. windows中所有API ...

  5. 《windows核心编程系列》二谈谈ANSI和Unicode字符集 .

    http://blog.csdn.net/ithzhang/article/details/7916732转载请注明出处!! 第二章:字符和字符串处理 使用vc编程时项目-->属性-->常 ...

  6. 《windows核心编程系列 》六谈谈线程调度、优先级和关联性

    线程调度.优先级和关联性 每个线程都有一个CONTEXT结构,保存在线程内核对象中.大约每隔20ms windows就会查看所有当前存在的线程内核对象.并在可调度的线程内核对象中选择一个,将其保存在C ...

  7. 《windows核心编程系列》四谈谈进程的建立和终止

    第二部分:工作机理 第一章:进程 上一章介绍了内核对象,这一节开始就要不断接触各种内核对象了.首先要给大家介绍的是进程内核对象.进程大家都不陌生,它是资源和分配的基本单位,而进程内核对象就是与进程相关 ...

  8. 《windows核心编程系列》七谈谈用户模式下的线程同步

    用户模式下的线程同步 系统中的线程必须访问系统资源,如堆.串口.文件.窗口以及其他资源.如果一个线程独占了对某个资源的访问,其他线程就无法完成工作.我们也必须限制线程在任何时刻都能访问任何资源.比如在 ...

  9. 《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存

    在应用程序中使用虚拟内存 Windows提供了以下三种机制对内存进行操控: 一:虚拟内存.最适合来管理大型对象数据或大型结构数组. 二:内存映射文件.最适合用来管理大型数据流,以及在同一机 器上运行的 ...

  10. 《Windows核心编程系列》九谈谈同步设备IO与异步设备IO之同步设备IO

    同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...

随机推荐

  1. 获取鼠标位置的几个通用的JS函数

    原文:http://www.open-open.com/code/view/1421401009218 /*两个通用函数,用于获取鼠标相对于整个页面的当前位置*/ function getX(e) { ...

  2. 怎样扩展EasyUI在页面中马上显示选中的本地图片

    在编写前台页面的时候,有时须要将选中的图片夹杂着其它信息一起上传到服务端,在选着本地图片的时候,为了获得更好的效果,须要将该图片显示在页面上. 最初思路有两个.详细例如以下: 1.获取选中文件的二进制 ...

  3. Spark SQL 源代码分析之Physical Plan 到 RDD的详细实现

    /** Spark SQL源代码分析系列文章*/ 接上一篇文章Spark SQL Catalyst源代码分析之Physical Plan.本文将介绍Physical Plan的toRDD的详细实现细节 ...

  4. antd 表单验证

    antd  form 自带方法 /** * 获取 form 自带方法 * getFieldDecorator * getFieldsError * getFieldError * isFieldTou ...

  5. hdu 1879 继续畅通project

    本题链接:pid=1879http://">点击打开链接 本题大意: 输入n行数据.每行数据前两个表示该条路连通的两个村庄的编号,第三个表示修该条路的成本.最后的0或1表示该路未修或已 ...

  6. SQL2012 尝试读取或写入受保护的内存。这通常指示其它内存已损坏

    今天打开SQL2012,突然就连接不了数据库.一開始还以为是某个server崩溃了.结果试了好几个.都还是如此,弹出提演示样例如以下: 尝试读取或写入受保护的内存.这通常仅仅是其它内存已损坏.(Sys ...

  7. PHPCMS中GET标签概述、 get 标签语法、get 标签创建工具、get 调用本系统演示样例、get 调用其它系统演示样例

    一.get 标签概述 通俗来讲,get 标签是Phpcms定义的能直接调用数据库里面内容的简单化.友好化代码,她可调用本系统和外部数据,仅仅有你对SQL有一定的了解,她就是你的绝世好剑!也就是适合熟悉 ...

  8. 【问题记录】LoadRunner 接口压测-json格式报文

    [问题起因] 前段时间,协助其他项目录制接口压测脚本,对方要求请求报文内容实现参数化. 请求方法如下: 直接在Parameter List中新增一个parameter, 将请求报文放入dat文件中.这 ...

  9. 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用

    责任链模式的具体应用   1.业务场景 生产车间中使用的条码扫描,往往一把扫描枪需要扫描不同的条码来处理不同的业务逻辑,比如,扫描投入料工位条码.扫描投入料条码.扫描产出工装条码等,每种类型的条码位数 ...

  10. 3 微信开发本地代理环境的搭建--实现将内网ip映射到外网

    微信公众号的开发,要搭建网站,并且随时都有可能修改网站内容进行调试,这就需要临时外网能返回本地开发环境搭建的项目进行测试,即内网映射到公网,但是好多开发者没有自己的域名和服务器,这里我们先来搭建一个本 ...