_resetstkoflw
Odzyskiwanie po przepełnieniu stosu.
Ważne
Tego interfejsu API nie można używać w aplikacjach wykonywanych w środowisko wykonawcze systemu Windows. Aby uzyskać więcej informacji, zobacz Funkcje CRT nieobsługiwane w aplikacjach platforma uniwersalna systemu Windows.
Składnia
int _resetstkoflw( void );
Wartość zwracana
Nonzero, jeśli funkcja powiedzie się, zero, jeśli zakończy się niepowodzeniem.
Uwagi
Funkcja _resetstkoflw
odzyskuje dane po warunku przepełnienia stosu, co umożliwia kontynuowanie programu zamiast niepowodzenia z powodu błędu wyjątku krytycznego. _resetstkoflw
Jeśli funkcja nie jest wywoływana, nie ma stron ochrony po poprzednim wyjątku. Przy następnym przepełnieniu stosu nie ma żadnych wyjątków, a proces kończy się bez ostrzeżenia.
Jeśli wątek w aplikacji powoduje EXCEPTION_STACK_OVERFLOW
wyjątek, wątek pozostawił swój stos w uszkodzonym stanie. Ten wyjątek różni się od innych wyjątków, takich jak EXCEPTION_ACCESS_VIOLATION
lub EXCEPTION_INT_DIVIDE_BY_ZERO
, gdzie stos nie jest uszkodzony. Stos jest ustawiany na dowolnie małą wartość po pierwszym załadowaniu programu. Następnie stos rośnie na żądanie, aby zaspokoić potrzeby wątku. Wzrost na żądanie jest implementowany przez umieszczenie strony z dostępem PAGE_GUARD
na końcu bieżącego stosu. Aby uzyskać więcej informacji, zobacz Tworzenie stron ochrony.
Gdy kod powoduje, że wskaźnik stosu wskazuje adres na tej stronie, wystąpi wyjątek, a system wykonuje następujące trzy czynności:
PAGE_GUARD
Usuwa ochronę na stronie ochrony, aby wątek mógł odczytywać i zapisywać dane w pamięci.Przydziela nową stronę ochrony znajdującą się poniżej jednej strony poniżej poprzedniej.
Ponownie uruchamia instrukcję, która zgłosiła wyjątek.
W ten sposób system może automatycznie zwiększyć rozmiar stosu wątku. Każdy wątek w procesie ma maksymalny rozmiar stosu. Rozmiar stosu jest ustawiany w czasie kompilacji przez /STACK
opcję (Alokacje stosu) lub przez instrukcję STACKSIZE
w .def
pliku projektu.
Po przekroczeniu tego maksymalnego rozmiaru stosu system wykonuje następujące trzy czynności:
Usuwa ochronę PAGE_GUARD na stronie ochrony zgodnie z wcześniejszym opisem.
Próbuje przydzielić nową stronę ochrony poniżej ostatniego. Jednak alokacja kończy się niepowodzeniem, ponieważ przekroczono maksymalny rozmiar stosu.
Zgłasza wyjątek, aby wątek mógł obsłużyć go w bloku wyjątków.
W tym momencie stos nie ma już strony straży. Następnym razem, gdy program zwiększa stos do miejsca zapisu poza końcem stosu, powoduje naruszenie dostępu.
Wywołaj metodę _resetstkoflw
, aby przywrócić stronę ochrony po zakończeniu odzyskiwania po wyjątku przepełnienia stosu. Tę funkcję można wywołać z wewnątrz głównej __except
treści bloku lub na zewnątrz __except
bloku. Istnieją jednak pewne ograniczenia dotyczące tego, kiedy należy go używać. _resetstkoflw
nie powinien być wywoływany z:
Wyrażenie filtru.
Funkcja filtru.
Funkcja wywoływana z funkcji filter.
Blok
catch
.Blok
__finally
.
W tych punktach stos nie jest jeszcze wystarczająco niezwiązany.
Wyjątki przepełnienia stosu są generowane jako wyjątki strukturalne, a nie wyjątki języka C++, więc _resetstkoflw
nie jest przydatne w zwykłym catch
bloku, ponieważ nie przechwyci wyjątku przepełnienia stosu. Jeśli _set_se_translator
jednak jest używany do implementowania translatora wyjątków strukturalnych, który zgłasza wyjątki języka C++ (jak w drugim przykładzie), wyjątek przepełnienia stosu powoduje wyjątek języka C++, który może być obsługiwany przez blok catch języka C++.
Nie można bezpiecznie wywołać _resetstkoflw
w bloku catch języka C++, który jest osiągany z wyjątku zgłaszanego przez funkcję translatora wyjątków strukturalnych. W takim przypadku przestrzeń stosu nie zostanie zwolniona, a wskaźnik stosu nie zostanie zresetowany do momentu poza blokiem przechwytywania, mimo że destruktory zostały wywołane dla wszelkich obiektów destruktorów przed blokiem catch. Ta funkcja nie powinna być wywoływana do momentu zwolnienia miejsca na stosie i zresetowania wskaźnika stosu. W związku z tym należy go wywołać tylko po wyjściu z bloku catch. Jak najmniejsza przestrzeń stosu powinna być używana w bloku catch. Przepełnienie stosu, które występuje w bloku catch, który sam próbuje odzyskać po poprzednim przepełnieniu stosu, nie jest możliwy do odzyskania. Może to spowodować, że program przestanie odpowiadać, ponieważ przepełnienie w bloku catch wyzwala wyjątek obsługiwany przez ten sam blok catch.
Istnieją sytuacje, w których _resetstkoflw
można zakończyć się niepowodzeniem, nawet jeśli jest używana w prawidłowej __except
lokalizacji, na przykład w bloku. Może nie być wystarczającej ilości miejsca na stos do wykonania _resetstkoflw
bez zapisywania na ostatniej stronie stosu, nawet po odwijaniu stosu. _resetstkoflw
Następnie nie można zresetować ostatniej strony stosu jako strony straży i zwraca wartość 0 wskazującą błąd. Bezpieczne użycie tej funkcji powinno obejmować sprawdzanie wartości zwracanej zamiast zakładać, że stos jest bezpieczny do użycia.
Obsługa wyjątków strukturalnych nie przechwytuje STATUS_STACK_OVERFLOW
wyjątku podczas kompilowania /clr
aplikacji (zobacz /clr
(kompilacja środowiska uruchomieniowego języka wspólnego)).
Domyślnie stan globalny tej funkcji jest zakresem aplikacji. Aby zmienić to zachowanie, zobacz Stan globalny w CRT.
Wymagania
Procedura | Wymagany nagłówek |
---|---|
_resetstkoflw |
<malloc.h> |
Aby uzyskać więcej informacji o zgodności, zobacz Zgodność.
Biblioteki: wszystkie wersje funkcji biblioteki CRT.
Przykład
W poniższym przykładzie pokazano zalecane użycie _resetstkoflw
funkcji.
// crt_resetstkoflw.c
// Launch program with and without arguments to observe
// the difference made by calling _resetstkoflw.
#include <malloc.h>
#include <stdio.h>
#include <windows.h>
void recursive(int recurse)
{
_alloca(2000);
if (recurse)
recursive(recurse);
}
// Filter for the stack overflow exception.
// This function traps the stack overflow exception, but passes
// all other exceptions through.
int stack_overflow_exception_filter(int exception_code)
{
if (exception_code == EXCEPTION_STACK_OVERFLOW)
{
// Do not call _resetstkoflw here, because
// at this point, the stack isn't yet unwound.
// Instead, signal that the handler (the __except block)
// is to be executed.
return EXCEPTION_EXECUTE_HANDLER;
}
else
return EXCEPTION_CONTINUE_SEARCH;
}
int main(int ac)
{
int i = 0;
int recurse = 1, result = 0;
for (i = 0 ; i < 10 ; i++)
{
printf("loop #%d\n", i + 1);
__try
{
recursive(recurse);
}
__except(stack_overflow_exception_filter(GetExceptionCode()))
{
// Here, it is safe to reset the stack.
if (ac >= 2)
{
puts("resetting stack overflow");
result = _resetstkoflw();
}
}
// Terminate if _resetstkoflw failed (returned 0)
if (!result)
return 3;
}
return 0;
}
Przykładowe dane wyjściowe bez argumentów programu:
loop #1
Program przestaje odpowiadać bez wykonywania dalszych iteracji.
Z argumentami programu:
loop #1
resetting stack overflow
loop #2
resetting stack overflow
loop #3
resetting stack overflow
loop #4
resetting stack overflow
loop #5
resetting stack overflow
loop #6
resetting stack overflow
loop #7
resetting stack overflow
loop #8
resetting stack overflow
loop #9
resetting stack overflow
loop #10
resetting stack overflow
opis
W poniższym przykładzie pokazano zalecane użycie _resetstkoflw
w programie, w którym wyjątki strukturalne są konwertowane na wyjątki języka C++.
Kod
// crt_resetstkoflw2.cpp
// compile with: /EHa
// _set_se_translator requires the use of /EHa
#include <malloc.h>
#include <stdio.h>
#include <windows.h>
#include <eh.h>
class Exception { };
class StackOverflowException : Exception { };
// Because the overflow is deliberate, disable the warning that
// this function will cause a stack overflow.
#pragma warning (disable: 4717)
void CauseStackOverflow (int i)
{
// Overflow the stack by allocating a large stack-based array
// in a recursive function.
int a[10000];
printf("%d ", i);
CauseStackOverflow (i + 1);
}
void __cdecl SEHTranslator (unsigned int code, _EXCEPTION_POINTERS*)
{
// For stack overflow exceptions, throw our own C++
// exception object.
// For all other exceptions, throw a generic exception object.
// Use minimal stack space in this function.
// Do not call _resetstkoflw in this function.
if (code == EXCEPTION_STACK_OVERFLOW)
throw StackOverflowException ( );
else
throw Exception( );
}
int main ( )
{
bool stack_reset = false;
bool result = false;
// Set up a function to handle all structured exceptions,
// including stack overflow exceptions.
_set_se_translator (SEHTranslator);
try
{
CauseStackOverflow (0);
}
catch (StackOverflowException except)
{
// Use minimal stack space here.
// Do not call _resetstkoflw here.
printf("\nStack overflow!\n");
stack_reset = true;
}
catch (Exception except)
{
// Do not call _resetstkoflw here.
printf("\nUnknown Exception!\n");
}
if (stack_reset)
{
result = _resetstkoflw();
// If stack reset failed, terminate the application.
if (result == 0)
exit(1);
}
void* pv = _alloca(100000);
printf("Recovered from stack overflow and allocated 100,000 bytes"
" using _alloca.");
return 0;
}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Stack overflow!
Recovered from stack overflow and allocated 100,000 bytes using _alloca.