每一个线程都有一个叫 TEB(Thread Environment Block)
的线程环境块数据结构,这个结构中有一个叫做 NT_TIB
的结构,它里面有两个字段分别为 StackBase
和 StackLimit
,前面叫做栈基址,也就是栈顶,后者叫做 栈边界 ,因为栈空间是向小地址增长的,所以用 StackBase - StackLimit
就能算出所谓的栈内存大小,接下来我们用 windbg 演示一下。
大家可以用 windbg 直接调试你的程序,我手里刚好有一个 dump 文件,这里就从主线程看起吧。
- 0:000> ~0s
- ntdll!NtWaitForSingleObject+0x14:
- 00007ffe`28b9fa74 c3 ret
- 0:000> !teb
- TEB at 000000b4da0ae000
- ExceptionList: 0000000000000000
- StackBase: 000000b4d9fa0000
- StackLimit: 000000b4d9f98000
- SubSystemTib: 0000000000000000
- FiberData: 0000000000001e00
- ArbitraryUserPointer: 0000000000000000
- Self: 000000b4da0ae000
- EnvironmentPointer: 0000000000000000
- ClientId: 0000000000000c74 . 00000000000041a4
- RpcHandle: 0000000000000000
- Tls Storage: 000001f90edad1d0
- PEB Address: 000000b4da0ad000
- LastErrorValue: 0
- LastStatusValue: 103
- Count Owned Locks: 0
- HardErrorMode: 0
-
从输出看两个值分别为:StackBase=000000b4d9fa0000
和 StackLimit=000000b4d9f98000
,那它的大小就是 32768byte = 32k
。
- 0:000> ? 000000b4d9fa0000 - 000000b4d9f98000
- Evaluate expression: 32768 = 00000000`00008000
-
这里要提醒一下,操作系统的内存页是 4k
为一个粒度,也就说所有的输出结果肯定是4k
的倍数,比如当前栈空间就是 8
个内存页。
NT_TIB
结构刚才用的是快捷命令,接下来我们直接查看 _TEB 结构下的 NT_TIB
struct 结构变量。
- 0:000> .thread
- Implicit thread is now 000000b4`da0ae000
- 0:000> dt _NT_TIB 000000b4`da0ae000
- combase!_NT_TIB
- +0x000 ExceptionList : (null)
- +0x008 StackBase : 0x000000b4`d9fa0000 Void
- +0x010 StackLimit : 0x000000b4`d9f98000 Void
- +0x018 SubSystemTib : (null)
- +0x020 FiberData : 0x00000000`00001e00 Void
- +0x020 Version : 0x1e00
- +0x028 ArbitraryUserPointer : (null)
- +0x030 Self : 0x000000b4`da0ae000 _NT_TIB
-
可以看到,上面的两个值和 !teb
显示的一模一样。