2008-09-10 4 views
28

Я использую MinGW с GCC 3.4.5 (специальная перспектива r3).Проверка размера стека в C

В моем приложении C используется много стека, поэтому я задавался вопросом, есть ли какой-либо способ, который я могу сказать программным образом, сколько осталось стека, чтобы я мог чисто обрабатывать ситуацию, если обнаружил, что я собираюсь закончиться.

Если бы не какие-либо другие способы решения проблемы потенциального истечения пространства стека?

Я понятия не имею, какой размер стека я начну с этого, и нужно будет также определить это программно.

ответ

9

Raymond Chen (The Old New Thing) имеет хороший ответ на такого рода вопрос:

Если вы должны спросить, вы, вероятно, делаете что-то неправильно.

Вот некоторые данные Win32 о распределении стека: MSDN.

Если вы считаете, что можете ограничить пространство в стеке, вы почти наверняка будете ограничены доступной виртуальной памятью, и в этом случае вам нужно будет найти другое решение.

Что именно вы пытаетесь сделать?

+1

A (не большой) пример: void подпрограмма (int i) { char foo [20000]; i ++; , если (i <1000) подпрограмма (i); } – 2008-09-10 12:57:50

2

Предполагая, что вы знаете размер полного стека, вы, вероятно, можете добавить код сборки для чтения ESP.
Если вы прочитали ESP и сохранили его в основной функции, вы можете сравнить текущий ESP с ESP, который у вас есть на главном экране, и посмотреть, как сильно изменился ESP. Это даст вам представление о том, сколько стека вы используете.

13

Использование адреса локальной переменной из стека будет работать. Затем в более вложенном вызове вы можете вычесть адрес других местного найти разницу между ними

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 на основе каждого потока ,

+1

Мне нравится этот ответ, но я не знаю размера штабеля. У меня нет способа сказать, собираюсь ли я взорвать его. – 2008-09-10 12:18:16

+0

Будет установлен размер стека по умолчанию для потоков в вашей системе. В Windows это 1 МБ адресного пространства. Вы можете контролировать это, если создаете свои собственные потоки. Хотя, как указывает Skizz, лучше не беспокоиться о точном пределе! – 2008-09-10 13:16:13

+3

В MinGW это может быть хорошо. В общем, стек для программы не гарантированно соприкасается. Это законно для реализации (например, у которой нет виртуальной памяти) для распределения блоков стека по мере необходимости и объединения их друг в друга. Конечно, если ваша платформа это делает, то для программы может даже не быть максимальный размер стека по умолчанию: вы можете просто продолжать, пока не закончите свободную память. Но хорошей причиной для того, чтобы иметь ограничение в любом случае, является предотвращение безудержной рекурсии от снятия всей системы за счет исчерпания памяти. – 2009-11-02 12:15:46

2

Это проблема, с которой я отказался. С большим количеством взломов и (в основном) молитв вы можете получить решение, которое работает в данный момент на данной машине. Но в целом, похоже, нет достойного способа сделать это.

Вам нужно будет получить положение и размер стека вне программы (на Linux вы можете получить ее от /proc/<pid>/maps). В вашей программе вы должны как-то проверить, где вы находитесь в стеке. Использование локальных переменных возможно, но нет реальной гарантии, что они действительно находятся в стеке. Вы также можете попытаться получить значение из регистра указателя стека с некоторой сборкой.

Итак, теперь у вас есть местоположение стека, его размер и текущее положение, и вы предполагаете, что знаете, в каком направлении растет стек. Когда вы переходите в режим переполнения стека? Лучше не делать этого близко к концу, потому что ваша оценка (то есть адрес локальной переменной или значения из указателя стека), вероятно, немного оптимистична; это не редкость для адресации памяти за указателем стека. Кроме того, вы не имеете понятия о том, сколько места в стеке требуется для каждой данной функции (и функций, которые она вызывает). Таким образом, вам нужно будет оставить совсем немного места в конце.

Я могу только посоветовать вам не попасть в этот беспорядок и попытаться избежать очень глубокой рекурсии.Вы также можете увеличить размер стека; По-моему, в Windows вы должны скомпилировать это в исполняемый файл.

9

проверить, если ваш компилятор поддерживает stackavail()

2

может быть, это поможет для платформы Windows, только:

в заголовке 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 (база данных) + IMAGE_DOS_HEADER.e_lfanew) -> IMAGE_NT_HEADERS, и там вы найдете эти поля: SizeOfStackReserve и SizeOfStackCommit.

Максимальное пространство, которое выделяет ОС для вашего стека, - SizeOfStackReserve.

Если вы попробуете это, сообщите мне, и я помогу вам. Существует способ получить размер стека, используемый в определенной точке.

2

В Linux вы должны называть getrusage и проверять возвращаемый объект struct rusage ru_isrss (интегральный размер нераспределенного стека).

С сайта MINGW и отслеживания его патчей на сайте sourceforge я вижу, что в мае 2008 года было сделано исправление, связанное с getrusage, и похоже, что оно в течение длительного времени поддерживалось. Вы должны тщательно проверить любые оговорки в отношении того, какая часть типичных функций Linux поддерживается MinGW.

18

Функция 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, которая позволит установить пределы. Но, как указано в других ответах, если вам нужно настроить стек, то, вероятно, вам стоит подумать о своем дизайне. Если вы хотите большой массив, почему бы не взять память из кучи?

3

Для окон: Я сделал это перед использованием функции VirtualQuery из Kernel32.dll. У меня есть только пример в 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); 
    } 

BTW: Этот код также можно найти на StackOverflow на другой вопрос, который я спросил, когда я пытался исправить ошибку в коде: Arithmetic operation resulted in an overflow in unsafe C#enter link description here