在C中检查可用的堆栈大小
我使用MinGW和GCC 3.4.5(mingw-special vista r3)。
我的C应用程序使用了大量的堆栈,所以我想知道有没有什么办法可以通过程序来告诉我剩下多less堆栈,所以如果我发现即将耗尽,我可以干净地处理这种情况。
如果没有其他方法可以解决堆栈空间可能用尽的问题?
我不知道我会从什么大小的堆栈开始,所以需要以编程方式来识别。
雷蒙德·陈( 旧新事物 )对这样的问题有一个很好的答案:
如果你不得不问,你可能做错了什么。
以下是有关堆栈分配的一些Win32详细信息: MSDN 。
如果您认为可能会受到堆栈空间的限制,那么几乎肯定会受到可用虚拟内存的限制,在这种情况下,您将需要find不同的解决scheme。
你到底在做什么?
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 getrlimit
。 相同的信息可以通过ulimit -s
或ulimit -a
堆栈大小行获取。 也看看setrlimit
函数,可以设置限制。 但正如在其他答案中提到的,如果你需要调整堆栈,那么可能你应该考虑你的devise。 如果你想要一个大数组,为什么不从堆中取出内存?
从堆栈中取出局部variables的地址是可行的。 然后在一个更嵌套的调用中,您可以减去另一个本地的地址以查找它们之间的差异
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; }
如果你的代码是multithreading的,那么你需要处理在每个线程的基础上存储top_of_stackvariables。
检查你的编译器是否支持stackavail()
对于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的另一个问题上find,当我试图修复一个代码中的错误时:问题算术运算导致溢出在不安全的C# input链接描述在这里
假设你知道堆栈的大小,你可以添加一些汇编代码来读取ESP。
如果您阅读ESP并将其保存在主function中,则可以将当前的ESP与主要的ESP进行比较,看看ESP有多less变化。 这会给你一个你使用多less堆栈的指示。
这是我放弃的一个问题。 通过大量的黑客攻击(主要是)祈祷,你可以得到一个解决scheme,在给定的机器在给定的时间。 但总的来说,似乎没有体面的方式来做到这一点。
您将不得不从程序外部获取堆栈位置和大小(在Linux上,您可以从/proc/<pid>/maps
find它)。 在你的程序中,你必须以某种方式testing你在堆栈中的位置。 使用局部variables是可能的,但是并不能保证它们实际上在堆栈中。 您也可以尝试从某个程序集的堆栈指针寄存器中获取值。
所以,现在你有堆栈的位置,大小和当前位置,并且你假设你知道堆栈在哪个方向增长。 你什么时候进入堆栈溢出模式? 你最好不要靠近最后,因为你的估计(即从栈指针的本地variables或值的地址)可能有点过于乐观; 寻址堆栈指针之外的内存并不罕见。 另外,对于任何给定函数(及其调用的函数)所需的堆栈空间,您都不知道。 所以你不得不在最后留下一些空间。
我只能build议你不要进入这个混乱,并尝试避免非常深的recursion。 你可能也想增加你的堆栈大小; 在Windows上,你必须把它编译成可执行文件,我相信。
也许这只会帮助Windows平台:
在您的exe的PE头(IMAGE_NT_HEADERS)中有一些logging,如:
typedef struct _IMAGE_NT_HEADERS { DWORD签名; 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结构,这将帮助你findIMAGE_NT_HEADERS结构(imagebase + IMAGE_DOS_HEADER。 e_lfanew) – > IMAGE_NT_HEADERS,在那里你会发现这些字段: SizeOfStackReserve和SizeOfStackCommit 。
OS将为您的堆栈分配的最大空间量是SizeOfStackReserve。
如果你考虑这个,让我知道,我会协助你。 有一种方法可以获得某个点使用的堆栈的大小。
在Linux上,您可以调用getrusage并检查返回的struct rusage的ru_isrss成员(整数非共享堆栈大小)。
从MINGW站点及其sourceforge站点追踪补丁的情况看,2008年5月,围绕getrusage进行了一些修补,似乎已经得到了相当长时间的支持。 您应该仔细检查MinGW支持多less典型Linuxfunction的任何警告。