Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

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

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

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

Другие советы

Функция 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 вам придется скомпилировать это в исполняемый файл.

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

возможно, это поможет только для платформы 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.

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

На Linux вы вызовите getRusage и проверяете возвращенный элемент ru_isrss от ru_isrss (интегральный размер стека).

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top