Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Boroda_2

.doc
Скачиваний:
7
Добавлен:
20.03.2015
Размер:
1.67 Mб
Скачать

Важно то, что исключения могут вызываться программно, а перехватываются они аппаратно или на микропрограммном уровне. Помимо перехвата исключе­ния, есть и другой способ определения факта переполнения. Для этого нужно иметь 1-разрядный регистр, который будет устанавливаться всякий раз, когда происходит переполнение. В этом случае программисту, который хотел бы проверять результат на переполнение, после каждой арифметической команды пришлось бы включать в программу команду перехода по переполнению, что очень неудобно. То есть по сравнению с явной программной проверкой перехват исключений экономит время и память.

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

К наиболее распространенным условиям, которые могут вызывать исключе­ния, относятся: переполнение и исчезновение значащих разрядов при выполне­нии операций с плавающей точкой, переполнение при выполнении операций с целыми числами, нарушения защиты, неопределяемый код операции, пере­полнение стека, запуск несуществующего устройства ввода-вывода, вызов слова с нечетным адресом, деление на 0.

Прерывания

Прерывания — это изменения в потоке управления, вызванные не самой про­граммой, а чем-либо другим. Обычно прерывания связаны с процессом ввода- вывода. Например, программа может дать команду диску начать передачу ин­формации и инициировать прерывание, как только передача данных завершится. Как и в случае исключений, при прерываниях работа программы останавлива­ется, и управление передается программе обработки прерываний (Interrupt Service Routine, ISR), или обработчику прерываний, который выполняет опре­деленные действия. После завершения этих действий обработчик прерываний передает управление прерванной программе. Она должна заново начать прерван­ный процесс в том же самом состоянии, в котором находилась в момент преры­вания. Это значит, что прежнее состояние всех внутренних регистров (то есть состояние, которое было до прерывания) должно быть восстановлено.

Различие между исключениями и прерываниями заключается в том, что ис­ключения синхронны по отношению к программе, а прерывания асинхронны. Если многократно перезапускать программу с одними и теми же входными дан­ными, исключения каждый раз будут происходить в одних и тех же местах про­граммы, а прерывания — нет (в нашем примере с диском прерывание произойдет только тогда, когда диск завершит передачу данных, а не когда этого потребует программа). Причина воспроизводимости исключений и невоспроизводимости прерываний состоит в том, что первые вызываются программой непосредствен­но, а вторые — опосредованно.

Чтобы понять, как происходят прерывания, рассмотрим обычный пример: компьютеру нужно вывести на терминал строку символов. Программа сначала собирает в буфер все символы, предназначенные для вывода на экран, инициа­лизирует глобальную переменную ptr, которая должна указывать на начало бу­фера, и делает вторую глобальную переменную count равной числу символов, выводимых на экран. Затем программа проверяет, готов ли терминал, и, если го­тов, выводит на экран первый символ (например, используя регистры, показан­ные на рис. 5.20). Начав процесс ввода-вывода, центральный процессор освобож­дается и может запустить другую программу или сделать что-либо еще.

Через некоторое время символ отображается на экране. После этого может быть инициировано прерывание. Ниже перечислены основные шаги (в упрощен­ной форме).

Действия аппаратного обеспечения:

  1. Контроллер устройства нагружает линию прерывания на системной шине.

  2. Когда центральный процессор оказывается готовым к обработке прерывания, он выставляет на шине символ подтверждения прерывания.

  3. Когда контроллер устройства обнаруживает, что сигнал прерывания подтвер­жден, он помещает небольшое целое число на информационные линии, чтобы «представиться» (то есть, показать, что за устройство является источником прерывания). Это число называется вектором прерываний1.

  4. Центральный процессор считывает вектор прерываний с шины и временно его сохраняет.

  5. Центральный процессор помещает в стек счетчик команд и слово состояния программы.

  6. Центральный процессор определяет местонахождение нового счетчика команд, используя вектор прерывания в качестве индекса в таблице в нижней части па­мяти. Если, например, размер счетчика команд составляет 4 байта, тогда вектор прерываний п соответствует адресу 4п. Новый счетчик команд указывает на начало программы обработки прерываний для устройства, ставшего источни­ком прерывания. Часто помимо этого загружается или изменяется слово со­стояния программы (например, чтобы блокировать дальнейшие прерывания).

Дальнейшие действия выполняются программно:

  1. Программа обработки прерываний сохраняет все нужные ей регистры таким образом, чтобы их можно было восстановить позднее. Их можно сохранить в стеке или в системной таблице.

  2. Каждый вектор прерываний используется всеми устройствами данного типа совместно, поэтому в данный момент еще не известно, какой терминал вы­звал прерывание. Номер терминала можно узнать, считав значение како­го-нибудь регистра.

  3. После этого можно считывать любую другую информацию прерывания, на­пример коды состояния.

  4. Если происходит ошибка ввода-вывода, на этом этапе ее нужно обработать.

  5. Глобальные переменные ptr и count обновляются. Первая увеличивается на 1, чтобы показывать на следующий байт, а вторая уменьшается на 1, чтобы ука­зать, что осталось вывести на 1 байт меньше. Если count все еще больше 0, значит, еще не все символы выведены на экран. Тот символ, на который в данный момент указывает ptr, копируется в выходной буферный регистр.

  6. В случае необходимости выдается специальный код, который сообщает уст­ройству или контроллеру прерывания, что прерывание обработано.

  7. Восстанавливаются все сохраненные регистры.

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

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

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

Здесь существует два подхода. Первый — для всех программ обработки пре­рываний сначала (даже до сохранения регистров) предотвратить последующие прерывания — в этом случае прерывания будут происходить строго поочередно. Однако это может привести к проблемам с устройствами, которые не могут дол­го простаивать. Например, на линию связи, поддерживающую скорость передачи 9600 бит в секунду, символы поступают каждые 1042 микросекунды. Если пер­вый символ окажется необработанным, когда поступит второй, данные будут по­теряны.

Если компьютер имеет подобные устройства ввода-вывода, то лучше всего приписать каждому устройству определенный приоритет, высокий — для более критичных, низкий — для менее критичных устройств. Центральный процессор тоже должен иметь приоритеты, которые определяются по одному из полей сло­ва состояния программы. Если устройство с приоритетом п вызывает прерыва­ние, программа обработки прерывания тоже должна работать с приоритетом п.

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

Поскольку сами программы обработки прерываний могут прерываться, един­ственно возможный способ точно управлять ситуацией — сделать так, чтобы все

прерывания были прозрачными. Рассмотрим простой пример с несколькими прерываниями. Пусть компьютер имеет три устройства ввода-вывода: принтер, диск и линию RS232 с приоритетами 2, 4 и 5 соответственно. Изначально (t = О, где t время) работает пользовательская программа. При t = 10 принтер неожи­данно инициирует прерывание. Запускается программа обработки прерывания (ISR) от принтера, как показано на рис. 5.28.

При t = 15 прерывания требует линия RS232. Так как линия RS232 имеет бо­лее высокий приоритет (5), чем принтер (2), инициируется обработка этого пре­рывания. Состояние машины, при котором работает ISR принтера, сохраняется в стеке, и начинается выполнение программы обработки прерывания линии RS232.

Немного позже, при t = 20, диск завершает свою работу и сигнализирует об этом прерыванием. Однако его приоритет (4) ниже, чем приоритет работающей в данный момент программы обработки прерываний (5), поэтому центральный процессор не подтверждает прием сигнала прерывания, и диск вынужден ждать. При £ = 25 заканчивает работать ISR линии RS232, и машина возвращается в то состояние, в котором она находилась до прерывания от линии RS232, то есть в состояние, соответствующее работе ISR принтера с приоритетом 2. Как только центральный процессор переключается на приоритет 2, еще до выполнения пер­вой команды, диск с приоритетом 4 совершает прерывание, и запускается ISR диска. После ее завершения снова продолжается программа обработки прерыва­ний от принтера. Наконец, при t = 40 все программы обработки прерываний за­вершаются, и выполнение пользовательской программы начинается с того места, на котором она прервалась.

Со времен процессора 8088 все процессоры Intel имеют два уровня (приори­тета) прерываний: маскируемые и немаскируемые прерывания. Немаскируемые прерывания обычно используются только для сообщения об очень серьезных си­туациях, например об ошибках четности в памяти. Для всех устройств ввода-вы­вода имеется единственное маскируемое прерывание.

Когда устройство ввода-вывода требует прерывания, центральный процессор использует вектор прерывания при индексировании таблицы из 256 элементов, чтобы найти адрес программы обработки прерываний. Элементы таблицы пред­ставляют собой 8-байтные дескрипторы сегмента. Таблица может начинаться в любом месте памяти. Глобальный регистр указывает на ее начало.

При наличии только одного уровня прерываний у центрального процессора нет возможности сделать так, чтобы высокоприоритетное устройство прерывало работу среднеприоритетной программы обработки прерываний, пока этому ме­шает низкоприоритетное устройство. Для решения проблемы центральные про­цессоры Intel обычно используют внешний контроллер прерываний (например, 8259А). При первом прерывании (например, с приоритетом п) работа процессо­ра приостанавливается. Если после этого происходит еще одно прерывание с бо­лее высоким приоритетом, контроллер прерывания инициирует прерывание во второй раз. Если же второе прерывание обладает более низким приоритетом, оно не инициируется до окончания первого. Чтобы эта система работала, контроллер прерываний должен каким-либо образом узнавать о завершении текущей про­граммы обработки прерываний. Поэтому когда полностью завершается обработ­ка текущего прерывания, центральный процессор должен посылать контроллеру прерываний специальную команду.

Ханойская башня

Теперь, когда мы изучили уровень архитектуры набора команд трех машин, нам нужно все обобщить. Давайте подробно рассмотрим все тот же пример решения задачи «Ханойская башня»). В листинге 5.6 приведена версия этой программы на языке Java. В следующих подразделах мы предложим программы на ассемб­лере Pentium 4 и UltraSPARC III.

Однако чтобы избежать проблем с вводом-выводом Java, для машин Pentium 4 и UltraSPARC III мы будем транслировать версию программы не на Java, а на С. Единственное различие — это замена Java-onepaTopa print!п стандартным опера­тором языка С:

printf("Переместить диск с 2d на %й\п". i, j)

Синтаксис строки в операторе printf не важен (строка печатается буквально за исключением символов *d, означающих, что следующее целое число будет представлено в десятичной системе счисления). Здесь важно только то, что про­цедура вызывается с тремя параметрами: форматирующей строкой и двумя це­лыми числами.

Мы использовали язык С для Pentium 4 и UltraSPARC III, поскольку библиоте­ка ввода-вывода Java для этих машин недоступна, в отличие от библиотеки С вво­да-вывода. Разница минимальна — всего один оператор вывода строки на экран.

Решение задачи «Ханойская башня» на ассемблере Pentium 4

В листинге 5.7 приведен возможный вариант программы на языке С для компь­ютера Pentium 4 после трансляции. Регистр EBP используется в качестве указа­теля фрейма. Первые два слова требуются для сборки, поэтому первый параметр п (или N, поскольку регистр символов в макроассемблере не важен) находится в ячейке EBP + 8, а за ним следуют параметры i и j в ячейках EBP + 12 и EBP + 16 соответственно. Локальная переменная к находится в EBP + 20.

Листинг 5.7

.586 .MODEL FLAT PUBLIC _towers EXTERN _printf:NEAR .CODE towers:

Решение задачи «Ханойская башня» для Pentium 4

; компилируется для Pentium

L1:

экспорт 'towers' импорт printf

PUSH EBP

сохраняет EBP (указатель фрейма)

MOV EBP, ESP

устанавливает новый указатель фрейма над ESP

CMP[EBP+8],1

if (п—1)

JNE LI

переход, если п не равно 1

MOV EAX, [EBP+16]

printf("...", i. j);

PUSH EAX

сохранение параметров i, j и формата

MOV ЕАХ, [EBP+12]

строка помещается в стек

PUSH EAX

в обратном порядке (требование языка С)

PUSH OFFSET FLAT:format

OFFSET FLAT - это адрес формата

CALL printf

вызов процедуры printf

ADD ESP, 12

удаление параметров из стека

JMP Done

завершение

MOV EAX,6

начало вычисления k=6-i-j

SUB EAX, [EBP+12]

EAX=6-i

SUB EAX, [EBP+16]

EAX=6-i-j

MOV [EBP+20], EAX

k=EAX

PUSH EAX

начало процедуры towers(n-1. i. k)

MOV EAX, [EBP+12]

EAX=i

PUSH EAX

помещает в стек i

MOV EAX, [EBP+8]

EAX=n

DEC EAX

EAX=n-l

PUSH EAX

помещает в стек n-1

CALL towers

вызов процедуры towers(n-l, i, 6-i-j)

ADD ESP, 12

удаляет параметры из стека

MOV EAX, [EBP+16]

начало процедуры towers (1, i. j)

PUSH EAX

помещает в стек j

MOV EAX, [EBP+12]

EAX=i

PUSH EAX

помещает в стек i

PUSH 1

помещает в стек 1

CALL towers

вызывает процедуру towersd, i, j)

ADD ESP, 12

удаляет параметры из стека

MOV EAX, [EBP+12]

начало процедуры towers(n-l, 6-i-j, i)

PUSH EAX

помещает в стек i

MOV EAX, [EBP+20]

EAX=k

PUSH EAX

помещает в стек к

MOV EAX, [EBP+8]

ЕАХ = п

DEC EAX

ЕАХ=п-1

PUSH EAX

помещает в стек п-1

CALL towers

вызов процедуры towers(n-l, 6-i-j, i)

ADD ESP, 12

корректировка указателя стека


Done: LEAVE ; подготовка к выходу

RET 0 ; возврат к вызывающей программе

.DATA

format DB "Переместить диск с %6 на %6\п" ; форматирующая строка END

Процедура начинается с создания нового фрейма в конце старого. Для этого значение регистра ESP копируется в указатель фрейма ЕВР. Затем п сравнивает­ся с 1, и если n > 1, совершается переход к оператору else. Тогда код then поме­щает в стек три значения: адрес форматирующей строки, i и j, а затем вызывает сам себя.

Параметры помещаются в стек в обратном порядке, поскольку это требование языка С. Необходимо поместить указатель на форматирующую строку в верши­ну стека. Процедура printf имеет переменное число параметров, и если парамет­ры будут помещаться в стек в прямом порядке, то процедура не сможет узнать, в каком месте стека находится форматирующая строка.

После вызова процедуры к регистру ESP прибавляется 12, чтобы удалить па­раметры из стека. На самом деле они не удаляются из памяти, но изменение ре­гистра ESP делает их недоступными через обычные операции со стеком.

Выполнение части el se начинается с метки L1. Здесь сначала вычисляется вы­ражение 6 — i — j, а полученное значение сохраняется в переменной к. Сохране­ние значения в переменной к избавляет от необходимости вычислять это выра­жение во второй раз.

Затем процедура вызывает сама себя три раза, каждый раз с новыми парамет­рами. После каждого вызова стек освобождается.

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

Решение задачи «Ханойская башня» на ассемблере UltraSPARC III

А теперь рассмотрим ту же программу на ассемблере UltraSPARC III (лис­тинг 5.8). Поскольку программа для UltraSPARC III совершенно нечитабельна даже после длительной практики, мы решили определить несколько символов, чтобы прояснить дело. Чтобы такая программа работала, ее перед ассемблирова­нием нужно пропустить через программу под названием срр (препроцессор С). Здесь мы используем строчные буквы, поскольку ассемблер UltraSPARC III это­го требует (это на тот случай, если читатели захотят набрать эту программу и за­пустить).

Листинг 5.8. Решение задачи «Ханойская башня» для UltraSPARC III

Idefine Scratch Ш /* в срр комментарии записываются как в С*/

! if(n— 1)

.ргос 04 .global towers towers: save £sp,-112, £sp cmp N, 1 bne Else

sethi Xhi(format),

! if (n != 1) goto Else ParamO ! printf("Переместить диск с %6 на %6\n",

! i. j)

or ParamO, Xlo(format). ParamO ! ParamO - адрес форматирующей строки

2 (к)

(J)

(J)

mov I, Paraml call printf mov J, Param2 b Done nop

Else: mov 6, К

sub K,J,K sub K,I,K

add N, -1, Scratch mov Scratch, ParamO mov I, Paraml call towers mov K, Param2

mov 1, ParamO mov I, Paraml call towers mov J, Param2

mov Scratch, ParamO mov K, Paraml call towers mov J. Param2

Paraml = i

вызов printf ДО установки параметра 2 (j) пустая операция для установки параметра 2 завершение

вставляет пустую операцию

начало вычисления к = 6

k=6-j

k=6-i-j

  1. к)

начало процедуры towers(п-1,

Scratch = п-1 параметр 1 = i

вызов towers ДО установки параметра пустая операция после вызова процедуры для установки параметра 2

начало процедуры towersd, i, j) параметр 1 = i

вызов towers ДО установки параметра 2 параметр 2 = j

начало процедуры towers(n-1, k, j) параметр 1 = к

вызов towers ДО установки параметра 2 параметр 2 = j

Done: ret ! выход из процедуры

restore ! вставка пустой команды после ret

! для восстановления окон format: .asciz "Переместить диск с %6 на %6\п"

По алгоритму версия UltraSPARC III идентична версии Pentium 4. В обоих случаях сначала проверяется п, и, если n > 1, совершается переход к else. Основ­ные сложности версии UltraSPARC III связаны с некоторыми свойствами архи­тектуры команд.

Сначала код UltraSPARC III должен передать адрес форматирующей строки в printf, но машина не может просто переместить адрес в регистр, который со­держит выходной параметр, поскольку нельзя поместить 32-разрядную констан­ту в регистр одной командой. Для этого требуется выполнить две команды: sethi и ог.

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

А теперь рассмотрим команду пор, которая следует за Done. Это пустая опера­ция, которая всегда будет выполняться, даже если она следует за командой ус­ловного перехода. Сложность состоит в том, что процессор UltraSPARC III очень конвейеризирован, и к тому моменту, когда аппаратно обнаруживается ко­манда перехода, следующая команда уже практически заканчивает обрабаты­ваться. Добро пожаловать в удивительный мир RISC-программирования!

Эта особенность распространяется и на вызовы процедур. Рассмотрим пер­вый вызов процедуры towers в части el se. Процедура помещает п - 1 в %о0, a i — в %ol, но совершает вызов процедуры towers до того, как поместит последний па­раметр в нужное место. На компьютере Pentium 4 вы сначала передаете парамет­ры, а затем вызываете процедуру. А здесь вы сначала передаете часть парамет­ров, затем вызываете процедуру, и только после этого передаете последний параметр. К тому моменту, когда машина осознает, что она имеет дело с коман­дой cal 1, следующую команду все равно приходится выполнять (из-за конвейе­ризации). А почему бы в этом случае не использовать пустую операцию, чтобы передать последний параметр? Даже если этот параметр потребуется самой пер­вой команде вызванной процедуры, он уже будет на своем месте.

Наконец, рассмотрим часть команды Done. Здесь после команды ret тоже вставляется пустая операция. Эта пустая операция используется для команды restore, которая увеличивает на 1 значение CWP, чтобы вернуть регистровое ок­но в прежнее состояние.

Архитектура IA-64 и процессор Itanium 2

Неизбежно, и очень скоро, наступит момент, когда создать на основе архитекту­ры команд IA-32 и линейки процессоров Pentium 4 что-то более совершенное, чем существующие модели, будет уже невозможно. Пока что новые модели со­вершенствуются только благодаря новым технологиям производства, которые позволяют уменьшать размеры транзисторов, а значит, повышать тактовые час­тоты. В то же время, повышать скорость работы становится все труднее, и виной тому — ограничения, свойственные архитектуре команд IA-32.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]