用C检查可用的堆栈大小

Pau*_*ves 33 c stack mingw

我正在使用MinGW和GCC 3.4.5(mingw-special vista r3).

我的C应用程序使用了大量的堆栈,所以我想知道是否有任何方法可以以编程方式告诉剩余多少堆栈,因此如果我发现我即将耗尽,我可以干净地处理这种情况.

如果没有其他方法可以解决可能耗尽堆栈空间的问题?

我不知道我将从哪个堆栈大小开始,因此需要以编程方式识别.

pho*_*xis 21

getrusage函数可以获得当前的用法.(见man getrusage).

getrlimitLinux中,将有助于获取与堆栈大小RLIMIT_STACK参数.

#include <sys/resource.h>
int main (void)
{
  struct rlimit limit;

  getrlimit (RLIMIT_STACK, &limit);
  printf ("\nStack Limit = %ld and %ld max\n", limit.rlim_cur, limit.rlim_max);
}
Run Code Online (Sandbox Code Playgroud)

请看一下man getrlimit.同样的信息可以通过以下方式获取ulimit -sulimit -a堆栈大小排.还要看一下setrlimit可以设置限制的功能.但正如在其他答案中提到的,如果你需要调整堆栈,那么你可能应该重新考虑你的设计.如果你想要一个大数组,为什么不从堆中获取内存?

  • `getrusage()`对Linux上的堆栈大小不起作用."`ru_isrss(unmaintained)这个字段目前在Linux上尚未使用."(http://linux.die.net/man/2/getrusage).我不知道什么时候会这样,但内核2.6.28也是如此. (4认同)
  • @phoxis :`getrlimit (RLIMIT_STACK, &amp;limit)`似乎给出了总堆栈大小,而不是剩余的可用堆栈大小。 (2认同)
  • @phoxis:这就是我要说的。这是总堆栈大小。在 ᴏᴘ 的情况下,仅获得剩余的才是有用的。 (2认同)

Rob*_*ker 14

将局部变量的地址从堆栈中取出将起作用.然后在更嵌套的调用中,您可以减去另一个本地的地址以找到它们之间的差异

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;
} 
Run Code Online (Sandbox Code Playgroud)

如果你的代码是多线程的,那么你需要处理每个线程存储top_of_stack变量.

  • 我喜欢这个答案,但在不知道前面堆栈的大小的情况下,我无法判断我是否要炸毁它。 (3认同)
  • 特别是在MinGW上这可能没问题.通常,程序的堆栈不保证是连续的.实现(例如,没有虚拟内存的实现)根据需要分配堆栈块并将它们链接在一起是合法的.当然,如果你的平台做到了这一点,那么程序甚至可能没有默认的最大堆栈大小:你可以继续运行直到你的可用内存耗尽.但无论如何都有一个限制的一个很好的理由是防止失控的递归通过耗尽内存来取消整个系统. (3认同)
  • 警告:某些平台(特别是嵌入式系统)不在堆栈上分配数据(仅函数返回地址存储在堆栈上)。在这种情况下,局部变量的地址是没有意义的。 (2认同)

dmi*_*gov 9

检查你的编译器是否支持stackavail()


Ski*_*izz 6

Raymond Chen(The Old New Thing)对这类问题有一个很好的答案:

如果你不得不问,你可能做错了什么.

这里是关于堆栈分配的一些Win32细节:MSDN.

如果您认为可能受到堆栈空间的限制,您几乎肯定会受到可用虚拟内存的限制,在这种情况下,您需要找到不同的解决方案.

你究竟想做什么?

  • 虽然如果你曾尝试编写真正的,真正可移植的代码,你会发现"你总是要问,而且你总是做错了,因为没有"堆栈使用"的可移植概念,但它是程序员的责任不要使用太多堆栈.所以最好只加入沉默的阴谋,编写一个功能测试,你希望在实践中消耗尽可能多的堆栈,并留给平台集成商担心". (42认同)
  • 问题不是"我应该检查堆栈大小吗?" 它是"如何检查堆栈大小?" (6认同)
  • 一个(不太好)的例子是: void subroutine(int i) { char foo[20000]; 我++; if (i &lt; 1000) 子程序(i); } (2认同)

Nat*_*man 5

假设您知道完整堆栈的大小,您可能会添加一些汇编代码来读取 ESP。
如果您阅读 ESP 并将其保存在 main 函数中,您可以将当前的 ESP 与您在 main 中的 ESP 进行比较,并查看 ESP 发生了多少变化。这会给你一个指示你使用了多少堆栈。


mwe*_*den 5

这是一个我已经放弃的问题。通过大量的黑客攻击和(主要是)祈祷,您可以获得在给定时间在给定机器上运行的解决方案。但总的来说,似乎没有合适的方法来做到这一点。

您必须从程序外部获取堆栈位置和大小(在 Linux 上,您可能从 获取/proc/<pid>/maps)。在你的程序中,你必须以某种方式测试你在堆栈中的位置。使用局部变量是可能的,但不能真正保证它们实际上位于堆栈上。您还可以尝试使用某些程序集从堆栈指针寄存器获取值。

现在您已经知道了堆栈的位置、其大小和当前位置,并且您假设您知道堆栈的增长方向。你什么时候进入堆栈溢出模式?你最好不要在接近结束时这样做,因为你的估计(即局部变量的地址或堆栈指针的值)可能有点过于乐观;对堆栈指针之外的内存进行寻址并不罕见。此外,您不知道任何给定函数(及其调用的函数)需要多少堆栈空间。所以最后你必须留出一些空间。

我只能建议你不要陷入这种混乱,并尽量避免非常深的递归。您可能还想增加堆栈大小;我相信,在 Windows 上你必须将其编译成可执行文件。