用C检查可用的堆栈大小

时间:2008-09-10 11:53:16

标签: c stack mingw

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

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

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

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

9 个答案:

答案 0 :(得分:21)

getrusage功能可以获得当前的使用情况。 (见man getrusage)。

Linux中的getrlimit将有助于使用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);
}

请查看man getrlimitulimit -sulimit -a堆栈大小行可以获取相同的信息。 还可以查看允许设置限制的setrlimit函数。 但正如在其他答案中提到的,如果你需要调整堆栈,那么你可能应该重新考虑你的设计。如果你想要一个大数组,为什么不从堆中获取内存呢?

答案 1 :(得分: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;
} 

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

答案 2 :(得分:9)

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

答案 3 :(得分:6)

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

  

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

以下是有关堆栈分配的一些Win32详细信息:MSDN

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

你究竟想做什么?

答案 4 :(得分:3)

假设您知道完整堆栈的大小,您可以添加一些汇编代码来读取ESP 如果您在主函数中读取ESP并将其保存在一旁,则可以将当前ESP与主要ESP进行比较,并查看ESP已更改的数量。这会让你知道你使用了多少筹码。

答案 5 :(得分:3)

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

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

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

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

答案 6 :(得分:3)

对于Windows:我在使用Kernel32.dll的VirtualQuery函数之前已经完成了这个。我只在C#中有一个例子,但它演示了这个技术:

public static class StackManagement
    {
        [StructLayout(LayoutKind.Sequential)]
        struct MEMORY_BASIC_INFORMATION
        {
            public UIntPtr BaseAddress;
            public UIntPtr AllocationBase;
            public uint AllocationProtect;
            public UIntPtr RegionSize;
            public uint State;
            public uint Protect;
            public uint Type;
        };

        private const long STACK_RESERVED_SPACE = 4096 * 16;

        public unsafe static bool CheckForSufficientStack(UInt64 bytes)
        {
            MEMORY_BASIC_INFORMATION stackInfo = new MEMORY_BASIC_INFORMATION();
            UIntPtr currentAddr = new UIntPtr(&stackInfo);
            VirtualQuery(currentAddr, ref stackInfo, sizeof(MEMORY_BASIC_INFORMATION));

            UInt64 stackBytesLeft = currentAddr.ToUInt64() - stackInfo.AllocationBase.ToUInt64();

            return stackBytesLeft > (bytes + STACK_RESERVED_SPACE);
        }

        [DllImport("kernel32.dll")]
        private static extern int VirtualQuery(UIntPtr lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
    }

顺便说一句:这个代码也可以在StackOverflow上的另一个问题上找到,当我尝试修复代码中的错误时我问过:Arithmetic operation resulted in an overflow in unsafe C# enter link description here

答案 7 :(得分:2)

这可能仅对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) - &gt; IMAGE_NT_HEADERS,你会在那里找到这些字段: SizeOfStackReserve SizeOfStackCommit

操作系统为堆栈分配的最大空间量是SizeOfStackReserve。

如果你考虑尝试这个,请告诉我,我会帮助你。有一种方法可以获得某一点使用的堆栈大小。

答案 8 :(得分:2)

在Linux上,你会调用getrusage并检查返回的struct rusage ru_isrss成员(整数非共享堆栈大小)。

从MINGW网站及其sourceforge网站对补丁的跟踪,我看到在2008年5月围绕getrusage进行了一些补丁,看起来它已经被支持了很长一段时间。您应该仔细检查MinGW支持多少典型Linux功能的注意事项。