读书笔记(十). 16. 线程的堆栈
第16章线程的堆栈
系统会在你自己进程的地址空间中保留一些区域。
每当创建一个线程时,系统就会为线程的堆栈(每个线程有它自己的堆栈)保留一个堆栈空间区域,并将一些物理存储器提交给这个已保留的区域。
当调用CreateThread或_beginthreadex函数时,可以重载原先提交的内存数量。这两个函数都有一个参数,可以用来重载原先提交给堆栈的地址空间的内存数量。如果设定这个参数为0,那么系统将使用/STACK开关指明的已提交的堆栈大小值。
图16-1显示了在页面大小为4KB的计算机上的一个堆栈区域的样子(保留的起始地址是0x08000000)。该堆栈区域和提交给它的所有物理存储器均拥有页面保护属性PAGE_READWRITE
当保留了这个区域后,系统将物理存储器提交给区域的顶部的两个页面。在允许线程启动运行之前,系统将线程的堆栈指针寄存器设置为指向堆栈区域的最高页面的结尾处
从顶部向下的第二个页面称为保护页面。
每当线程试图访问保护页面中的存储器时,系统就会得到关于这个情况的通知。作为响应,系统将提交紧靠保护页面下面的另一个存储器页面。然后,系统从当前保护页面中删除保护页面的保护标志,并将它赋予新提交的存储器页面。这种方法使得堆栈存储器只有在线程需要时才会增加。
当该线程试图访问这个保留的(未提交的)内存时,系统就会引发一个访问违规异常条件。如果在线程试图访问该堆栈时引发了这个访问违规异常条件,线程就会陷入很大的麻烦之中。这时,系统就会接管控制权,并终止进程的运行
C/C++运行期库包含一个堆栈检查函数。当编译源代码时,编译器将在必要时自动生成对该函数的调用。堆栈检查函数的作用是确保页面被适当地提交给线程的堆栈。
如果函数需要的堆栈空间大于目标系统的页面大小,编译器将自动插入对堆栈检查函数的调用。