Home > itblogs > А что, собственно, происходит, когда бросается исключение?

А что, собственно, происходит, когда бросается исключение?

October 15th, 2008

Нет, ну в общих чертах понятно – процессор генерирует исключение, операционная система находит нужный обработчик и вызывает его. А что происходит, если посмотреть подробнее? В Windows происходит примерно следующее.

Выполняя поток команд, процессор проверят возможность выполнения каждой инструкции, корректность её аргументов и все остальные факторы, влияющие на корректность выполнения кода. В случае если команда не может быть выполнена (деление на ноль, обращение к несуществующей странице, несоответствие уровня привилегий и т.д.), процессор генерирует исключение – вызывает один из обработчиков, зарегистрированных операционной системой в IDT (Interrupt Dispatch Table).

При вызове обработчика процессор делает сразу несколько вещей: переключается в режим ядра (Ring 0), переключает указатель стека на ядерный стек и сохраняет предыдущие указатели команд и стека в ядерном стеке.

Transition to Ring0

Получив контроль, обработчик исключения сохраняет остальные регистры процессора в стеке и выполняет действия, специфичные для конкретного исключения. Например, обработчик Page Fault Exception запрашивает подкачку страницы у Memory Manager. Если обработчику удалось разрешить проблему, вызвавшую генерацию исключения, обработчик восстанавливает сохраненное состояние процессора и выполняет возврат в пользовательский код (Ring 3). В противном случае, в дело вступает диспетчер исключений.

Диспетчер исключений размещает структуру CONTEXT в пользовательском стеке и копирует туда сохранённое состояние регистров из ядерного стека. Туда же сохраняется текущее состояние Floating Point регистров. Информация об исключении записывается в структуру EXCEPTION_RECORD. Далее, диспетчер подменяет адрес возврата в пользовательский код адресом диспетчера исключений пользовательского режима и выполняет возврат в Ring 3.

Windows поддерживает специальную структуру, TEB (Thread Environment Block), где хранятся локальные данные потока. Эта структура доступна из Ring 3 через сегмент FS (или GS для x64). В самом начале этой структуры хранится указатель на список вложенных блоков “__try” и соответствующих им блоков “__except” и “__finally”. Компилятор генерирует код, добавляющий элемент в этот список при входе в блок “__try” и удаляющий при выходе из блока. (Примечание: справедливо только для x86. 64-х битный код “раскручивает” стек пользуясь сгенерированным компилятором описанием кода.)

Transition to Ring0

Получив управление, диспетчер исключений пользовательского режима по очереди опрашивает обработчики из списка, позволяя каждому из них обработать данное исключение. Найдя обработчик, согласившийся обработать исключение, диспетчер исключений выполняет «раскрутку» стека (Stack Unwinding) и передает управление выбранному обработчику. Этот механизм довольно детально описывался во всевозможных статьях о том, как работает SEH (Structured Exception Handling) (например здесь или здесь), так что я не буду останавливаться на этом детально. «Раскрутчик» стека отслеживает текущее состояние (указатель стека и команд, состояние регистров) в локальной копии структуры CONTEXT. По окончанию обработки исключения, состояние из структуры CONTEXT загружается в процессор, завершая обработку исключения.

И это только поверхностное описание. :-)

  1. Rail
    October 15th, 2008 at 20:30 | #1

    помню еще в режиме рального времени (под ДОС) в самом начале памяти (с адреса 0) лежала таблица адресов обработчиков прерываний (от таймера, клавы, … ). насколько помню там же был обработчик исключений (хотя могу путать).

    Interrupt Dispatch Table – это та (старая добрая) таблица и есть?

    • October 16th, 2008 at 07:57 | #2

      Она самая, только в защищенном режиме у неё формат совсем другой и располагаться она может где угодно в памяти.

  2. byleas
    October 15th, 2008 at 22:42 | #3

    > В самом начале этой структуры хранится указатель на список вложенных блоков “__try” и соответствующих им блоков “__except” и “__finally”.
    Это справедливо для х86, в х64 таблицы “фреймов” статические. Или же TIB::ExceptionList остался в х64 для поддержки wow64?

    • October 16th, 2008 at 08:01 | #4

      Не, это я нахомутал. На картинках – x64 регистры, а описание exception chain – для x86. Познее напишу небольшой пост про отличия x64 от x86 и каким боком там Wow64 приделана. Вот здесь вот есть совсем подробное описание как делается unwinding на x64: http://www.nynaeve.net/?p=113

      PS: TIB::ExceptionList есть и в 64-х битном TEB, но указывает он совсем не на exception chain.

      • byleas
        October 20th, 2008 at 23:31 | #5

        но указывает он совсем не на exception chain

        А на что он указывает, если не секрет? :)

        • October 21st, 2008 at 07:07 | #6

          Ну это же так просто. :-) Сначала:

          windbg.exe notepad.exe
          !teb

          
          0:000> !teb
          TEB at 000007fffffdd000
              ExceptionList:        0000000000000000
          

          А затем:

          windbg.exe %windir%\syswow64\notepad.exe
          !teb

          
          Wow64 TEB32 at 000000007efdd000
              ExceptionList:        00000000002df7ec
          
          Wow64 TEB at 000000007efdb000
              ExceptionList:        000000007efdd000
          

          ExceptionList 64-х битного TEB указывает на 32-х битный TEB, если он есть.

  3. e.v.e
    October 15th, 2008 at 23:29 | #7

    А в чем нарисованы столь красивые картинки?

  4. oleg
    October 16th, 2008 at 02:32 | #9

    Ага, картинки и в самом деле хороши.
    Текст, врочем, тоже.

  5. Злой Анонимус
    October 17th, 2008 at 20:00 | #10

    Злостный офтопик (не путать со спамом!):

    А ты не мог бы прояснить ситуацию: как там дела в Микрософте? Вон на привете пишут, что “hiring frozen”, потом ссылки дают, что таки не фрозен.

    Есть смысл вам в контору CV отсылать, или стоит подождать пару месяцев, пока все не устоканится?

    • October 17th, 2008 at 21:30 | #11

      Сложно сказать. Hiring freeze как таковой ещё не случился, по моим сведениям. По крйней мере продолжают нанимать. Но слухи ходят скорее всего неспроста. Я бы ожидал, что активность набора активность набора должна была бы снизтся. Кризис, все дела. Как на самом деле – хз.

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

      P.S. Смысл отсылать резюме есть всегда. Даже когда точно не нанимают. Оно кушать не просит.

Comments are closed.