每个线程都有自己的栈空间,这个空间大小是在CreateThread时指定的,而主线程的栈则是由xp在创建进程时指定的,在vs2008下设置一个断点,中断程序的执行,可以看到主线程ESP的值为0x00124914,这个指针落在下面这个区域:
基址 | 分配基址 | 分配保护 | 大小 | 状态 | 保护 | 类型 |
0011e000 | 00030000 | 00000004 PAGE_READWRITE | 00012000 | 00001000 MEM_COMMIT | 00000004 PAGE_READWRITE | 00020000 MEM_PRIVATE |
这块空间的上限是0x0013 0000,这个值与我们读出来的NT_TIB结构体里面的StackBase的值是一致的,也就是说主线程的栈空间从0x0013 0000开始往下增长。但是在NT_TIB里面的StackLimit值却只有0x00000 a000,很显然,可用的栈空间是不只这么一点的。
注意到上述内存区域的分配基址与这个内存区域的起始地址是不一样的,看看完整的内存块:
基址 | 分配基址 | 分配保护 | 大小 | 状态 | 保护 | 类型 |
00030000 | 00030000 | 00000004 PAGE_READWRITE | 000ed000 | 00002000 MEM_RESERVE | 00000000 | 00020000 MEM_PRIVATE |
0011d000 | 00030000 | 00000004 PAGE_READWRITE | 00001000 | 00001000 MEM_COMMIT | 00000104 PAGE_READWRITE PAGE_GUARD | 00020000 MEM_PRIVATE |
0011e000 | 00030000 | 00000004 PAGE_READWRITE | 00012000 | 00001000 MEM_COMMIT | 00000004 PAGE_READWRITE | 00020000 MEM_PRIVATE |
也就是说windows分配这块空间的时候是从0x0003 0000 ~ 0x0013 0000的,恰好1M,这也是默认情况下主线程的栈空间大小。但由于我们实际还没有使用这么大的栈空间,所以0x0003 0000开始的这一块空间的状态是MEM_RESERVE。
再看中间的这个内存区域,它的保护标志多了个PAGE_GUARD,MSDN里面这样解释这个标志位:
Pages in the region become guard pages. Any attempt to access a guard page causes the system to raise a STATUS_GUARD_PAGE_VIOLATION exception and turn off the guard page status. Guard pages thus act as a one-time access alarm. For more information, see Creating Guard Pages.
When an access attempt leads the system to turn off guard page status, the underlying page protection takes over.
从这里大致可以知道,当ESP向下增长超过目前已经分配的空间,此时将触发一个异常,然后windows再调整这几块内存页的属性。当然,ESP的增长不能超过整个栈的大小,否则程序就无法继续了。
在PAGE_GUARD的指引下,我们可以很轻易地从这些内存块中找出其它的栈:
基址 | 分配基址 | 分配保护 | 大小 | 状态 | 保护 | 类型 |
00390000 | 00390000 | 00000004 PAGE_READWRITE | 0003c000 | 00002000 MEM_RESERVE | 00000000 | 00020000 MEM_PRIVATE |
003cc000 | 00390000 | 00000004 PAGE_READWRITE | 00001000 | 00001000 MEM_COMMIT | 00000104 PAGE_READWRITE PAGE_GUARD | 00020000 MEM_PRIVATE |
003cd000 | 00390000 | 00000004 PAGE_READWRITE | 00003000 | 00001000 MEM_COMMIT | 00000004 PAGE_READWRITE | 00020000 MEM_PRIVATE |
这个栈属于另外一个线程,其大小为0x0003 c000,实际只使用了0x0000 3000。
下面是另一个线程的栈:
基址 | 分配基址 | 分配保护 | 大小 | 状态 | 保护 | 类型 |
00c00000 | 00c00000 | 00000004 PAGE_READWRITE | 000f9000 | 00002000 MEM_RESERVE | 00000000 | 00020000 MEM_PRIVATE |
00cf9000 | 00c00000 | 00000004 PAGE_READWRITE | 00001000 | 00001000 MEM_COMMIT | 00000104 PAGE_READWRITE PAGE_GUARD | 00020000 MEM_PRIVATE |
00cfa000 | 00c00000 | 00000004 PAGE_READWRITE | 00006000 | 00001000 MEM_COMMIT | 00000004 PAGE_READWRITE | 00020000 MEM_PRIVATE |
总共三个栈空间,和程序里面的三个线程一一对应。
编缉推荐阅读以下文章
版权与免责声明
1、本站所发布的文章仅供技术交流参考,本站不主张将其做为决策的依据,浏览者可自愿选择采信与否,本站不对因采信这些信息所产生的任何问题负责。
2、本站部分文章来源于网络,其版权为原权利人所有。由于来源之故,有的文章未能获得作者姓名,署“未知”或“佚名”。对于这些文章,有知悉作者姓名的请告知本站,以便及时署名。如果作者要求删除,我们将予以删除。除此之外本站不再承担其它责任。
3、本站部分文章来源于本站原创,本站拥有所有权利。
4、如对本站发布的信息有异议,请联系我们,经本站确认后,将在三个工作日内做出修改或删除处理。
请参阅权责声明!