Проверка доступного размера стека в C - PullRequest
33 голосов
/ 10 сентября 2008

Я использую MinGW с GCC 3.4.5 (mingw-special vista r3). ​​

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

Если нет, то какими еще способами вы могли бы обойти проблему потенциального исчерпания стекового пространства?

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

Ответы [ 9 ]

21 голосов
/ 03 мая 2011

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

14 голосов
/ 10 сентября 2008

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

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 для каждого потока.

9 голосов
/ 10 сентября 2008

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

6 голосов
/ 10 сентября 2008

Раймонд Чен ( Старая новая вещь ) имеет хороший ответ на этот вопрос:

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

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

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

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

3 голосов
/ 03 января 2012

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

Кстати: этот код также можно найти в StackOverflow по другому вопросу, который я задал, когда пытался исправить ошибку в коде: Арифметическая операция привела к переполнению в небезопасном C # enter описание ссылки здесь

3 голосов
/ 10 сентября 2008

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

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

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

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

3 голосов
/ 10 сентября 2008

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

2 голосов
/ 12 сентября 2008

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

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

2 голосов
/ 11 сентября 2008

возможно это поможет только для платформы Windows:

в PE-заголовке (IMAGE_NT_HEADERS) вашего exe есть несколько записей, таких как:


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) -> IMAGE_NT_HEADERS, и там вы найдете следующие поля: SizeOfStackReserve и SizeOfStackCommit .

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...