Проверка доступного размера стека в C
Я использую MinGW с GCC 3.4.5 (mingw-special vista r3).
В моем приложении C используется много стека, поэтому я задавался вопросом, есть ли какой-либо способ, который я могу сказать программно, сколько осталось в стеке, чтобы я мог чисто обрабатывать ситуацию, если обнаружил, что я скоро закончил.
Если бы не другие способы решения проблемы потенциального истечения пространства стека?
Я понятия не имею, какой размер стека я начну, поэтому также нужно будет определить это программно.
Ответы
Ответ 1
Раймонд Чен (The Old New Thing) имеет хороший ответ на этот вопрос:
Если вы должны спросить, вы, вероятно, делаете что-то неправильно.
Здесь некоторые данные Win32 о распределении стека: MSDN.
Если вы считаете, что можете ограничить пространство стека, вы почти наверняка будете ограничены доступной виртуальной памятью, и в этом случае вам нужно будет найти другое решение.
Что именно вы пытаетесь сделать?
Ответ 2
Функция 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
Возьмем адрес локальной переменной из стека. Затем в более вложенном вызове вы можете вычесть адрес другого локального, чтобы найти разницу между ними.
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 по каждому потоку.
Ответ 4
проверьте, поддерживает ли ваш компилятор stackavail()
Ответ 5
Для окон: я сделал это, прежде чем использовать функцию VirtualQuery из Kernel32.dll. У меня есть только пример на С#, но он демонстрирует технику:
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 по другому вопросу, который я задал, когда пытался исправить ошибку в коде: Арифметическая операция привела к переполнению небезопасных С# введите ссылку здесь
Ответ 6
Предполагая, что вы знаете размер полного стека, вы, вероятно, можете добавить код сборки для чтения ESP.
Если вы прочитали ESP и сохранили его в основной функции, вы можете сравнить текущий ESP с ESP, который у вас есть, и посмотреть, как сильно изменился ESP. Это даст вам представление о том, сколько стека вы используете.
Ответ 7
Это проблема, с которой я отказался. С большим количеством взломов и (в основном) молитв вы можете получить решение, которое работает в данный момент на данной машине. Но в целом, похоже, нет достойного способа сделать это.
Вам нужно будет получить позицию и размер стека извне вашей программы (в Linux вы можете получить ее от /proc/<pid>/maps
). В вашей программе вы должны как-то проверить, где вы находитесь в стеке. Использование локальных переменных возможно, но нет реальной гарантии, что они действительно находятся в стеке. Вы также можете попытаться получить значение из регистра указателя стека с некоторой сборкой.
Итак, теперь у вас есть местоположение стека, его размер и текущая позиция, и вы предполагаете, что знаете, в каком направлении растет стек. Когда вы переходите в режим? Лучше не делать этого близко к концу, потому что ваша оценка (то есть адрес локальной переменной или значения из указателя стека), вероятно, немного оптимистична; это не редкость адресовать память за указателем стека. Кроме того, вы не имеете понятия о том, сколько места в стеке требуется для каждой данной функции (и функций, которые она вызывает). Таким образом, вам нужно будет оставить довольно комнату в конце.
Я могу только посоветовать вам не попасть в этот беспорядок и попытаться избежать очень глубокой рекурсии. Вы также можете увеличить размер стека; на Windows вы должны скомпилировать это в исполняемый файл, я полагаю.
Ответ 8
возможно, это поможет только для платформы 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.
Если вы попробуете это, сообщите мне, и я помогу вам. Существует способ получить размер стека, используемый в определенной точке.
Ответ 9
В Linux вы будете называть getrusage и проверять возвращаемые struct rusage's
ru_isrss member (интегральный размер разделенного стека).
С сайта MINGW и его сайта sourceforge отслеживания патчей, я вижу, что в мае 2008 года было какое-то исправление в getrusage, и похоже, что оно в целом поддерживалось довольно долгое время. Вы должны тщательно проверить любые оговорки в отношении того, какая часть типичных функций Linux поддерживается MinGW.