我们如何获得这些信息?我想这是依赖于操作系统的,我正在运行Windows,所以我的问题是指Windows API。
是否有任何函数可以为我们做这些 - 获取调用线程的剩余堆栈内存?
或者,如果我们能够找到以下详细信息,我们将能够自行计算:
CreateThread
时指定了它)。但是如果它是由我们的程序的操作系统启动的主线程,或者我们没有明确启动的任何其他线程,我们如何找到它?esp
进行检查,或者使用本地变量的地址来获取一个位置。这是出于教育目的,但我想它可以用来阻止递归算法导致堆栈溢出 - 而不是使用任何最大深度限制函数。
答案 0 :(得分:5)
你可以使用NtCurrentTeb(),它可以获得指向TEB的指针。这有NT_TIB作为其第一个成员:
typedef struct _NT_TIB
{
PEXCEPTION_REGISTRATION_RECORD ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
// ....
} NT_TIB, *PNT_TIB;
答案 1 :(得分:1)
没有直接回答OP的问题,而是提到它最后提到的想法:“......它可以用来阻止递归算法导致堆栈溢出 - 而不是使用任何最大深度限制功能“。
Windows API提供方法SetThreadStackGuarantee()
,允许定义最小堆栈大小,以便在抛出堆栈溢出异常时保持可用。此方法与VC运行时库方法_resetstkoflw()
一起使用可以从堆栈溢出中恢复。
有关详细信息,请参阅MSDN上的this。
答案 2 :(得分:1)
获取线程堆栈基址:作为wj32 showed,使用线程信息块的StackBase
。
获取线程堆栈大小:确定线程保留堆栈大小(它的最大大小)是不同的。 StackLimit
显示的是lowest commited address,它可以显示堆栈有多大,而不是它的限制。除了传递CreateThread
标志之外,传递给STACK_SIZE_PARAM_IS_A_RESERVATION
的堆栈大小也不是初始提交大小,而不是保留大小。您的程序的堆栈大小由linker parameter指定,如果未指定,则默认为1MB。所以很可能所有线程都有1MB的堆栈预留。
从stack is a guard page的最后一页开始,您可以想象从StackPage
开始,并检查每个下层堆栈页面VirtualQuery
,以找到将成为堆栈末尾的gaurd页面。这当然完全依赖于实现定义的行为。
获取当前堆栈指针:您可以使用StackLimit
来获取堆栈的最大提交大小,但这与当前指针不同。 esp
显然是当前的堆叠位置,可能高于StackLimit
。
关于reserved vs commited的注释。在Windows中,保留意味着虚拟地址已保留供使用,不能用于其他内容。保留地址不消耗任何物理或虚拟内存。提交后,地址将映射到物理或虚拟内存并可以使用。 Windows用户线程具有固定的堆栈保留大小 - 为堆栈保留地址空间并且不能增加和变量提交大小 - 堆栈将仅在需要时使用(提交)内存。
修改强>
我对检查gaurd页面的想法是行不通的。我编写了一个测试程序,并且保护页面设置为提交限制,因此不起作用。但我确实发现在堆栈上的任何地方运行VirtualQuery
将给出堆栈中最低地址的AllocationBase
,因为保留大小是一次分配的。以下示例显示了此操作:
#include <windows.h>
#include <WinNT.h>
#include <stdio.h>
DWORD GetThreadStackSize()
{
SYSTEM_INFO systemInfo = {0};
GetSystemInfo(&systemInfo);
NT_TIB *tib = (NT_TIB*)NtCurrentTeb();
DWORD_PTR stackBase = (DWORD_PTR)tib->StackBase;
MEMORY_BASIC_INFORMATION mbi = {0};
if (VirtualQuery((LPCVOID)(stackBase - systemInfo.dwPageSize), &mbi, sizeof(MEMORY_BASIC_INFORMATION)) != 0)
{
DWORD_PTR allocationStart = (DWORD_PTR)mbi.AllocationBase;
return stackBase - allocationStart;
}
return 0;
}
DWORD WINAPI ThreadRtn(LPVOID param)
{
DWORD stackSize = GetThreadStackSize();
printf("%d\n", stackSize);
return 0;
}
int main()
{
ThreadRtn(NULL);
HANDLE thread1 = CreateThread(NULL, 65535, ThreadRtn, NULL, 0, NULL);
WaitForSingleObject(thread1, -1);
HANDLE thread2 = CreateThread(NULL, 65535, ThreadRtn, NULL, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
WaitForSingleObject(thread2, -1);
return 0;
}
输出:
1048576 1048576 65536
应该如此。
答案 3 :(得分:-1)
编辑:这是一个非常出色的教育用途问题!有一个upvote为此。在进程或线程开始执行时,堆栈空间是固定的编辑:;我认为你必须指的是动态分配内存的堆(例如,通过malloc()。这里有一个很好的讨论这个问题 on MSDN。我没有看到确切的API调用你是寻找:你将不得不四处寻找;它不会太远。
HTH