Проверка доступного размера стека в C

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

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

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

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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
39
0
68 666
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

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

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

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

Мне нравится этот ответ, но, не зная заранее размер стека, я не могу сказать, собираюсь ли я его взорвать.

Paul Hargreaves 10.09.2008 16:18

В вашей системе будет размер стека по умолчанию для потоков. В Windows это 1 МБ адресного пространства. Вы можете контролировать это, если создаете свои собственные потоки. Хотя, как указывает Скиз, было бы лучше не беспокоиться о точном лимите!

Rob Walker 10.09.2008 17:16

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

Steve Jessop 02.11.2009 15:15

В Linux вы можете получить размер стека с помощью ulimit -a.

Mark Lakata 29.06.2018 03:35

Предупреждения: некоторые платформы (особенно встроенные системы) не выделяют данные в стеке (в стеке хранятся только адреса возврата функций). В этом случае адреса локальных переменных не имеют смысла.

Mark Lakata 29.06.2018 03:37
Ответ принят как подходящий

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

If you have to ask, you're probably doing something wrong.

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

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

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

Пример (не самый лучший): void subroutine (int i) {char foo [20000]; i ++; if (i <1000) подпрограмма (i); }

Paul Hargreaves 10.09.2008 16:57

Вы правы, это не очень хороший пример. Что я действительно хотел знать, так это то, что вы делали с массивом 20k.

Skizz 10.09.2008 17:39

Хотя, если вы когда-либо пытались написать действительно, действительно переносимый код, вы узнаете, что «вы всегда должны спрашивать, и вы всегда делаете что-то не так, потому что не существует переносимой концепции« использования стека », и все же это ответственность не использовать слишком много стека. Так что лучше просто присоединиться к заговору молчания, написать функциональный тест, который, как вы надеетесь, потребляет столько стека, сколько ваша программа когда-либо будет на практике, и оставьте это на усмотрение интегратора платформы ".

Steve Jessop 02.11.2009 15:10

Вопрос не в том, нужно ли мне проверять размер стека? это «Как мне проверить размер стека?»

Justicle 20.05.2018 21:25

@Justicle: Да, это правда, но ответ, который я дал, все еще в силе, если вам нужно спросить, вы делаете что-то не так - этот сайт о попытках стать лучшими инженерами. Вместо этого OP должен искать другие, более переносимые решения, а не полагаться на непереносимое пространство стека - например, с использованием динамического распределения и хранения только указателей в стеке. При программировании всегда следует учитывать худший случай. Обработка ошибок при сбое динамического распределения намного проще, чем обработка ошибок вне стека.

Skizz 25.05.2018 01:39

Я просто хочу запросить размер стека, чтобы ответить на вопрос «помещаются ли в стек массивы с областью видимости файла?» Я предполагаю, что нет, но, создав две простые программы, которые запрашивают размер стека, одну с массивом с файловой областью, а другую без массива, я могу ответить на этот вопрос сам.

twitchdotcom slash KANJICODER 18.04.2020 00:49

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

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

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

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

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

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

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

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

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

Это лучший способ сделать это, если только вы не делаете какое-то безумное статически распределенное сопоставление. Нельзя сказать, что все статически назначенные сопоставления безумны, но обычно это так :)

Tim Post 26.04.2011 15:24

getrusage() не работает с размером стека в Linux. «ru_isrss (unmaintained) This field is currently unused on Linux.» (linux.die.net/man/2/getrusage). Не знаю, когда это произошло, но это верно для ядра 2.6.28.

atzz 13.01.2012 15:43

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

getrusage() не работает с размером стека в Linux. «ru_isrss (unmaintained) This field is currently unused on Linux.» (linux.die.net/man/2/getrusage). Не знаю, когда это произошло, но это верно для ядра 2.6.28.

atzz 13.01.2012 15:46

@phoxis: getrlimit (RLIMIT_STACK, &limit), похоже, дает общий размер стека, а не размер оставшегося свободного стека.

user2284570 19.07.2016 20:32

@ user2284570: Из man getrlimit я вижу, что написано «Максимальный размер стека процесса в байтах». . Можете ли вы уточнить, что заставляет вас думать, что это может быть оставшийся размер стека?

phoxis 21.07.2016 15:55

@phoxis: Это то, что я говорю. Это общий размер стека. А в случае ᴏᴘ полезно получить только оставшееся.

user2284570 22.07.2016 16:20

Для 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#введите описание ссылки здесь

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