문제
저는 GCC 3.4.5(mingw-special vista r3)와 함께 MinGW를 사용하고 있습니다.
내 C 응용 프로그램은 많은 스택을 사용하므로 스택이 얼마나 남았는지 프로그래밍 방식으로 알려서 스택이 부족할 경우 상황을 깔끔하게 처리할 수 있는 방법이 있는지 궁금합니다.
그렇지 않다면 잠재적으로 스택 공간이 부족해지는 문제를 해결하는 다른 방법은 무엇입니까?
어떤 크기의 스택으로 시작할지 모르므로 프로그래밍 방식으로도 이를 식별해야 합니다.
다른 팁
getrusage 함수는 현재 사용량을 알려줍니다.(보다 man getrusage
).
그만큼 getrlimit
Linux에서는 다음을 사용하여 스택 크기를 가져오는 데 도움이 됩니다. 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
한계를 설정할 수 있는 기능입니다.그러나 다른 답변에서 언급했듯이 스택을 조정해야 한다면 디자인을 다시 고려해야 할 것입니다.큰 배열을 원한다면 힙에서 메모리를 가져오는 것은 어떨까요?
스택에서 지역 변수의 주소를 가져오는 것이 작동합니다.그런 다음 더 중첩된 호출에서 다른 로컬 주소를 빼서 둘 사이의 차이점을 찾을 수 있습니다.
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 변수 저장을 처리해야 합니다.
컴파일러가 stackavail()을 지원하는지 확인하세요.
전체 스택의 크기를 알고 있다고 가정하면 ESP를 읽기 위해 일부 어셈블리 코드를 추가할 수 있습니다.
ESP를 읽고 메인 함수에 따로 저장하면 현재 ESP를 메인에 있는 ESP와 비교하여 ESP가 얼마나 변경되었는지 확인할 수 있습니다.그러면 얼마나 많은 스택이 사용되었는지 알 수 있습니다.
이건 제가 포기한 문제입니다.많은 해킹과 (주로) 기도를 통해 특정 머신에서 특정 시간에 작동하는 솔루션을 얻을 수 있습니다.그러나 일반적으로 이를 수행하는 적절한 방법은 없는 것 같습니다.
프로그램 외부에서 스택 위치와 크기를 얻어야 합니다(Linux에서는 다음에서 얻을 수 있습니다). /proc/<pid>/maps
).프로그램에서 어떻게든 스택의 위치를 테스트해야 합니다.지역 변수를 사용하는 것은 가능하지만 실제로 스택에 있다는 보장은 없습니다.일부 어셈블리를 사용하여 스택 포인터 레지스터에서 값을 가져오려고 시도할 수도 있습니다.
이제 스택의 위치, 크기 및 현재 위치를 알았으며 스택이 어느 방향으로 성장하는지 알고 있다고 가정합니다.언제 스택 오버플로 모드로 전환됩니까?당신의 추정(예:지역 변수의 주소 또는 스택 포인터의 값)은 아마도 너무 낙관적일 것입니다.스택 포인터 너머의 메모리 주소를 지정하는 것은 드문 일이 아닙니다.또한 특정 함수(및 호출하는 함수)가 스택에 얼마나 많은 공간을 필요로 하는지 전혀 알 수 없습니다.그래서 마지막에 꽤 많은 공간을 남겨두어야 합니다.
나는 이 혼란에 빠지지 말고 매우 깊은 재귀를 피하려고 노력하라고 조언할 뿐입니다.스택 크기를 늘리고 싶을 수도 있습니다.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#에서 산술 연산으로 인해 오버플로가 발생했습니다.여기에 링크 설명을 입력하세요
아마도 이것은 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_NT_HEADERS 구조(imagebase+IMAGE_DOS_HEADER.e_lfanew) -> IMAGE_NT_HEADERS를 찾는 데 도움이 되는 IMAGE_DOS_HEADER 구조를 찾을 수 있는 주소가 제공됩니다. 해당 필드를 찾을 것입니다: SizeOfStackReserve 그리고 SizeOfStack커밋.
OS가 스택에 할당하는 최대 공간은 SizeOfStackReserve입니다.
시도해 볼 것을 고려하고 계시다면 알려주시면 도와드리겠습니다.특정 지점에서 사용되는 스택의 크기를 구하는 방법이 있습니다.
Linux에서는 getrusage를 호출하고 반환 된 Struct Rusage의 Ru_isrss 회원 (적분하지 않은 스택 크기)을 확인합니다.
MINGW 사이트와 해당 sourceforge 사이트의 패치 추적을 통해 2008년 5월에 getrusage 관련 패치가 일부 이루어졌으며 꽤 오랫동안 일반적으로 지원되어 온 것으로 보입니다.MinGW에서 지원되는 일반적인 Linux 기능의 양과 관련된 주의 사항을 주의 깊게 확인해야 합니다.