Важнейшей особенностью защищенного режима является возможность «параллельного» выполнения нескольких программ. Описанные выше механизмы защиты памяти позволяют создать операционную систему с эффективным использованием многозадачности. Хотя в принципе механизмы защиты памяти и механизмы переключения задач жестко друг с другом не связаны.
Под многозадачностью понимается поочередное
выполнение нескольких программ
(или фрагментов программ - подпрограмм). Переключение задач осуществляется или
программно (командами CALL или JMP), или по прерываниям (например, от таймера). В
последнем случае каждой задаче отводится определенный квант времени, после чего
управление передается следующей задаче (и так циклически), в результате чего
возникает иллюзия того, что все задачи выполняются враз. Задачей может
быть как целая программа, так и ее часть (например, некоторая процедура), или
программа обработки прерывания. При переключении на выполнение новой задачи
процессор сохраняет состояние текущей, с
тем, что бы потом возобновить ее выполнение. Основное отличие задач от
процедур лишь в том, что о каждой задаче процессор «знает» намного больше чем о
процедуре. Для хранения информации о задачи существует специальная структура – TSS (см. ниже). В то время как при
переходе к выполнению процедуры, процессор запоминает (в стеке) лишь точку
возврата (CS:IP).
Поддержка
многозадачности обеспечивается за счет следующих аппаратно поддерживаемых
структур и элементов:
· Сегмент состояния задачи (TSS).
· Дескриптор сегмента состояния задачи.
· Регистр задачи (TR).
· Дескриптор шлюза задачи.
Для сохранения состояния задачи определена такая
структура, как сегмент состояния задачи (TSS - Task State Segment), может
располагаться как в отдельном сегменте данных, так и внутри сегмента данных
задачи (формат TSS приведен на рисунке 5.3). TSS
описывается системным дескриптором, который может находиться только в GDT (рис
5.1).
Рис. 5.1 Дескриптор TSS (386+)
Поле TYPE для дескрипторов TSS
имеет значение:
· 1 - доступный сегмент состояния задачи 80286
(Available TSS-286);
· 3 - занятый сегмент состояния задачи 80286 (Busy
TSS-286);
· 9 - доступный сегмент состояния задачи 386+ (Available
TSS-386);
· B - занятый сегмент состояния задачи 386+ (Busy
TSS-386).
Селектор TSS текущей
задачи хранится в регистре TR. Регистр TR имеет "видимую" часть (т.е.
часть, которую может считывать и изменять программное обеспечение) и
"невидимую" часть (т.е. часть, обслуживаемую процессором и
недоступную программному обеспечению). Селектор, находящийся в видимой части,
индексирует дескриптор TSS в
GDT. Процессор использует невидимую часть регистра TR для приема туда значений
базы и границы из дескриптора TSS.
Хранение в регистре этих значений делает выполнение задачи более эффективным,
поскольку для ссылки к TSS
текущей задачи процессору не требуется извлекать эти значения из памяти.
Для каждой задачи (фрагмента кода), участвующей в переключении
создается свой TSS.
Поля TSS делятся на две
основные категории:
1. Динамические поля, обновляемые процессором при каждом переключении задачи. В число этих полей входят:
· Регистры общего назначения (EAX, ECX, EDX, EBX, ESP,
EBP, ESI и EDI).
· Сегментные регистры (ES, CS, SS, DS, FS и GS).
· Регистр флагов (EFLAGS).
· Указатель команд (EIP).
· Селектор для TSS предыдущей задачи (обновляется только
когда ожидается возврат).
2. Статические поля, которые процессор считывает, но не изменяет. Эти поля устанавливаются при создании задачи:
· Селектор для LDT задачи.
· Логический адрес для стеков привилегированных уровней
0,1,2.
· Бит T (бит отладочной ловушки), который, будучи
установленным, заставляет процессор устанавливать при переключении задачи отладочное исключение.
· Базовый адрес битового массива разрешения
ввода/вывода. При наличии, данный массив всегда хранится в TSS по старшим
адресам. Базовый адрес указывает на начало массива.
Свободное поле TSS
может использоваться по усмотрению операционной системы. TSS для процессоров 386+ содержит
элементы, отсутствующие в 80286: битовые карты разрешения ввода/вывода и
перенаправления прерываний, а также бит отладочной ловушки Т (Debug trap bit) (при T=1 переключение в данную
задачу вызывает исключение отладки). Последним элементом TSS-386+ должен быть байт 0FFh. Карту перенаправления прерываний
поддерживают только процессоры с расширением “VME”. Значение поля лимита
дескриптора для TSS-286 (см. рис 5.4) должно превышать 002Bh, а для TSS-386+ - 0064h. Карта разрешения
ввода/вывода (I/O Permission Bit Map), расположенная в конце TSS-386+, имеет по одному биту на
каждый адрес портов ввода/вывода. Разрешению обращения соответствует нулевое
значение бита. Максимальный размер таблицы (2000h), соответствующий всем 64К
адресов, может быть урезан лимитом TSS,
но байт-терминатор 0FFh должен обязательно вписываться в лимит TSS. Порты с адресами, не попавшими в
усеченную таблицу, считаются недоступными.
Дескриптор шлюза задачи
обеспечивает косвенные, защищенные ссылки к задаче. Формат шлюза задачи показан
на рисунке 5.2
Рис. 5.2. Дескриптор шлюза задачи
Поле TYPE для дескрипторов шлюза задачи имеет
значение:
· 5 - шлюз задачи 80286 (Task Gate);
· D - шлюз задачи 386+ (Task Gate);
Поле «Селектор» шлюза задачи индексирует дескриптор TSS. RPL в данном селекторе не используется.
DPL шлюза задачи управляет доступом к дескриптору для переключения задачи.
Процедура не может выбрать дескриптор шлюза задачи до тех пор, пока RPL
селектора и CPL процедуры не будут численно меньше или равны DPL дескриптора.
Тем самым предотвращается переключение задачи менее привилегированными, чем она
сама, процедурами. (Отметим, что при использовании шлюза задачи DPL дескриптора
TSS назначения не
используется).
Рис. 5.3. Сегмент состояния задачи (386+)
Рис. 5.4. Сегмент состояния задачи (286)
Доступ к дескриптору TSS не дает возможности читать или модифицировать
дескриптор. Чтение и модификация его возможны только путем отображения в тот же
адрес памяти дескриптора данных. Загрузка дескриптора TSS в сегментный регистр
вызывает исключение. Дескрипторы TSS могут находиться только в таблице GDT.
Попытка доступа к TSS при помощи селектора с установленным битом TI (который
обозначает текущую LDT) генерирует исключение.
Переключение задач выполняется по инструкции межсегментного перехода (JMP)
или вызова (CALL). Команды JMP, CALL, представляют собой
инструкции, которые могут быть использованы и при обстоятельствах, не
приводящих к переключению задачи. Для того чтобы произошло переключение задачи,
команда JMP или CALL
может передать управление либо дескриптору TSS, либо шлюзу задачи.
Эффект в обоих случаях одинаковый: процессор передает управление требуемой
задаче.
JMP dword ptr adr_sel_TSS(/adr_task_gate)
CALL dword ptr adr_sel_TSS(/adr_task_gate)
Операция переключения задач
сохраняет состояние процессора (в TSS
текущей задачи), и связь с предыдущей задачей (в TSS новой задачи), загружает состояние новой задачи и начинает
ее выполнение. Задача, вызываемая по команде JMP, должна заканчиваться
аналогичной командой обратного перехода. В случае использования команды CALL -
возврат должен происходить по команде IRET, которая сохраняет контекст
завершаемой задачи и загружает контекст прерванной.
Алгоритм работы команды IRET в случае возврата из прерывания и в случае
обратного переключения задач различен. И определяется значением флага NT (Nested Task).
Если флаг сброшен, то выполняется обычный возврат из прерывания (через
стек). Если флаг установлен, то команда IRET инициирует обратное переключение
задач.
После загрузки компьютера флаг NT находится в
установленном состоянии. Однако любое аппаратное прерывание или исключение
сбрасывает этот флаг, в результате чего команда IRET, завершающая обработчик,
выполняется в «облегченном» варианте (возврат через стек). То же происходит при
выполнении процессором команды программного прерывания INT. Поскольку команда
IRET восстанавливает исходное состояние регистра флагов, после завершения
обработчика флаг NT снова оказывается установленным (если, конечно, он не был явно
сброшен выполняемой программой).
При выполнении процедуры переключения на новую задачу через шлюз или
непосредственно через TSS, процессор сохраняет в TSS текущей задачи слово
флагов и устанавливает в регистре флагов бит NT. Команда IRET, завершающая задачу,
обнаруживает NT=1 и, вместо осуществления возврата через стек, инициирует
механизм обратного переключения задач.
Если вложенная задача, в свою очередь, выполняет переключение на
следующую задачу, текущее слово флагов с установленным битом NT сохраняется в
TSS текущей задачи. После завершения новой задачи это слово будет возвращено в
регистр флагов и, таким образом, задача будет продолжаться с NT=1, что
обеспечит ее правильное завершение.
Задачи могут иметь произвольную вложенность, можно провести аналогию с
процедурам в языках высокого уровня. Да и логика программно переключаемых задач
не сильно отличается от процедур. Есть всего одно существенное отличие. После
выхода из процедуры точка входа по-прежнему указывает на ее начало. В случае с
переключением задач - на следующую, после команды обратного переключения,
команду (то есть, возможно, что фактически никуда). Поэтому при повторном
вызове задачи необходимо откорректировать ее TSS на предмет корректности точки входа.
При переключении задачи процессор выполняет следующую
последовательность действий:
1.
Проверяет право на переключение по уровню привилегий CPL
£ DPL;
2.
Сохраняет в TSS исходной
задачи ее контекст;
3.
Загружает в регистр TR
селектор TSS новой задачи;
4.
В поле связи TSS новой
задачи сохраняется селектор TSS исходной задачи, что обеспечивает возможность будущего
обратного переключения. Считывает контекст из TSS новой задачи (в CS:IP
появляется точка входа в новую задачу).
Флаг NT ¬ 1. Переключение произошло.
5.
Когда в новой исполняемой задаче встретится команда IRET, она
будет выполняться как обратное переключение задач (NT=1);
6.
Контекст текущей задачи сохранится в ее TSS ;
7.
В регистр TR загрузится
селектор TSS исходной задачи (из поля связи TSS текущей задачи);
8.
Регистры восстановится контекст исходной задачи.
Переключение задач программным способом, в большинстве случаев
практического применения не находит, в том числе из-за жестко и заранее определенной
последовательности выполнения задач. Более гибким является метод переключения
задач по прерываниям.
Переключение задач может происходить как по аппаратным, так и
программным прерываниям и исключениям. Для этого соответствующий элемент в IDT должен являться дескриптором шлюза задачи. Шлюз
задачи содержит селектор, указывающий на
дескриптор TSS (В отличие от дескриптора TSS, который указывает на сегмент,
содержащий полное состояние процессора) (рис 5.2).
Как и при обращении к любому другому дескриптору, при обращении к шлюзу
проверяется условие CPL £ DPL.
Стоит также отметить, что шлюз
задачи может находиться в любой дескрипторной таблице, а, следовательно,
обращение к нему может быть чисто-программное (командами CALL и JMP).