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

Textnew2

.pdf
Скачиваний:
13
Добавлен:
06.02.2018
Размер:
1.48 Mб
Скачать

7.1.Состав системных средств

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

Всовременных компьютерах типа IBM для отображения информации в текстовом режиме используются специальные диапазоны адресов общего пространства оперативной памяти. В простейшем и наиболее употребительном случае это диапазон действительных (физических) адресов, начинающийся со значения 0B8000h. В наиболее употребительном варианте 25 строк по 80 символов сам диапазон простирается на 4000 байтов, по два байта для позиции символа на экране. Каждые два таких байта задают последовательно собственно код символа и его атрибуты. Атрибуты определяет вид символа. В простейшем случае они задают только цвет фона в позиции символа и цвет линий его изображения. Самый старший бит в байте атрибутов ранее мог быть настроен на обеспечение автоматического мигания символа (вне зависимости от мигания или нет соседних), но сейчас с этой целью уже практически не используется (эта возможность требовала установки специального режима MS-DOS).

Вбайте атрибутов младшая половина выделена для задания цвета линий в изображении символа, а старшая - для цвета фона символа. В пределах цветовой тетрады самый младший бит определяет синий цвет, следующий - зеленый цвет, последующий - красный цвет, а старший бит задает повышенную интенсивность цвета. Поэтому код атрибута 1Eh (т.е. 00011110В) задает только бит синего цвета для фона, а также интенсивность, красный и зеленый цвет для линий символа (иначе называемый цветом переднего плана - foreground). Совокупность последних трех битов обеспечивает желтый цвет. Таким образом, данный атрибут задает желтый цвет на синем фоне.

Вреальном режиме для использования описанного средства можно использовать следующий прием

mov ax, 0b800h mov es, ax

mov [es:смещение_начала_позиции], код_символа

mov [es:смещение_начала_позиции+1], значение_атрибута

причем значение смещение_начала_позиции определяется по номеру M строки и номеру N столбца для символа как 160*M+2*N, где номера строки и столбца базируются нулем. (В режиме 80 символов в строке.)

Вчастности для записи символа '@' во третью (с обычным отсчетом от единицы) позицию второй строки ярко-красным цветом на зеленом фоне следует последние две команды фрагмента записать как

mov [es:164], '@' mov [es:165], 2Ch

Для программного доступа к электронной аппаратуре компьютера служат специальные регистры, которые, с одной стороны, являются обычными электронными

131

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

IN AL, номер порта или обозначение регистра DX

IN AX, номер порта или обозначение регистра DX а для вывода в порт вывода предназначены команды

OUT AL, номер порта или обозначение регистра DX

OUT AX, номер порта или обозначение регистра DX

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

Следует заметить, что доступ непосредственно к аппаратуре - это очень серьезное мероприятие и полноценные операционные системы категорически запрещают такие действия для любых прикладных программ. Но эти возможности оказываются не запрещены для прикладных программ в ОС MS-DOS и даже в MS Windows 9x. (В Unix и Windows NT такие средства недоступны и очень основательно запрещены.)

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

Упомянутый специальный порт маски (в основном контроллера прерывания) задает - битом с номером 1 - разрешение или запрет прохождения сигнала от клавиатуры для обработки этого сигнала процессором. Причем единичное значение бита является запрещающим, а нулевое - разрешающим. Сам этот порт имеет номер 21h (никакого отношения к основному номеру группы функций MS-DOS это значение не имеет, совпадение здесь случайное).

132

Для временного запрета поступления сигнала от клавиатуры (и следовательно, осуществления самого ввода с клавиатуры) может быть использована последовательность команд.

in al, 21h

or al, 00000010B

out 21h,al ; установка бита блокировки ввода с клавиатуры

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

В качестве примера для MS-DOS или Windows 9x можно предложить следующий фрагмент

. . .

in al, 21h

or al, 00000010B

out 21h,al ; установка бита блокировки ввода с клавиатуры ;--- вывод средствами API текста сообщения о блокировки ввода

. . .

;задержка в простейшем случае с помощью многократного цикла mov ecx,0fffffffh

pov: mul bx

loop pov,ecx

in al, 21h

and al, 11111101B

out 21h,al ; сброс бита блокировки ввода с клавиатуры

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

7.2. Обработчики программных прерываний в MS-DOS

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

133

Внутренние прерывания - это такие, которые могут происходить по ходу выполнения некоторых команд. Характерным примером является выполнение команды DIV с таким значение делителя, для которого результат не может быть размещен в регистре, требуемом конкретным вариантом команды деления (в регистре EAX, AX или AL). Например, при 16-битном делении на единицу делимого 100000 в регистрах DX,AX, результат имеет больше значащих цифр, чем может поместиться в 16-битном регистре. "Вырожденным", хотя и возможным вариантом подобной ситуации, является деление на нуль. Аппаратура процессора, выполняющая подобные деления, реагирует формированием прерывания с номером 0, для которого в нормально работающей ОС должна быть соответствующая процедура обработки (в простейшем случае - выдача сообщения о делении на нуль).

Влюбом случае для начала выполнения подпрограммы (процедуры) обработчика нужно, чтобы регистры CS, EIP задавали адрес начала этой подпрограммы. Поэтому совершенно необходимо, чтобы аппаратура процессора могла как-то автоматически перейти от номера прерывания к адресу процедуры обработчика. Значит, этот адрес должен быть где-то записан, чтобы автоматически соотносился с номером. Более того, такое соответствие в общем случае необходимо иметь для всех (или почти всех) возможных номеров прерываний.

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

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

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

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

ется не последовательностью команд, а записана внутри аппаратуры процессора. Внутренняя процедура прерывания последовательно выполняет следующие действия: вначале в стеке запоминается содержимое регистра флагов, затем - содержимое регистра CS, далее запоминается содержимое регистра указателя команд. Для реального режима таким образом вначале запоминается содержимое регистра FLAGS, затем - регистра CS и потом - регистра IP. (Действия в более сложном, чем реальный режим, будут рассмотрены позже.)

Далее в реальном режиме - по текущему номеру прерывания аппаратура извлекает содержимое соответствующего вектора прерывания и из младшей половины этого вектора заполняет регистр IP, а из старшей его половины - CS. (Кроме того,

134

сбрасывается специальный бит IF регистра флагов, который отвечает за прерывания - Interruption Flag).

Выполненные таким образом действия приводят к тому, что следующей командой, выбираемой обычным образом, будет первая команда обработчика прерывания. Для нормальной работы всей системы (с учетом описанных особенностей внутренней процедуры) подпрограмма обработчика должна завершать свои действия вызовом специальной команды IRET Команда IRET в реальном режиме снимает со стека целых шесть байтов. В обработчике прерываний совершенно необходимо использовать именно эту команду - вместо команды RET в обычных подпрограммах. Выполнение команды IRET снимает со стека информацию, заполняющую регистры FLAGS, CS и IP.

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

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

Использование системы прерывания с целью включения каких-то новых, отсутствующих действий, на ситуацию прерывания заключается - кроме построения собственно подпрограммы обработчика - в соотнесении нужного номера прерывания. Последние действия, в свою очередь, представляют собой запись нового содержимого вектора прерывания для соответствующего номера. Если нужно будет запускать обработчик с номером прерывания N, то требуется переформировать байты таблицы прерываний, имеющие адреса от 4*N до 4*N+3. Эти действия можно выполнить командами MOV, позаботившись о том, чтобы некоторый сегментный регистр указал на самое начало оперативной памяти. Следует отметить, что операционная система может содержать какие-то средства API для систематического изменения вектором прерывания (в старой ОС MS-DOS для этого служила функция 35h обращения через прерывание INT 21H).

На рис. 7.2.1 приведена ассемблерная программа для простейшей демонстрации возможностей системы прерываний. Эта программа запускает для работы пользовательский обработчик прерывания с номером 1Ch. Обработчик данного специального номера автоматически вызывается в ОС MS-DOS около 18 раз в секунду (точнее 18,2 раза в сек.). (Он вызывается командой INT 1Ch внутри системной процедуры обработчика аппаратных сигналов от таймера, которые в этой ОС возникают указанное число раз в секунду.)

135

SEGMENT .text org 100h

start: jmp sethandler ; procedure handler

handler: inc word [cs:count] cmp word [cs:count],50 jl nexta

mov word [cs:count],0 call beep

nexta: iret

;end handler count dw 0 ;procedure beep beep: pusha

mov dl,7 mov ah,2 int 21h popa

ret

;end procedure beep

endtsr:

sethandler:

push es ; store segment address of Our Program mov ax, 0

mov es, ax ; ES = базовый адрес Interrupt Vectors Table mov dx, handler

pop bx cli

mov [es:4*01Ch], dx mov [es:4*01Ch+2], bx sti

circ: jmp circ ; зацикливание текущего процесса или в MS-DOS

;вместо этого можно выполнить следующие две команды

;mov dx,endtsr

;int 27h

Рис. 7.2.1. Обработчик периодических прерываний в MS-DOS

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

136

автоматических вызовов нашего обработчика. При использовании простейшей организации памяти, в частности в микропроцессорных системах и системах реального режима, инициализирующую часть целесообразно размещать за собственно обработчиком. Тогда, используя специальные системные функции ОС, можно оставить в оперативной памяти только часть программы, которая и есть собственно обработчик, а участок памяти, содержащий инициализацию, - использовать далее уже другими программами. Для этих целей в MS-DOS служила специальная системная функция, вызываемая по прерыванию INT 27h, аргументом которой нужно было указать - до каких пор оставить участок с начала программы. Эта возможность представлена в примере двумя последними, закомментированными командами, при использовании которых следует удалить или закомментировать предыдущую команду "зацикливания" jmp circ.

Предполагается, что данный пример - после преобразования ассемблером NASMW в исполняемый файл типа COM - будет запускаться непосредственно в ОС MS-DOS. В частности, путем перезапуска компьютера в режиме эмуляции MS-DOS (но не непосредственно из под Windows 9x, что приводит к "подвешиванию" компьютера). Запущенная таким образом программа будет выдавать звуковой сигнал приблизительно каждые 2,5 секунды. Ее прекращение возможно только путем перегрузки компьютера.

Рассмотрим построение программы более подробно. В начале инициализирующей части содержимое регистра ES запоминается в стеке. В MS-DOS этот регистр при входе в программу содержит сегментный адрес начала программы в памяти. Далее в регистр ES заносится нулевое значение для последующего доступа к начальной области памяти, где находится таблица векторов прерывания. Из стека в регистр BX переносится сегментный адрес начала нашей программы (его нужно будет занести в старшую часть вектора прерывания). В регистр DX заносится смещение нашего обработчика с именем handler.

Далее выполняется очень важное действие. Командой CLI временно отключается доступ аппаратным сигналам к процессору, что блокирует возможное срабатывание системы прерываний на какие-то сигналы, пока наша программа что-то делает с такой важнейшей частью системы, как таблица прерывания. Этот прием будет и в дальнейшем систематически применяться: какие-то серьезные действия, затрагивающие центральные части ОС в простейших ОС всегда, а в более сложных, как правило, выполняются с полной монополизацией - со стороны текущей программы - основных средств ОС. Иначе возможны непредусмотренные взаимодействия с непредсказуемыми результатами. При временно отключенных прерываниях изменяется содержимое вектора прерывания с номером 1Ch.

Если бы мы временно не запрещали прерывания, то по сигналу от таймера, который мы не контролируем, могла бы вызываться процедура обработки, смещение для которой берется уже от нашей процедуры, а сегментный адрес остается еще от старой процедуры обработки. В результате мог произойти вызов несуществующей процедуры с непредсказуемыми последствиями. (Это произойдет, если сигнал прерывания от таймер возникнет при выполнении команды mov [es:4*01Ch],dx)

137

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

Собственно обработчик handler в каждом своем вызове инкрементирует значение своей внутренней переменной count, а затем проверяет не достигла эта переменная значения 50. Если не достигла, осуществляется просто выход из обработчика командой IRET. При достижении нулевого значения - переменная count обнуляется и вызывается служебная процедура beep, которая формирует звуковой сигнал. Этот сигнал вызывается путем вывода управляющего символа с кодом 07 в режиме телетайпа (функция 02 прерывания INT 21h). Заметим, что в обработчиках прерываний совершенно необходимо сохранение и восстановление используемых регистров, иначе срабатывание обработчика будет изменять значение регистров теоретически в любом месте другой программы, выполняемой в момент вызова обработчика.

Существенной особенностью рассмотренного обработчика является особая форма записи для доступа к именованной области данных count с помощью дополнительного указания сегментного регистра CS (в виде записи операнда [cs:count]).

Причиной этому является несоответствие содержимого автоматически используемого сегментного регистра DS - текущим данным при вызове обработчика. Дело в том, что в общем случае обработчик срабатывает, когда выполняется другая программа, которая использует сегментный регистр DS для автоматического доступа к своим данным. После срабатывания системы прерываний, этот регистр по прежнему будет задавать начало данных совсем другой программы. Внутренняя процедура обработки прерывания изменяет (правильно устанавливает) регистры CS и IP, но ничего не делает с другими регистрами. Поэтому в общем случае обработчик прерываний, если не позаботится о явном указании данных, будет использовать области с теми же смещениями, что нужно, но совсем в другом сегменте. Практическими решениями для реального режима являются два варианта. В первом все именованные данные обработчика размещаются в сегменте кода обработчика и доступ к ним задается в командах путем записи вида [CS: имя_данных]. Во втором варианте, построение обработчика ведется по следующей схеме

. . . ;вход в обработчик push ds

push ax

mov ax, имя_сегмента_данных_обработчика mox ds,ax

. . . ; собственно обработчик pop ax

pop dx iret

138

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

7.3.Особенности обработчиков аппаратных прерываний

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

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

Это решение имеет вид

. . .

pol: in al, порт_сигнала

test al, маска_сигнала_в_порту

jne pol

. . . чтение данных из другого порта нужных нам данных

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

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

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

139

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

Указанная аппаратура преобразования получила наименование контроллера прерываний (т.е. устройства управления прерываниями). Для сокращения мы будем обозначать этот контроллер как КНП. При детальном и содержательном анализе оказалась, что эта аппаратура не может быть сведена к простой логической схеме, собирающей несколько входных сигналов запроса прерываний в один сигнал на вход процессора. Дело прежде всего в том, что различным исходным сигналам прерываний, отражающих различные причины воздействия, должны однозначно соответствовать различные номера прерываний (на основе которых и выбирается тот или иной обработчик прерывания).

Получилось, что КНП должен выдавать процессору кроме самого сигнала прерывания еще и номер прерывания (в архитектуре Intel - как обычный байт данных). Причем для каждого входного сигнала - свой номер прерывания. Контроллеры прерываний, заложенные в структуру современных компьютеров, начали использоваться в конце 70-х годов XX века. Они представляли собой тогда большие интегральные электронные микросхемы, которые к концу этого века уже перестали казаться "большими" по своим функциям. Тем ни менее, функции этих микросхем КНП стали моделироваться более поздними и более сложными по возможностям электронными узлами - с целью обеспечения преемственности соответствующего системного обеспечения (возможностей новых модификаций ОС работать как на старом, так и на новом оборудовании).

В унаследованной таким образом функциональной ситуации до сих пор считается, что внутри компьютера типа IBM находятся два КНП, каждый из которых имеет только по 8 входов для аппаратных сигналов, причем эти КНП соединены последовательно. Один из этих КНП есть главный (Master), а второй вспомогательный (Slave). До сих пор существенно (используется во всех операционных системах, несмотря на усложнившиеся возможности управления аппаратурой), что порты главного КНП имеют номера 20h и 21h, а порты вспомогательного КНП - номера 0A0h и 0A1h. Меньший из этих номеров называется базовым. (В общем случае сложные контроллеры, создававшиеся в 70-е годы XX века, имели набор портов с последовательными номера, начинающимися с базового, причем не обязательно только два таких порта.)

Управление КНП осуществляется процессором с помощью универсальных команд IN и OUT при использовании указанных номеров портов. Заметим, что номера портов, следующие за базовыми, отвечают как раз внутренним регистрам масок этих портов, что уже использовалось в разделе 8.1. Базовый порт применяется для управления самим контроллером. Вся совокупность возможностей такого управления довольно сложна, мы ограничимся знакомством с минимальных набором, необ-

140

Соседние файлы в предмете Операционные системы