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

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

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

Различные варианты реакции процессора на прерывания при выполнении кода ядра определяются уровнями привилегий, упомянутыми выше. Когда уровень привилегий прерванного кода совпадает с уровнем обработчика, действует упрощенный механизм. Механизм, ориентированный на TSS и создание нового стека, применяется только в том случае, когда прерванный код менее привилегирован, чем обработчик. Уровень привилегий запоминается в селекторе сегмента кода, который сохраняется в стеке, что и позволяет при возврате из прерывания определять, какой механизм будет применяться. При создании нового стека аппаратно решается еще одна задача. Проверяется, достаточно ли новый стек велик для того, чтобы вместить необходимый минимум информации. Это предотвращает случайный (или намеренный) крах ядра, когда пользователь делает системный вызов при несоответствующем размере стека. Такая функциональность встроена в процессор специально для того, чтобы реализовывать мультипрофаммные операционные системы.

Описанное выше поведение может показаться непонятным непосвященным в архитектуру 32-битных процессоров Intel. Обычно мы будем стараться избегать подобных деталей, но для понимания того, как осуществляются переключения между процессами, существенно важно вникнуть в механизм обработки прерываний и работы инструкции iretd. То, что значительная часть работы выполняется аппаратно, упрощает жизнь программиста и, по видимости, делает систему более эффективной. С другой стороны, такая помощь со стороны аппаратного обеспечения усложняет осознание того, что происходит, для того, кто читает код.

Аппаратные прерывания различает только крохотная часть MINIX. Ответственный за это код находится в файле mpx386.s. Для каждого прерывания в этом файле имеется точка входа. Так, обработчики от hwintOO до hwint07 вызывают hwint master, а обработчики от liwint08 до hwintl5 вызывают hwint slave. Каж-



дая точка входа передает вызову аргумент, указывающий, какое из устройств требует внимания. Первое, что делает hwint master, это вызов save. Эта подпрограмма помещает в стек значения вех регистров, необходимых для восстановления прерванного процесса. Ее можно было бы переписать как часть макроса, чтобы увеличить скорость, но это увеличило бы размер макроса более чем в два раза, и, кроме того, подпрограмма save необходима для вызовов, осуществляемых другими функциями. Как мы увидим, save занимается тем, что хитро манипулирует со стеком. После возврата из hwint master используется стек, выделенный ядром, а не кадр стека в таблице процессов. Следующие действия блокируют контроллер прерываний, чтобы до завершения обработчика не было принято прерывание от того же источника. Это делается путем блокирования способности контроллера отвечать на сигнал по одному из входов; команды процессора для запрета обработки всех прерываний здесь пока не применяются.

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

Примеры низкоуровневого кода драйверов мы увидим в следующей главе. Тем не менее для понимания того, что происходит в hwint master, мы упомянем, что низкоуровневый код может вызывать функцию interrupt (ее код находится в ргос.с, мы рассмотрим этот файл в следующей главе). Последняя преобразует прерывание в сообщение для задачи, обслуживающей вызвавшее прерывание устройство. Кроме того, interrupt затрагивает планировщик и может сделать так, чтобы нужная задача следующей получила управление. После возврата из специфичного для устройства кода процессору при помощи инструкции сИ вновь запрещается обрабатывать прерывания. Контроллер прерываний при этом опять подготавливается к тому, чтобы, как и раньше, отвечать на сигналы от вызвавшего текущее прерывание устройства. Затем hwint master завершается инструкцией ret. В данный момент происходит нечто не совсем очевидное. Если процесс был прерван, то в этой точке используется стек ядра, а не стек в таблице процессов. Это приводит к тому, что задача, сервер или пользовательский процесс снова получает процессор. Получивший управление процесс может и не быть тем процессом, который был прерван. Более того, это, скорее всего, будет другой процесс. Низкоуровневый код вправе вмешаться в очереди планировщика. Таким образом, мы видим самое сердце механизма, обеспечивающего иллюзию нескольких одновременно работающих процессов.

Для полноты упомянем, что в том случае, когда прерывание происходит в тот момент, когда выполняется код ядра, уже используется стек ядра. Поэтому функция save помещает в стек адрес restartl. Тогда после завершения hwint master



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

Процедура hwint slave отличается от hwint master только тем, что в ней необходимо восстанавливать два контроллера, и подчиненный и основной, так как они оба блокируются при приеме подчиненным контроллером сигнала. Здесь вы можете увидеть несколько тонких моментов профаммирования на ассемблере. Прежде всего обратите внимание на инструкцию

jmp .+2

Эта инструкция означает переход на следующую команду. Она помещена в код исключительно для того, чтобы добавить небольшую задержку. Разработчики BIOS IBM PC решили, что между последовательными инструкциями ввода/вывода необходима задержка, и мы следуем их рекомендациям, хотя для современных компьютеров это может быть ненужным. Подобные тонкости - одна из причин, по которой некоторые считают работу с аппаратным обеспечением чем-то сродни колдовства. Далее вы можете увидеть инструкцию с числовой меткой:

0: ret

По этой метке четырьмя строками позже осуществляется условный переход: jz Of

Запись Of означает не шестнадцатеричное число, а так называемую локальную метку. Здесь это переход вверх по коду к ближайшей инструкции с меткой 0. Обычные метки с чисел начинаться не могут. В этом же файле есть еще один интересный и способный запутать момент, шестью десятками строк выше в hwint master. Ситуация тут еще более запутана, чем может показаться на первый взгляд, так как метки находятся внутри макросов, которые разворачиваются перед тем, как код передается ассемблеру. Таким образом, ассемблер видит в означенном коде 16 меток 0:. Каждый раз, когда ассемблеру будет встречаться ссылка на подобную метку, будет использоваться ближайшая из них, поэтому различные метки не будут конфликтовать. Возможность применять метки в макросах, несомненно, и явилась причиной существования локальных меток.

Теперь двинемся дальше и рассмотрим процедуру save, уже несколько раз упомянутую ранее. Ее имя описывает ее функции, заключающиеся в том, чтобы сохранять контекст прерванного процесса в выделенный процессором стек (в кадре стека внутри таблицы процессов). Кроме того, для поддержки вложенных прерываний save использует переменную k reenter, чтобы подсчитывать число вложений и определять их наличие. Если процесс был прерван, инструкция

mov esp. k stktop

переключается на стек в ядре, а следующая за ней инструкция помещает в стек адрес подпрофаммы restart. В противном случае, если уже используется стек ядра, в него заносится адрес restartl. Независимо от того, какая ветвь алгоритма была выполнена, для возврата из процедуры не годится обычная инструкция ret.



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 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.