有没有办法在运行时确定可用的栈空间?

11

我知道栈大小是固定的,因此我们不能在栈上存储大型对象,而需要转向动态分配内存(例如malloc)。此外,当函数调用嵌套时,栈也会被使用,因此出于这个原因,我们也避免递归函数。在运行时有没有一种方法可以确定已使用多少栈内存以及剩余多少内存?

在这里,我假设在x86体系结构下使用gcc编译器的linux环境。

6个回答

5

有一个pthread API可以确定堆栈的位置:

#include <pthread.h>

void PrintStackInfo (void)
   {   pthread_attr_t Attributes;
       void *StackAddress;
       int StackSize;

   // Get the pthread attributes
   memset (&Attributes, 0, sizeof (Attributes));
   pthread_getattr_np (pthread_self(), &Attributes);

   // From the attributes, get the stack info
   pthread_attr_getstack (&Attributes, &StackAddress, &StackSize);

   // Done with the attributes
   pthread_attr_destroy (&Attributes);

   printf ("Stack top:     %p\n", StackAddress);
   printf ("Stack size:    %u bytes\n", StackSize);
   printf ("Stack bottom:  %p\n", StackAddress + StackSize);
   }

在i386架构中,堆栈从底部开始增长并向顶部延伸。
因此,您知道有($ESP-StackAddress)字节可用。
在我的系统中,我在pthread_create()周围包装了一个函数,因此每个线程都从我的私有函数开始。在该函数中,我按上述描述找到堆栈,然后找到未使用的部分,然后使用独特的模式(或者像我的Massachusetts州Somerville出生的岳父所说的“Patton”)初始化该内存。
然后,当我想知道堆栈已使用了多少时,我从顶部开始搜索并朝向底部找到第一个与我的模式不匹配的值。

2

仅读取%esp,并记住其值会下降。您已经从环境中知道了默认的最大大小,以及您线程的起始点。

与其他一些不可靠的编译器不同,gcc具有出色的汇编支持。


堆段怎么样?我听说栈和堆段是相反增长的?这会影响栈的有效大小吗?如果我错了,请纠正我。 - Vinit Dhatrak
对于你的问题,你可以将其视为正交的..自由存储或堆不是堆栈空间,而“相反增长”并不是一个有用的思考方式。在堆分配(new你的类型)的时候,你的堆栈大小可能会短暂/临时受到影响,但这不是你面临的问题。 - rama-jka toti
这并不像“它们相互对立”那么简单。glibc可以使用mmap()来请求额外的虚拟内存区域,理论上可能会在虚拟内存地址空间的任何位置存在,因此malloc()不一定会使用堆栈将要使用的空间。glibc用于malloc()的分配方法可能会因许多因素而异(例如,它可能使用sbrk(),也可能使用mmap())。 - ehabkost

1
如果您的应用程序需要确保能够使用 X MB 的内存,则通常的方法是在启动时为进程分配它(如果无法分配最低要求,则无法启动)。
当然,这意味着应用程序必须采用自己的内存管理逻辑。

@diciu,我想了解关于堆栈内存的信息,而不是动态分配的内存。堆栈由系统分配,其大小是固定的。 - Vinit Dhatrak
不,它的大小并非固定的。请参考ulimit - 它允许您控制操作系统分配给进程的堆栈大小。 - diciu

1

您可以通过查看/proc/<pid>/smaps来查看堆栈虚拟内存区域的状态。当您使用更多的堆栈空间时,堆栈VMA会自动向下增长。您可以通过检查smaps上堆栈区域的上限与%esp的距离(因为堆栈向下增长)来检查您实际上使用了多少堆栈空间。如果使用了太多的堆栈空间,可能会首先达到由ulimit设置的限制。

但请记住,这些底层细节可能会在没有任何通知的情况下发生变化。不要期望所有Linux内核版本和所有glibc版本都具有相同的行为。我永远不会让我的程序依赖于此信息。


请注意,我只是在谈论 x86 架构的 Linux。 - ehabkost

0

这非常取决于您的操作系统及其内存管理。在Linux上,您可以使用procfs。它类似于/proc/$PID/memory。我现在不在Linux上。

GCC通常为寄存器添加16位(以跳回从中引用的函数上下文)到堆栈帧。通常,您可以通过反汇编程序来获得有关程序编译方式的更多信息。或者使用-S获取汇编代码。


0
Tcl曾经在某个时候进行了堆栈检查,以避免由于无限递归或其他堆栈问题而导致崩溃。但它并不太具有可移植性,例如在某些BSD系统上会崩溃...但你可以尝试找到他们使用的代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接