每当创建一个线程时,系统就会为线程的堆栈(每个线程有它自己的堆栈)保留一个堆栈空间区域,并将一些物理存储器提交给这个已保留的区域。按照默认设置,系统保留1 MB的地址空间并提交2个页面的内存。当然也可以修改这个数,VC中通过添加链接参数/STACK:reserve[.commit]

当保留了这个区域后,系统提交2个物理存储器页面到栈的的顶部的两个页面,栈顶的保留地址为0x08000000。在线程启动运行之前,系统将线程的堆栈指针寄存器设置为指向堆栈区域的最高页面的结尾处(一个非常接近0 x 0 8 1 0 0 0 0 0的地址)。顶部下面的第二个页面称为保护页面。如图16-1

clip_image002

当线程访问保护页面中的存储器时,系统得到关于这个情况的通知。作为响应,系统将提交保护页面下面的一个存储器页面。然后,系统删除保护页面的保护标志,并将它赋予新提交的存储器页面。这种方法使得堆栈存储器只有在线程需要时才会增加。

最终,如果线程的调用树继续扩展,堆栈区域就会变成图1 6 - 2所示的样子。

clip_image004

如图1 6 - 2所示,假定线程的调用树非常深,堆栈指针C P U寄存器指向堆栈内存地址0 x 0 8 0 0 3 0 0 4。这时,当线程调用另一个函数时,系统必须提交更多的物理存储器。但是,当系统将物理存储器提交给0 x 0 8 0 0 1 0 0 0地址上的页面时,但是它们于前面提交的差别是,系统并不将保护属性应用于新的物理存储器页面( 0 x 0 8 0 0 1 0 0 0)。这意味着该堆栈已保留的地址空间全部提交了物理存储器。

最底下的页面总是被保留的,从来不会被提交。

图1 6 - 3显示了堆栈的保留内存区域的样子。

clip_image006

当系统将物理存储器提交给0 x 0 8 0 0 1 0 0 0地址上的页面时,它必须再执行一个操作,即它要引发一个E X C E P T I O N _ S TA C K _ O V E R F L O W 异常处理(在Wi n N T. h 文件中定义为0 x C 0 0 0 0 0 F D)。通过使用结构化异常处理( S E H),你的程序将能得到关于这个异常处理条件的通知,并且能够实现适度恢复。

如果在出现堆栈溢出异常条件之后,线程继续使用该堆栈,那么在0 x 0 8 0 0 1 0 0 0地址上的页面内存均将被使用,同时,该线程将试图访问从0 x 0 8 0 0 0 0 0 0页面中的内存。当该线程访问这个保留的(未提交的)内存时,系统就会引发一个访问违规异常条件。

如果在线程访问该堆栈时引发了这个访问违规异常条件,线程就会陷入很大的麻烦之中。这时,系统就会接管控制权,并终止进程的运行—不仅终止线程的运行,而切终止整个进程的运行。系统甚至不向用户显示一个消息框,整个进程都消失了!

关于线程栈就写这么多巴