44

我将 MinGW 与 GCC 3.4.5(mingw-special vista r3)一起使用。

我的 C 应用程序使用了很多堆栈,所以我想知道有什么方法可以通过编程方式告诉我还剩下多少堆栈,这样如果我发现我即将用完,我就可以干净地处理这种情况。

如果不是,您还有什么其他方法可以解决可能耗尽堆栈空间的问题?

我不知道我将从多大的堆栈开始,所以也需要以编程方式识别它。

4

9 回答 9

30

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 -s可以通过ulimit -a堆栈大小的行获取相同的信息。还可以查看setrlimit允许设置限制的功能。但是正如其他答案中提到的那样,如果您需要调整堆栈,那么您可能应该重新考虑您的设计。如果你想要一个大数组,为什么不从堆中取出内存呢?

于 2011-05-03T03:37:29.410 回答
18

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

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 变量。

于 2008-09-10T12:02:00.633 回答
9

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

于 2008-09-10T13:40:50.003 回答
6

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

于 2008-09-10T12:01:09.433 回答
5

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

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

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

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

于 2008-09-10T12:47:35.267 回答
4

也许这仅对 Windows 平台有帮助:

在您的 exe 的 PE 标头 (IMAGE_NT_HEADERS) 中有一些记录,例如:

typedef 结构 _IMAGE_NT_HEADERS {
    双字签名;
    IMAGE_FILE_HEADER 文件头;
    IMAGE_OPTIONAL_HEADER32 可选标题;
} IMAGE_NT_HEADERS32,*PIMAGE_NT_HEADERS32;

typedef 结构 _IMAGE_OPTIONAL_HEADER {
    ...
    DWORD SizeOfStackReserve;
    DWORD SizeOfStackCommit;
    ...
}

有一种获取这些值的简单方法:使用 GetModuleHandle(NULL) 将为您提供模块的镜像库(句柄),您将在其中找到 IMAGE_DOS_HEADER 结构的地址,该结构将帮助您找到 IMAGE_NT_HEADERS 结构(imagebase+IMAGE_DOS_HEADER. e_lfanew) -> IMAGE_NT_HEADERS,在那里你会找到这些字段:SizeOfStackReserveSizeOfStackCommit

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

如果您考虑尝试这样做,请告诉我,我会为您提供帮助。有一种方法可以获取在某个点使用的堆栈大小。

于 2008-09-11T13:55:22.880 回答
3

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

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

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

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

你到底想做什么?

于 2008-09-10T12:38:18.407 回答
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 上找到我在尝试修复代码中的错误时提出的另一个问题:算术运算导致不安全 C# 中的溢出在此处输入链接描述

于 2012-01-03T17:44:37.270 回答
2

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

从 MINGW 站点及其 sourceforge 站点对补丁的跟踪中,我看到在 2008 年 5 月,围绕 getrusage 进行了一些补丁,看起来它已经被普遍支持了很长一段时间。您应该仔细检查有关 MinGW 支持多少典型 Linux 功能的任何警告。

于 2008-09-12T17:34:02.097 回答