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


_resetstkoflw

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

Важно!

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

int _resetstkoflw ( void );

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

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

Заметки

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Выражения фильтра.

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

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

  • Блока catch.

  • Блока __finally.

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

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

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

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

Структурной обработкой исключений не будет перехвачено исключение STATUS_STACK_OVERFLOW, если приложение скомпилировано с /clr или /clr:pure (см. /clr (компиляция CLR)).

Требования

Подпрограмма

Обязательный заголовок

_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 is not 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

Описание

В следующем примере показано рекомендуемое использование _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.

Эквивалент в .NET Framework

Неприменимо. Для вызова стандартной функции C используйте PInvoke. Дополнительные сведения см. в разделе Примеры вызовов неуправляемого кода.

См. также

Ссылки

_alloca