Я пытаюсь понять механизм раскручивания стека в C++. Другими словами, меня интересует, как эта функция реализована (и является ли она частью стандарта).
Таким образом, поток выполняет некоторый код, пока не возникнет исключение. Когда возникает исключение, какие потоки/обработчики прерываний используются для записи состояния и очистки стека? Что гарантирует стандарт и что зависит от реализации?
Стандарт C++ дает только общие требования, поэтому он может различаться в зависимости от операционной системы и даже выбора среды выполнения в одной и той же операционной системе. Что вызывает такие вопросы: stackoverflow.com/questions/15670169/…
На некоторые части этого вопроса можно ответить, поэтому StackOverflow рекомендует задавать по одному вопросу на вопрос. Разматывание стека выполняется, например, в потоке, вызвавшем исключение.
Поток выполняет некоторый код до тех пор, пока не возникнет исключение, и он продолжает это делать. Обработка исключений по-прежнему является кодом C++.
Выражение throw создает объект C++, работающий в контексте бросающей функции. Пока работает конструктор объекта-исключения, все объекты в области действия вызывающей функции все еще живы.
Однако сразу после этого происходит раскрутка стека. Компилятор C++ организует обратный путь, который не требует возвращаемого объекта, но позволяет передавать объект исключения. Как и при обычном возврате, объекты, которые являются локальными для функции, уничтожаются, когда функция возвращается. На двоичном уровне это довольно просто: это всего лишь набор вызовов деструктора, и обычно также корректируется указатель стека.
Что стандарт также не определяет, так это механизм, используемый для определения того, из скольких областей необходимо выйти. Стандарт описывает в терминах catch, но типичный ЦП не имеет прямого эквивалента. Следовательно, это обычно является частью C++ ABI для данной платформы, поэтому компиляторы, использующие ABI, согласны. Совместимость с ABI требует, чтобы вызывающий объект перехватывал исключения от вызываемого объекта, даже если они были скомпилированы с помощью разных компиляторов. И, очевидно, деструкторы должны быть вызваны, поэтому ABI также должен организовать этот механизм. Промежуточные функции могли быть даже скомпилированы третьим компилятором — пока они имеют общий ABI, все должно работать.
Как отмечено в комментариях, C++ не имеет понятия о прерываниях. Если ОС нужно, чтобы что-то происходило с прерываниями, об этом должен позаботиться компилятор. Неважно, что именно делает код C++ в это время.
Я думаю, что в тексте есть немного неточное утверждение: это не совсем обратный путь, который позволяет передать объект исключения; это звучит так, как будто функция способна альтернативно возвращать объект исключения, хотя я не думаю, что на самом деле это происходит. Код очистки каждой функции в стеке вызывается кодом раскручивания стека, но он не получает и не возвращает объект исключения. catch оговорки получают, но опять же не возвращают.
Можно привести пару примеров того, как предложения catch и finally могут быть обнаружены кодом раскручивания стека. Один из способов — всегда помещать в стек специальное дозорное значение в try и помещать его в конец предложения try-catch-finally. Затем код раскручивания стека должен только посмотреть значение прямо перед адресом возврата в стеке.
Другой способ — всегда следовать за каждой инструкцией call с относительным переходом на несколько байтов дальше и сохранять контрольное значение в этих байтах. Это работает немного лучше в обычных условиях, но требует больше работы во время раскручивания стека, потому что раскручивателю стека теперь нужно просмотреть код по адресу возврата, но в целом это нормально.
Нет, прерываний нет. Я думаю, вы путаете исключения C++ с исключениями более низкого уровня.