|
|||
Лабораторная работа №8. Многозадачный режим с управлением от клавиатурыЛабораторная работа №8. Многозадачный режим с управлением от клавиатуры 1 Цель и порядок работы Цель работы – изучить работу многозадачного режима с управлением от клавиатуры и научиться писать программы с его использованием. Порядок выполнения работы: - ознакомиться с описанием лабораторной работы; - получить задание у преподавателя; - написать программу, ввести программу, отладить и решить ее на ЭВМ; - оформить отчет. 2 Краткая теория В многозадачной системе должна быть предусмотрена возможность переключения с задачи на задачу по каким-либо событиям, например, истечению заданного кванта времени или командам с клавиатуры. Рассмотрим детально процесс переключения задач в условиях действия аппаратных прерываний. Рисунок 8.1 – Взаимодействие вычислительных объектов в процессе переключения задач
На рисунке 8.1 схематически изображено взаимодействие программно-аппаратных элементов при выполнении программы, которая носит "полусинхронный" характер – вторую задачу можно запустить в произвольный момент нажатием клавиши, однако дальше активизированная задача становится неуправляемой, выполняясь до завершающей команды irеt. При загрузке компьютера ведущий контроллер прерываний (IRQ0 – IRQ7) отображается на прерывания начиная с базового вектора 08h (int 08h – int 0Fh), ведомый (IRQ8 – IRQ15) – 70h (int 70h – int 77h). Номера прерываний, на которые отображаются аппаратные прерывания (IRQ), вызываемые первым контроллером по умолчанию, совпадают с номерами некоторых исключений. Конечно, можно из обработчика опрашивать контроллер прерываний, чтобы определить, выполняется ли обработка аппаратного прерывания или это исключение, но Intel рекомендует перенастраивать контроллер прерываний так, чтобы никакие аппаратные прерывания не попадали на область от 0 до 1Fh (зарезервировано для исключений). Поэтому необходимо выполнить перепрограммирование контроллера прерываний, как это рассматривалось в одной из предыдущих работ. Исходная задача 0 выполняется в условиях установленных флагов NT и IF. Начальное состояние флага NT особого значения не имеет, а прерывания, естественно, должны быть разрешены. Приход сигнала аппаратного прерывания приводит к сбросу флагов NT и IF, занесению вектора прерванного процесса в стек задачи 0 и передаче управления через шлюз прерывания на обработчик прерываний от клавиатуры. В примере он находится в том же сегменте, что и задача 0. В качестве адреса возврата в стеке сохраняется адрес очередной команды (на рисунке он обозначен меткой а:). В обработчике после анализа кода нажатой клавиши выполняется команда дальнего косвенного вызова call dword ptr task1_offs для которой в качестве сегментного адреса перехода указан селектор сегмента состояния задачи 1 TSS_1. На рисунке эта команда условно обозначена call tss_l, чтобы подчеркнуть, что вызов осуществляется через сегмент состояния задачи. Команда call инициирует действия по переключению задач: сегмент состояния первой задачи TSS_0 заполняется текущим контекстом, а содержимое TSS_1 используется в качестве исходного контекста для запуска задачи 1. В этой процедуре есть два тонких момента. Во-первых, в TSS_0 загружается фактически не контекст задачи 0, а контекст обработчика прерываний к моменту выполнения команды call. В частности, в качестве точки возврата указывается адрес возврата в обработчик (метка b:), а в слове флагов сброшены флаги NT и IF (оба флага сбрасываются процессором при получении сигнала аппаратного прерывания). Если обработчик изменил содержимое каких-то регистров, то в TSS попадут именно эти измененные значения. Другая особенность первого переключения на задачу 1 заключается в том, что контекст задачи целиком берется из сегмента состояния задачи TSS_1, который должен быть предварительно программно инициализирован. В примере поле флагов (по относительному адресу 24h) содержит 0. Следовательно, при выполнении задачи 1 будут запрещены прерывания. Что же касается флага NT, то, хотя в слове флагов TSS он был сброшен, задача 1 будет выполняться при NT=1, так как этот флаг устанавливается процессором при переключении задачи. Задача 1 выполняется до завершающей команды iret, которая при NT=1 инициирует обратное переключение задач, получив из области связи текущего TSS_1 адрес "предыдущего" TSS_0. TSS_1 заполняется текущим контекстом задачи 1, при этом в поле для EIP записывается адрес команды, следующей за командой iret (метка с: на рисунке). Из TSS_0 берется контекст возврата (фактически - контекст обработчика прерываний) и управление передается на метку b: обработчика. Программа обработчика продолжается и, поскольку флаг NT сброшен, завершающая команда iret передает управление не через сегмент состояния задачи, а через стек, обеспечивая правильный возврат в исходную задачу на метку а:. Последующие переключения на задачу 1 (по командам с клавиатуры) будут передавать управление на метку с:. Поскольку по этому адресу у нас находится команда безусловного перехода на начало программы (метка t_l на рисунке), задача будет выполняться правильно. В рассматриваемом примере разрушение контекста задачи 0 обработчиком и сохранение в TSS_0 "неправильного" контекста не приводит к нарушению работы всего комплекса, если только в начале программы обработчика сохраняются (например, в стеке) все используемые в нем регистры, а перед завершающей командой iret выполняется их восстановление. С другой стороны, задача 1 выполняется при запрещенных прерываниях, и воздействовать на ход ее выполнения нельзя – она всегда будет выполняться до команды iret. Для того чтобы получить возможность переключаться не только на задачу 1, но и из нее, надо выполнять эту задачу при разрешенных прерываниях. Прерывания можно разрешить как в самой задаче командой sti, так и в TSS задачи, записав в него исходное слово флагов с установленным флагом IF. Теперь нажатие клавиши в процессе выполнения задачи 1 снова активизирует обработчик прерывания, в котором, проанализировав код нажатой клавиши, можно выполнить команду переключения на другую задачу. Дескриптор сегмента состояния задачи может описывать TSS двух типов – свободные (код 9) и занятые (код Bh). Создавая в программе для каждой задачи сегменты состояния, объявляем их свободными. Команда call, осуществляя переключение на задачу, изменяет атрибут в дескрипторе соответствующего TSS, объявляя его занятым. TSS останется занятым до тех пор, пока в этой задаче не будет выполнена команда iret возврата назад по цепочке связанных задач. При этом команда call может активизировать только задачу со свободным TSS; при попытке вызывать командой call задачу с занятым TSS возбуждается исключение общей защиты. Из вложенной задачи с помощью команды call можно активизировать следующую задачу, но нельзя вернуться назад – для этого нужна команда iret. Для того чтобы обойти указанное ограничение, можно воспользоваться командой дальнего перехода через сегмент состояния задачи. Команда jmp, выполняя переключение задачи, не включает ее в связный список вложенных задач и, соответственно, но изменяет тип дескриптора TSS. Поэтому с помощью команды jmp можно осуществлять переключение задач как "вперед" – на новую задачу, так и "назад" – на ту задачу, из которой была активизирована данная. Рассмотрим пример многозадачной системы, в которой переключение задач осуществляется по командам с клавиатуры с помощью команд дальнего косвенного перехода jmp. Предусмотрим в программе главную задачу 0, в которой будет осуществляться инициализация всей системы, а также две демонстрационные задачи 1 и 2. Поначалу ради простоты расположим все задачи в одном сегменте команд, и будем считать, что всем задачам доступен общий сегмент данных.
Пример 8.1 – Многозадачный режим с переключением от клавиатуры ;Многозадачный режим с переключением от клавиатуры and ax, 0F000h and ax, 0F00h and ax, 0F0h and ax, 0Fh pop ax gdt_size = $-gdt_null idt trap 10 dup (<dummy_exc>); Исключения 0 - 7 имеют общий обработчик mes1 db 22,22,22," task_1 ",22,22,22 ; Строка выводимая задачей 1 mes2 db 22,22,22," task_2 ",22,22,22 ; Строка выводимая задачей 2 ; Переменные для маскирования прерываний data_size = $-gdt_null ;Послать EOI контроллеру прерываний ;Проверяем введенный символ cmp dl, 2 ; 02h = Клавиша <1> cmp dl, 3 ; 03h = Клавиша <2> jmp out_09h mov al, 20h ; Сигнал EOI mov al,20h ; Сигнал EOI для start: shl eax, 4 ; TSS 0 заполнится автоматически ; Заполним поля TSS 1 ; Установим флаг IF и сохраним регистр флагов EFLAGS в EAX mov [dword ptr tss_1+24h], eax ; EFLAGS
; Запрет аппаратных прерываний и NMI in al, 70h ;Перепрограммируем ведущий контроллер IRQ0-IRQ7 mov dx, 20h ; Поpт ведущего контpоллеpа inc dx ; Второй порт контроллера (21h) mov al, 20h ; СКИ2 - базовый вектоp mov al, 4 ; СКИ3 - ведомый подключен к IRQ2 (4 = 000000100) mov al, 1 ; СКИ4 - 80х86, пpогpаммная генеpация EOI ;Перепрограммируем ведомый контроллер IRQ8-IRQ16 mov dx, 0A0h; Поpт ведомого контpоллеpа inc dx ; Второй порт контроллера 0A1h mov al, 28h ; СКИ2: базовый вектор mov al, 2 ;СКИЗ: ведомый подключен к IRQ2 mov al, 1 ; СКИ4 - 80х86, пpогpаммная генеpация EOI ; Маскируем прерывания ведущего контроллера ; Маскируем прерывания ведомого контроллера ;Подготовка к загрузке IDTR
mov eax, CR0 db 0EAh mov ax, 24 mov ax, 32 ;Загрузим селектор TSS 0 в регистр TR ;Разрешим аппаратные и немаскируемые прерывания ; Задача 0
mov ax,0ffffh ;Закрыть линию A20 ; Запрет аппаратных прерываний и NMI ;Возврат в реальный ражим db 0EAh mov ax, @stack ;Восстановим значение IDTR для работы в реальном режиме ;Восстановим обратоно контроллер прерываний inc dx ; Второй порт контроллера (21h) mov al, 08h ; СКИ2 - базовый вектоp mov al, 4 ; СКИ3 - ведомый подключен к IRQ2 (4 = 000000100) mov al, 1 ; СКИ4 - 80х86, пpогpаммная генеpация EOI ;Перепрограммируем ведомый контроллер IRQ8-IRQ16 (по умолчанию int 70h - int 77h) inc dx ; Второй порт контроллера 0A1h mov al, 2 ;СКИЗ: ведомый подключен к IRQ2 mov al, 1 ; СКИ4 - 80х86, пpогpаммная генеpация EOI ; Восстановим маски прерываний ; Маскируем прерывания ведомого контроллера ;Разрешим аппаратные и немаскируемые прерывания
mov ah, 09h ;Вывод на экран ;Задержка pop cx jmp a1 ; Задача 2: выводит на экран сообщение mes2 ;Вывод на экран ;Задержка pop cx
В таблице глобальных дескрипторов предусмотрены три дескриптора gdt_tss_0, gdt_tss_l и gdt_tss_2 дня сегментов состояния задач. Размер TSS - 104 байта (граница равна 103), тип дескриптора – сободные TSS МП 486. В таблице IDT на месте, соответствующем вектору клавиатуры, расположен дескриптор - шлюз прерывания для вызова обработчика new_09h. Три двухсловных ячейки t0_addr, t1_addr и t2_addr предназначены для команд дальних косвенных переходов, реализующих переключение задач. Первые слова этих ячеек игнорируются, во-вторых, записаны селекторы сегментов состояния задач TSS_0, TSS_1 и TSS_2. Сообщения с именами mes1 и mcs2 предназначены для вывода задачами 1 и 2. Сообщения украшены с обеих сторон символами полосочек (код ASCII 22). Обработчик прерываний от клавиатуры анализирует коды нажимаемых клавиш и осуществляет переключение на задачи 0, 1 или 2 при нажатии клавиш <0>, <1> и <2>, соответственно. Поскольку при запуске программы начинает работать задача 0, первое переключение должно быть на задачу 1 или 2. После этого задачи можно переключать в любом порядке, хотя недопустимо переключение на активную задачу. На этапе инициализации следует обратить внимание на инициализацию сегментов состояния переключаемых задач. TSS задачи 0, как и раньше, не инициализируем: он будет заполнен процессором при первом переключении на задачи 1 или 2. TSS задач 1 и 2 инициализируются почти одинаково. В TSS заполняются поля CS (селектором общего сегмента команд), IP (относительными адресами задач), SS (селектором общего сегмента стека), DS (селектором общего сегмента данных), ЕS (селектором видеобуфера). В поля для SP заносятся разные смещения в пределах одного сегмента стека, размер которого увеличен в три раз». Таким образом, фактически задачи будут работать на разных стеках, что необходимо, поскольку переключения по аппаратным прерываниям могут происходить в произвольные моменты времени. Для того чтобы вход в задачи 1 и 2 происходил при разрешенных прерываниях, в поля для EFLAGS заносится текущее содержимое EFLAGS после того, как командой sti явно разрешены прерывания. Задачи 1 и 2 практически одинаковы. В каждой из них выводится на экран мигающая строка текста, для чего организован цикл из двух шагов. В одном шаге цикла строка выводится с одним атрибутом, в другом шаге – с другим. Завершающая команда jmp обеспечивает бесконечное выполнение каждой задачи. Таким образом, в программе не предусматривается "естественное” завершение задач, что потребовало бы их заметного усложнения. Рассмотрим ход выполнения программного комплекса, а также его возможности и недостатки (рис. 8.2). Исходная задача 0, как и в предыдущем примере, выполняется в условиях установленных флагов NT и IF. Приход сигнала аппаратного прерывания приводит к сбросу флагов NT и IF, занесению вектора прерванного процесса в стек задачи 0 и передаче управления через шлюз прерывания на обработчик прерываний от клавиатуры. В качестве адреса возврата в стеке сохраняется адрес очередной команды задачи 0 (на рисунке он обозначен меткой а:). В обработчике после анализа кода нажатой клавиши с помощью команды дальнего косвенного перехода Jmp dword ptr task1_offs выполняется переключение на задачу 1. Сегмент состояния главной задачи TSS_0 заполняется текущим контекстом, а содержимое TSS_1 используется в качестве исходного контекста для запуска задачи 1. Поскольку в момент переключения выполняется программа обработчика, в TSS_0 в поле для EIP записывается адрес команды обработчика, следующей за командой jmp и помеченной на рисунке меткой b. Задача 1 представляет собой бесконечный цикл, поэтому она будет выполняться до тех пор, пока пользователь не подаст с клавиатуры команду переключения задач. Приход прерывания от клавиатуры, как и раньше, приводит к сбросу флагов NT и IF, занесению вектора прерванного процесса в стек задачи 1 и передаче управления на обработчик прерываний от клавиатуры. Пусть пользователь подал команду возврата в задачу 0 (нажав на клавишу <0>). Обработчик, проанализировав код нажатой клавиши, передает управление на команду переключения на задачу 0 (на рисунке она обозначена, как jmp tss_0). Эта команда инициирует в процессоре процедуру переключения, в процессе которой TSS задачи 1 заполняется текущим контекстом, а содержимое TSS_0 используется в качестве исходного контекста для запуска задачи 0. Но в TSS_0 в качестве адреса возврата указано значение метки b в обработчике.
Рисунок 8.2 – Взаимодействие вычислительных объектов в многозадачном комплексе
Таким образом, происходит переключение не на задачу 0, а на обработчик. После каждой команды переключения jmp в обработчике стоит команда перехода на завершающую команду iret. Поскольку флаг IF сброшен, команда iret выполняет обычный возврат через стек текущей задачи, т.е. задачи 0. В результате происходит возврат в задачу 0 в ту точку, где она была прервана при переключении на задачу 1. Все три задачи нашего программного комплекса вполне равноправны, поэтому, подавая соответствующие команды с клавиатуры, можно переключать их в произвольном порядке. Правда, в программе не предусмотрена защита от повторного переключения на активную задачу. Если попытаться выполнить такое переключение, будет возбуждено исключение общей защиты. Не предусмотрено также завершение задач 1 или 2 – они прекращают работу при завершении главной задачи. Отмеченные выше недостатки являются следствием примитивности нашей системы, которая предназначена не для изучения принципов построения многозадачных систем, а лишь для демонстрации техники переключения задач и знакомства с соответствующими средствами микропроцессора. Однако и с этой точки зрения приведенный пример далек от совершенства. Одним из слабых его мест является использование всеми задачами комплекса общего сегмента данных. Так, обработчик прерываний выполняет переход на задачи через ячейки с селекторами TSS, расположенные в сегменте данных главной задачи; задачи 1 и 2 выводят сообщения, также хранящиеся в общем сегменте данных. Казалось бы, задачи 1 и 2 вполне могут использовать собственные сегменты данных, так как при переключении задач происходит сохранение старого контекста (включая сегментные регистры) и загрузка нового. Однако вызов обработчика аппаратных прерываний происходит без переключения контекста (заменяется только содержимое CS:IP); обработчик фактически работает на контексте прерванной задачи. Поэтому манипуляции с сегментными регистрами в задаче могут оказаться фатальными для се работоспособности. Впрочем, здесь нет ничего нового: и в реальном режиме при входе в обработчик прерываний сегментные регистры адресуют сегменты прерванного процесса. Рассмотрим пример, в котором несколько снижена взаимозависимость операционных сред задач и обработчика прерываний. Ниже описаны лишь изменения, внесенные в программу примера 8.1. Из общего сегмента данных удалены ячейки t0_addr, tl_addr и t2_addr с адресами TSS задач, а также сообщения задач mesl и mes2. Адреса TSS задач перенесены в процедуру обработчика прерываний, а сообщения задач – в процедуры задач.
Пример 8.2 – Модифицированная программа обработчика прерываний клавиатуры proc new_09h ;Чтение скан-кода ;Обработка прерывания клавиатуры ;Послать EOI контроллеру прерываний ;Проверяем введенный символ cmp dl, 2 ; 02h = Клавиша <1> cmp dl, 3 ; 03h = Клавиша <2> jmp out1 Команды вида jmp dword ptr cs:t0 с заменой сегмента и адресацией через сегмент команд возможны лишь при условии, что сегмент команд объявлен с разрешением исполнения и чтения (тип дескриптора 5). В первых программах, где сегмент команд объявлялся только исполняемым (тип 4), такая адресация привела бы к нарушению общей защиты. Байт атрибутов 1 должен иметь значение 9Ah. При инициализации сегментов состояния задач изменено содержимое DS: поскольку поля данных задач размещены в их же процедурах, в DS загружается селектор сегмента команд:
Пример 8.3 – Модифицированная инициализация полей TSS ; Заполним поля TSS 1
В задачах 1 и 2 вывод сообщений на экран выполняется непосредственно из сегмента команд. Поскольку регистры DS и ES настраиваются автоматически в процессе переключения, требуется только настроить SI и DI (для выполнения команд обработки цепочек):
Пример 8.4 – Модифицированная процедура задачи 1 proc task1 ;Вывод на экран ;Задержка pop cx jmp a1 mes1 db 22,22,22," task_1 ",22,22,22 ; Строка выводимая задачей 1
Аналогично выглядит и задача task2. Введем теперь в программу защиту от повторного переключения на текущую задачу. Фактически нам надо после каждого нажатия клавиш <0>, <1> или <2> выяснять, какая задача является текущей и не допускать переключения, если с клавиатуры случайно подана команда переключения на ту задачу, которая как раз выполняется. Узнать, какая задача является текущей, просто: достаточно прочитать содержимое регистра задачи TR. В нем всегда находится селектор сегмента состояния текущей задачи. Преобразуем процедуру обработчика прерывания от клавиатуры, введя в нее анализ регистра TR. Ниже приводится только модифицированный обработчик прерываний от клавиатуры. Остальной текст программы не изменился.
Пример 8.5 – Обработчик прерывания от клавиатуры с анализом регистра состояния задачи proc new_09h ;Чтение скан-кода ;Обработка прерывания клавиатуры ;Послать EOI контроллеру прерываний ;Проверяем введенный символ cmp dl, 2 ; 02h = Клавиша <1> cmp dl, 3 ; 03h = Клавиша <2> jmp out1 pop dx jmp out_09h pop dx jmp out_09h pop dx Если обработчик фиксирует нажатие клавиши <0> (из порта 60h получен скен-код 0Bh), выполняется чтение TR, для чего предусмотрена специальная команда str. Полученное значение сравнивается с известным нам селектором сегмента состояния задачи 0 (число 40). В случае равенства осуществляется выход из обработчика без выполнения каких-либо действий. Точно так же обрабатываются команды <1> и <2>. 3 Контрольные вопросы 1. Каково взаимодействие вычислительных объектов в процессе переключения задач? 2. Как осуществляется вызов обработчика прерываний от клавиатуры? 3. Как инициализируются сегменты состояния задачи? 4. Как ввести в программу защиту от повторного переключения на текущую задачу? 5. Какова роль флага NT в многозадачной среде? 4 Задание 1. Изучить теоретические основы использования многозадачного режима и переключения задач по прерыванию от клавиатуры. 2. Набрать и отладить и протестировать предложенную программу (пример 8.1) с использованием переключения задач. При этом обратить внимание на следующие этапы: 2.1. Определение таблицы дескрипторов и дескрипторов используемых сегментов. 2.2. Определение таблицы прерываний. 2.3. Определение переменных для переключения задач и маскирования прерываний. 2.4. Определение и структура обработчика системного таймера (IRQ0). 2.5. Определение и структура обработчика прерывания клавиатуры (IRQ1). 2.6. Подготовка дескрипторов сегментов TSS и TSS задач 2.7. Перепрограммирование ведущего контроллера прерываний. 2.8. Маскирование прерывания ведущего и ведомого контроллера. 2.9. Определение и структура задач. 2.10. Процесс переключение между задачами. 2.11. Восстановление настроек контроллера прерываний. 2.12. Восстановление масок прерываний. 3. Отладить и протестировать полученную программу. 4. Модифицировать пример 8.1 в соответствии с примером 8.2, отладить и протестировать программу. 5. Модифицировать пример 8.1 в соответствии с примером 8.3, отладить и протестировать программу. 6. Оформить отчёт. 5 Содержание отчета 1. Титульный лист. 2. Краткое теоретическое описание. 3. Задание на лабораторную работу. 4. Листинг программы. Результаты выполнения работы.
|
|||
|