Исключительные ситуации

Демонстрация calltrace с помощью macros.inc

Программные исключения

Базовая лекция

Обработка событий в ЭВМ

Что такое «событие»?

Тип события

Место в программе известно

Предусмотрено
программой

Источник

Термин

Нештатная ситуация вычислительного процесса (деление на 0, переполнение, защита памяти, нет такой инструкции и т. п.)

да

нет

Внутренний

Исключение (exception)

Имитация нештатной ситуации («система, разбирайся»). В MIPS — исключение TRAP_EXCEPTION

да

да

Внутренний

Ловушка (trap)

Обращение к функции операционной системы

да

да

Внутренний

Системный вызов (system call)

Запрос внешнего устройства

нет

нет

Внешний

Прерывание (interrupt)

Сбой внешнего устройства

иногда

нет

Внешний

Прерывание (interrupt)

Строго говоря, системный вызов — не событие, т. к. всегда явно вызывается программой, но также требует обработки ядром. Общая идея обработки событий.

Адрес

Содержимое

Пояснение

0x80000100

0x80000180

адрес обработчика исключения № 0

0x80000104

0x800007ac

адрес обработчика исключения № 1

0x80000108

0x800015b0

адрес обработчика исключения № 2

0x80000120

0x80000e54

адрес обработчика исключения № 8

Обработка исключний в MIPS

Следит за аппаратурой, устанавливает и хранит дополнительные данные о событиях специальный управляющий сопроцессор MIPS (coprocessor0).

MARS имитирует основные элементы механизма MIPS32 исключений Регистры сопроцессора 0 MARS:

Название

Номер

Назначение

BadVAddr

8

Адрес, при обращении к которому произошло исключение (при записи/чтении по защищённому адресу)

Status

12

Состояние: маска прерываний, биты разрешений, ...

Cause

13

Тип («причина») исключения и биты отложенных прерываний

EPC

14

Адрес инструкции, которая вызвала исключение (или во время выполнения которой произошло прерывание)

Инструкции для работы с регистрами управляющего сопроцессора:

  1. mfc0 Rdest, C0src — загрузить содержимое управляющего регистра C0src в регистр общего назначения Rdest
  2. mtc0 Rsrc, C0dest — загрузить содержимое регистра общего назначения C0src в управляющий регистр Rdest
  3. eret — вернуться из обработчика исключительной ситуации Когда происходит исключение, сопроцессор совершает следующие действия:
  4. Устанавливает бит 1 управляющего регистра $12 (EXception Level, EXL).
  5. Устанавливает биты 2-6 управляющего регистра $13(причина) согласно типу исключения
  6. Устанавливает управляющий регистр $14 (EPC). В регистре сохраняется адрес инструкции, вызвавшей исключение.
  7. Если исключение было вызвано обращением к неправильному адресу памяти, управляющий регистр $8 (BadVaddr) устанавливается на этот неверный адрес.

  8. Поток выполнения переключается с текущей инструкции MIPS на адрес 0x8000180. Этот адрес в сегменте текста ядра (.ktext) является стандартным для расположением обработчика исключений MIPS32.

  9. Обработчик исключений может вернуть управление программе, используя команду eret. Это поместит значение из "EPC" (регистр $14) в счетчик команд("PC"). Кроме того, eret очищает бит EXL (см. далее) Замечание: Увеличение регистра $14 на 4 перед возвращением позволит пропустить инструкцию, вызвавшую исключение.

Лень и надувательство (см. спойлер):

Регистр Status:

bits

31-16

15-8

7-5

4

3,2

1

0

target

unused

Int. mask

unused

K/U

unused

Exception level

Int enable

Таким образом, в регистре Status формируется трёхуровневый стек для хранения состояний. Если обработка прерывания в прерывании запрещена, достаточно двух уровней, но есть, например, программные ловушки (trap).

Регистр Cause:

31

30-16

15-8

7

6-2

1-0

Br

unused

Pending interrupts

unused

Exception code

unused

Обработчик исключений

Типы исключений (не обязательно реализованы):

При написании обработчика можно без сохранения использовать только регистры $k0 и $k1, т. к. прерывания могут возникнуть когда угодно, а после возвращения ничего (!) не должно меняться.

⇒ В «больших» системах предусматривается отдельный стек ядра (пользовательский стек может быть испорчен).

Пример тривиального обработчика (пройти под отладчиком Mars):

   1 .text
   2         nop
   3         lw    $t0, ($zero)      # Попытка чтения по адресу 0
   4         li    $v0 10
   5         syscall
   6 
   7 .ktext  0x80000180
   8         mfc0    $k0 $14         # В регистре EPC — адрес инструкции, где произошло прерывание
   9         addi    $k0 $k0,4       # Добавим к этому адресу 4
  10         mtc0    $k0 $14         # Запишем обратно в EPС
  11         eret                    # Продолжим работу программы

Пример обработчика. Заметим, что коду обработчика разрешается менять только два регистра — $k0 и $k1, все остальные регистры следует сохранять, если они используется в коде.

   1 .text
   2         lui     $t0 0x7fff
   3         addi    $t0 $t0 0xffff
   4         addi    $t0 $t0 0xffff  # Целочисленное переполнение
   5         sw      $t0 0x400       # Обращение к недоступному адресу памяти
   6         divu    $t0 $t0 $zero   # Деление на 0
   7         teq     $zero $zero     # Программная имитация (ловушка)
   8         li      $v0 10
   9         syscall
  10 .kdata
  11 msg:    .asciiz "Exception "
  12 .ktext  0x80000180
  13         move    $k0 $v0         # Сохраняем $v0
  14         move    $k1 $a0         # Сохраняем $a0
  15         la      $a0 msg         # Выводим сообщение
  16         li      $v0 4
  17         syscall
  18         mfc0    $a0 $13         # Регистр Cause
  19         srl     $a0 $a0 2       # Выделяем в нём поле «причина»
  20         andi    $a0 $a0 0x1f
  21         li      $v0 1           # Выводим как целое
  22         syscall
  23         li      $a0 10
  24         li      $v0 11          # Выводим перевод строки
  25         syscall
  26 
  27         move    $v0 $k0         # Восстанавливаем $v0
  28         move    $a0 $k1         # Восстанавливаем $a0
  29 
  30         li      $k0 0
  31         mtc0    $k0 $13         # Затираем регистр Cause
  32         mfc0    $k0 $14         # В регистре EPC — адрес инструкции, где произошло прерывание
  33         addi    $k0 $k0,4       # Добавим к этому адресу 4
  34         mtc0    $k0 $14         # Запишем обратно в EPС
  35         eret                    # Продолжим работу программы

Вопрос: какой регистр мы всё-таки испортили в этом обработчике?

Замечание: в Mars исключение №9 — это деление на 0, а точки останова реализованы «аппаратно», т. е. внутри самого MARS

Пример полностью программного обработчика прерываний и исключений в проекте SPIM

Имитация исключений

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

С другой стороны, это очень эффективный механизм обращения к ОС почти (или совсем) без дополнительных действий, в отличие от syscall. Системный обработчик по адресу 0x80000180 вызывается немедленно одной инструкцией, атомарно с операцией сравнения.

teq $t1,$t2

Trap if equal

Trap if $t1 is equal to $t2

teqi $t1,-100

Trap if equal to immediate

Trap if $t1 is equal to sign-extended 16 bit immediate

tge $t1,$t2

Trap if greater or equal

Trap if $t1 is greater than or equal to $t2

tgei $t1,-100

Trap if greater than or equal to immediate

Trap if $t1 greater than or equal to sign-extended 16 bit immediate

tgeiu $t1,-100

Trap if greater or equal to immediate unsigned

Trap if $t1 greater than or equal to sign-extended 16 bit immediate, unsigned comparison

tgeu $t1,$t2

Trap if greater or equal unsigned

Trap if $t1 is greater than or equal to $t2 using unsigned comparision

tlt $t1,$t2

Trap if less than

Trap if $t1 less than $t2

tlti $t1,-100

Trap if less than immediate

Trap if $t1 less than sign-extended 16-bit immediate

tltiu $t1,-100

Trap if less than immediate unsigned

Trap if $t1 less than sign-extended 16-bit immediate, unsigned comparison

tltu $t1,$t2

Trap if less than unsigned

Trap if $t1 less than $t2, unsigned comparison

tne $t1,$t2

Trap if not equal

Trap if $t1 is not equal to $t2

tnei $t1,-100

Trap if not equal to immediate

Trap if $t1 is not equal to sign-extended 16 bit immediate

Таким образом гарантируется, что за время, прошедшее между сравнением и обращением к системе, никакие данные не изменятся (потому что этого времени нет :) ).

Назначение инструкций типа trap:

Содержимое поля code игнорируется системой и может быть использовано для кодированиия информации для системного ПО. Для получения этой информации программа должна загрузить слово команды из памяти (адрес хранится в регистре EPC сопроцессора 0). В ассемблере Mars эта возможность отсутствует, обидно.

Многофайловая сборка

базовая часть лекции

Свойства макроассемблера MARS

Замечания авторов MARS относительно их макроассемблера:

Д/З

  1. EJudge: NoErrors 'Без ошибок'

    Написать полную программу, которая пытается вводить целые числа и обрабатывает ошибки ввода до тех пор, пока не введёт 10 штук целых. Затем она их выводит, каждое с новой строки.

    Input:

    zzz
    20
    fwewefqwe
    .654
    71
    -124
    0.1
    82
    6.
    334423
    -94
    VII
    7535
    6
    .
    -
    17
    8968
    Output:

    20
    71
    -124
    82
    334423
    -94
    7535
    6
    17
    8968
  2. EJudge: ExCalc 'Простой калькулятор'

    Написать полную программу — простой целочисленный калькулятор с обработкой ошибок ввода, деления и переполнения.

    • Работа калькулятора:
      1. Ввод первого числа
      2. Ввод знака арифметической операции
      3. Ввод второго числа
      4. Вывод результата (применение операции к первому и второму числу). Результат выводится, даже если была ошибка.
      5. Результат становится первым числом для следующей операции
      6. Переход на п. 2
    • Поддерживаемые действия: "+", "-", "*", "/"
    • Дополнительно (обязательно с использованием обработчика исключений) отслеживаются:
      1. Ввод нечислового значения вместо числа (исключение 8). В этом случае:
        • выводится сообщение "Invalid input"
        • число вводится заново (и так до тех пор, пока не будет введено число)
      2. Деление на 0 (в Mars — исключение 9; обратите внимание на то, как разворачивается псевдоинструкция div $t0 $t1 %t2, и только она). В этом случае

        • выводится сообщение "Division by zero"
        • результат операции должен быть равен 0
      3. Знаковое переполнение (исключение 12). В этом случае
        • выводится сообщение "Error"
        • результат операции равен второму слагаемому
    • Ввод заканчивается, если введённый знак действия не поддерживается (пустая строка, точка, пробел и т п.)
    Input:

    22
    *
    lll
    3
    /
    0
    +
    43545
    +
    2147483647
    .
    Output:

    Invalid input
    66
    Division by zero
    0
    43545
    Error
    2147483647

TODO

  1. ???

LecturesCMC/ArchitectureAssembler2019/07_Exceptions (последним исправлял пользователь FrBrGeorge 2019-05-17 15:23:15)