Главная страница  Межпроцессное взаимодействие (состязание) 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 [ 47 ] 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

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

jmp RETADR-P STACKBASE(eax)

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

Далее в файле mpx386.s следует процедура s caU. Прежде чем вдаваться в детали, посмотрим на то, как она заканчивается. Здесь вы не увидите в конце инструкции ret или jmp. После того как командой cli запрещается обработка прерываний, код продолжает выполняться и переходит в restart. Функция s call является двойником механизма обработки прерываний для системных вызовов. Эта подпрограмма получает управление при возникновении программного прерывания, то есть в результате срабатывания инструкции int<nnn>. Программные прерывания обрабатываются так же, как и аппаратные, за исключением того, что индекс записи в таблице дескрипторов прерываний берется из инструкции, а не передается контроллером. Таким образом, когда s call получает управление, процессор уже переключен на стек в таблице процессов и в него помещены значения нескольких регистров. Так как у подпрограммы s call нет в конце команды выхода, выполнение переходит в restart, после чего подпрограмма окончательно завершается инструкцией iretd. В результате, как и в случае с аппаратным прерыванием, запускается процесс, на который в данный момент ссылается указатель proc ptr. На рис. 2.18 сравниваются механизмы обработки аппаратного прерывания и системного вызова, использующего программные прерывания.

Устройство:

Передает контроллеру прерываний

электрический сигнал

Контроллер:

1. Прерывает центральный процессор

2. Отправляет цифровой идентификатор устройства, вызвавшего прерывание

Ядро:

1. Сохраняет состояние регистров

2. Вызывает профамму драйвера, чтобы выполнить чтение с устройства

3. Отправляет сообщение

4. Перезапускает процесс (не обязательно тот, который был прерван)

Профамма:

1. Записывает указатель на сообщение и указатель на пункт назначения

в регистры процессора

2. Выполняет инструкцию программного прерывания

Ядро:

1. Сохраняет состояние ретстров

2. Отправляет и/или получает сообщение

3. Перезапускает процесс (не обязательно вызывающий)

Рис. 2.18. а - обработка аппаратного прерывания; б - так происходит системный вызов



Рассмотрим s caU более подробно. Еще одна метка, p s call, - специфика 16-битной версии MINIX, в которой имеются раздельные процедуры для защищенного и реального режимов работы. В 32-битной версии вызов по любой из меток приводит к одному результату. Когда программист на С программирует системный вызов, он пищет код, выглядящий как обычный вызов функции, локальной или библиотечной. Поддерживающая системные вызовы библиотечная подпрофамма подготавливает соответствующее сообщение, помещает идентификатор процесса и адрес сообщения в регистры процессора и вызывает инструкцию int SYS386 VECT0R. Как было сказано выше, такая инструкция инициирует профаммное прерывание и управление передается подпрограмме s caU, перед вызовом которой в стек (в таблице процессов) заталкивается ряд регистров.

Первая часть процедуры s caU напоминает код save с раскрытыми макросами. Как и в коде save, процессор переключается на стек в ядре инструкцией mov esp. k stktop

и разрешаются прерывания. (Сходство программных и аппаратных прерываний проявляется и в том, что в обоих случаях прерывания перед входом в обработчик запрещаются.) Далее следует вызов процедуры sys call, которую мы рассмотрим разделом позже. Сейчас мы скажем только то, что она приводит к доставке сообщения и, следовательно, запускает планировщик. Таким образом, когда sys caU выполняет возврат, указатель proc ptr может не указывать на тот процесс, который сделал системный вызов. Далее, перед тем как передать управление в restart, инструкция cli запрещает прерывания, во избежание переполнения кадра стека запускаемого процесса.

Мы видим, что вызов restart происходит в трех случаях:

1. В функции main при запуске системы.

2. При выполнении перехода в функциях hwint master или hwint slave после аппаратного прерывания.

3. После выполнения системного вызова, за счет того, что s call не содержит завершающей инструкции.

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

mov esp. ( proc ptr)

заносит в регистр указателя стека ссылку на кадр стека в таблице процессов. Далее инструкция

lidt P LDTSEL(esp)

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



лежащие тому процессу, который будет запущен. Следующая инструкция загружает из записи очередного процесса адрес кадра стека, который будет использоваться при возникновении следующего прерывания, и помещает этот адрес в сегмент состояния задачи (TSS). Первая часть подпрофаммы restart не нужна в том случае, если прерывание произошло в момент работы кода ядра, поскольку стек ядра уже используется, и после завершения обработчика выполнение кода должно продолжиться с прерванного места. Метка restartl отмечает инструкцию, с которой в данном случае необходимо продолжить выполнение. В этом месте декрементируется значение переменной k reenter, обозначая тем самым новый уровень вложения прерываний, а последующие команды восстанавливают состояние процессора. Завершающие инструкции модифицируют стек так, чтобы игнорировался адрес возврата, помещенный в стек при вызове save. Если прерывание произошло во время выполнения пользовательского процесса, завершающая инструкция iretd передает управление следующему процессу в очереди планировщика. Но если управление было передано через restartl, то есть задействован не кадр стека, а стек ядра, это означает, что после завершения совсем не нужно переходить на новый процесс, а требуется завершить выполнение прерванного кода ядра. Процессор отслеживает подобную ситуацию, когда извлекает дескриптор сегмента кода из стека при выполнении iretd, и, обнаружив ее, оставляет в использовании стек ядра.

В файле mpx386.s есть еще несколько достойных обсуждения моментов. В дополнение к программным и аппаратным прерываниям, различные ошибки выполнения могут вызвать возникновение исключения. Исключения - это не всегда плохо. Они полезны, чтобы побудить систему предоставить некоторые дополнительные услуги, например выделить программе дополнительную память или зафузить в оперативную память страницу, перемещенную в область подкачки (хотя в MINIX подобные сервисы не предусмотрены). Но, как бы то ни было, когда исключение возникает, его нельзя игнорировать. Обрабатывают исключения так же, как и прерывания, то есть через дескрипторы в таблице дескрипторов прерываний. В этой таблице имеется шестнадцать записей, содержащих указатели на точки входа обработчиков исключений, начиная с divide error и заканчивая copr err, которую можно увидеть в конце файла mpx386.s. Каждая из этих точек входа передает управление процедуре exception или errexception, в зависимости от того, помещается ли при исключении в стек код ошибки или нет. Обработка во многом сходна с уже рассмотренным нами кодом: значения регистров сохраняются в стеке и вызывается функция .exception на языке С (обратите внимание на знак подчеркивания перед именем). Некоторые исключения игнорируются, некоторые вызывают сообщение о сбое ядра (kernel panic), другие посылают сигналы процессам. Самой функцией .exception мы займемся в следующем разделе.

Существует еще одна точка входа, которая обрабатывается как прерывание, это levelO call. Ее мы отложим до следующего раздела, когда будем обсуждать код, на который она переходит: levelO func. Эта точка входа находится в файле mpx386.s вместе с прерываниями и исключениями потому, что она также вызывается при помощи инструкции int. Как и обработчики исключений, она выполняет вызов save, а завершается инструкцией ret, ведущей к .restart. Последняя



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 [ 47 ] 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

© 2000 - 2018 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.