在C中检查可用的堆栈大小
我正在将MinGW与GCC 3.4.5(mingw-special vista r3)一起使用。
我的C应用程序使用了很多堆栈,所以我想知道有什么方法可以通过编程方式告诉我们剩余的堆栈数,以便在发现即将用完时可以完全解决这种情况。
如果不是,我们还可以通过其他什么方法来解决可能耗尽堆栈空间的问题?
我不知道我要从多大的堆栈开始,因此也需要以编程方式识别它。
解决方案
回答
假设我们知道完整堆栈的大小,则可以添加一些汇编代码来读取ESP。
如果我们阅读ESP并将其保存在main函数中,则可以将当前的ESP与main中的ESP进行比较,并查看ESP发生了多少变化。这样可以指示我们使用了多少堆栈。
回答
将局部变量的地址从堆栈中取出将起作用。然后,在一个更嵌套的调用中,我们可以减去另一个本地的地址,以找出它们之间的差异
size_t top_of_stack; void Main() { int x=0; top_of_stack = (size_t) &x; do_something_very_recursive(....) } size_t SizeOfStack() { int x=0; return top_of_stack - (size_t) &x; }
如果代码是多线程的,则需要处理每个线程的top_of_stack变量。
回答
雷蒙德·陈(The Old New Thing)对于此类问题有一个很好的答案:
If you have to ask, you're probably doing something wrong.
这是有关堆栈分配的Win32详细信息:MSDN。
如果我们认为自己可能受到堆栈空间的限制,那么几乎可以肯定会受到可用虚拟内存的限制,在这种情况下,我们将需要找到其他解决方案。
我们到底想做什么?
回答
这是我已经放弃的问题。通过大量的黑客攻击和(大多数)祈祷,我们可以获得在给定时间在给定计算机上工作的解决方案。但总的来说,似乎没有做到这一点的体面方法。
我们将必须从程序外部获取堆栈的位置和大小(在Linux上,我们可以从/ proc / <pid> / maps
获取)。在程序中,我们必须以某种方式测试堆栈中的位置。可以使用局部变量,但不能真正保证它们确实在堆栈中。我们也可以尝试通过某些程序集从堆栈指针寄存器中获取值。
因此,现在我们有了堆栈的位置,堆栈的大小和当前位置,并假设我们知道堆栈向哪个方向增长。我们什么时候进入堆栈溢出模式?我们最好不要接近尾声,因为估算值(即局部变量的地址或者来自堆栈指针的值)可能有点过于乐观;在堆栈指针之外寻址内存并不罕见。同样,我们不知道任何给定函数(及其调用的函数)在堆栈上需要多少空间。因此,我们最后必须留出一些空间。
我只能建议我们不要陷入这种混乱状态,并尽量避免进行深度递归。我们可能还想增加堆栈大小;我相信,在Windows上,我们必须将其编译为可执行文件。
回答
检查编译器是否支持stackavail()
回答
也许这仅对Windows平台有用:
在exe文件的PE标头(IMAGE_NT_HEADERS)中,有一些记录,例如:
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; typedef struct _IMAGE_OPTIONAL_HEADER { ... DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; ... }
有一种简单的方法来获取这些值:使用GetModuleHandle(NULL)将为我们提供模块的图像库(句柄),并在其中找到IMAGE_DOS_HEADER结构的地址,这将找到IMAGE_NT_HEADERS结构(imagebase + IMAGE_DOS_HEADER。 e_lfanew)-> IMAGE_NT_HEADERS,然后在其中找到这些字段:SizeOfStackReserve和SizeOfStackCommit。
操作系统将为堆栈分配的最大空间为SizeOfStackReserve。
如果我们考虑尝试此操作,请告诉我,我们将为我们提供帮助。有一种方法可以获取特定点使用的堆栈大小。
回答
在Linux上,我们将调用getrusage并检查返回的struct rusage的
ru_isrss成员(集成的未共享堆栈大小)。
从MINGW站点及其sourceforge站点对补丁的跟踪来看,我发现在2008年5月,围绕getrusage进行了一些补丁,而且似乎已经得到了相当长的支持。我们应该仔细检查MinGW支持多少典型Linux功能方面的任何警告。