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

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

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

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

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

Асинхронная передача данных с помощью sendBeacon в JavaScript
Асинхронная передача данных с помощью sendBeacon в JavaScript
В современных веб-приложениях отправка данных из JavaScript на стороне клиента на сервер является распространенной задачей. Одним из популярных...
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Отказ от ответственности: Эта статья предназначена только для демонстрации и не должна использоваться в качестве инвестиционного совета.
Принципы ООП в JavaScript
Принципы ООП в JavaScript
Парадигма объектно-ориентированного программирования имеет 4 основных принципа,
Пройдите собеседование по Angular: Общие вопросы и ответы экспертов
Пройдите собеседование по Angular: Общие вопросы и ответы экспертов
Можете ли вы объяснить разницу между ngOnInit и конструктором в Angular?
Laravel с Turbo JS
Laravel с Turbo JS
Turbo - это библиотека JavaScript для упрощения создания быстрых и высокоинтерактивных веб-приложений. Она работает с помощью техники под названием...
Типы ввода HTML: Лучшие практики и советы
Типы ввода HTML: Лучшие практики и советы
HTML, или HyperText Markup Language , является стандартным языком разметки, используемым для создания веб-страниц. Типы ввода HTML - это различные...
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

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

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