Поделиться через


_resetstkoflw

Выполняет восстановление после переполнения стека.

Внимание

Этот API нельзя использовать в приложениях, выполняемых в среде выполнения Windows. Дополнительные сведения: Функции CRT, которые не поддерживаются в приложениях универсальной платформы Windows.

Синтаксис

int _resetstkoflw( void );

Возвращаемое значение

Ненулевое значение, если функция завершается успешно; в противном случае — ноль.

Замечания

Функция _resetstkoflw выполняет восстановление после переполнения стека, что позволяет программе продолжить работу вместо того, чтобы завершить работу с неустранимой ошибкой. _resetstkoflw Если функция не вызывается, после предыдущего исключения нет страниц защиты. При следующем переполнении стека отсутствуют исключения и процесс завершается без предупреждения.

Если поток в приложении вызывает EXCEPTION_STACK_OVERFLOW исключение, поток оставил свой стек в поврежденном состоянии. Это исключение отличается от других исключений, таких как EXCEPTION_ACCESS_VIOLATION или EXCEPTION_INT_DIVIDE_BY_ZERO, где стек не поврежден. При первой загрузке программы для стека задается произвольная небольшая величина. Затем стек увеличивается по требованию, чтобы отвечать потребностям потока. Рост по запросу реализуется путем размещения страницы с PAGE_GUARD доступом в конце текущего стека. Дополнительные сведения см. в разделе "Создание страниц охранника".

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

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

  • Создает новую защитную страницу, расположенную на одну страницу ниже последней.

  • Повторно выполняет инструкцию, которая вызвала исключение.

Таким образом система может увеличивать размер стека для потока автоматически. Каждый поток в процессе имеет максимальный размер стека. Размер стека задается во время компиляции параметром /STACK (Выделение стека) или STACKSIZE оператором в .def файле проекта.

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

  • Удаляет защиту PAGE_GUARD на защитной странице, как описано выше.

  • Пытается выделить новую защитную страницу после последней. Однако выделение завершается сбоем из-за превышения максимального размера стека.

  • Создает исключение, чтобы поток смог его обработать в блоке исключения.

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

Во время восстановления после исключения переполнения стека вызовите функцию _resetstkoflw для восстановления защитной страницы. Эта функция может вызываться из основного __except тела блока или за пределами __except блока. Однако существуют некоторые ограничения на момент ее использования. _resetstkoflw не следует вызывать из:

  • выражения фильтра;

  • функции фильтра;

  • функции, вызываемой из функции фильтра;

  • блока catch.

  • блока __finally.

На этих точках стек еще не достаточно развучен.

Исключения переполнения стека создаются в виде структурированных исключений, а не исключений C++, поэтому _resetstkoflw не полезно в обычном catch блоке, так как он не перехватывает исключение переполнения стека. Однако если _set_se_translator используется для реализации структурированного переводчика исключений, вызывающего исключения C++ (как и во втором примере), исключение переполнения стека приводит к исключению C++, которое может обрабатываться блоком перехвата C++.

Небезопасно вызывать _resetstkoflw блок перехвата C++, который достигается из исключения, вызываемого структурированной функцией переводчика исключений. В этом случае пространство стека не освобождается, а указатель стека не сбрасывается до тех пор, пока не выходит за пределы блока catch, даже если деструкторы были вызваны для любых деструктивных объектов до блока catch. Эта функция не должна вызываться до освобождения пространства стека и сброса указателя стека. Следовательно, она должна вызываться только после выхода из блока catch. Максимально мало места в стеке следует использовать в блоке catch. Переполнение стека, возникающее в блоке catch, которое пытается восстановиться после предыдущего переполнения стека, невозможно восстановить. Это может привести к остановке реагирования программы, так как переполнение блока catch активирует исключение, которое обрабатывается тем же блоком catch.

Существуют ситуации, когда _resetstkoflw может завершиться ошибкой даже в правильном расположении, например в блоке __except . Возможно, недостаточно места для выполнения _resetstkoflw стека без записи на последнюю страницу стека, даже после очистки стека. _resetstkoflw Затем не удается сбросить последнюю страницу стека в качестве страницы охранника и возвращает значение 0, указывающее на сбой. Безопасное использование этой функции должно включать проверку возвращаемого значения вместо того, чтобы предположить, что стек является безопасным для использования.

Структурированная обработка исключений не перехватывает STATUS_STACK_OVERFLOW исключение, если приложение компилируется с /clr помощью (см. ( /clr компиляция среды cl language Runtime)).

По умолчанию глобальное состояние этой функции ограничивается приложением. Чтобы изменить это поведение, см . статью "Глобальное состояние" в CRT.

Требования

Маршрут Обязательный заголовок
_resetstkoflw <malloc.h>

Дополнительные сведения о совместимости см. в разделе Совместимость.

Библиотеки: все версии функций библиотекИ CRT.

Пример

В следующем примере показано рекомендуемое использование функции _resetstkoflw.

// 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;
}

Пример выходных данных без аргументов программы:

loop #1

Программа перестает отвечать без выполнения дальнейших итераций.

С аргументами программы:

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

Description

В следующем примере показано рекомендуемое использование функции _resetstkoflw в программе, в которой структурированные исключения преобразуются в исключения C++.

Код

// 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.

См. также

_alloca