Поддержка концепции задачи в процессорах Intel 80x86

2000, 2007, 31.11.2017

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

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

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

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

Разобравшись с терминологией посмотрим, какое отношение имеет режим многозадачности к процессору. Очевидно, что формирует адресное пространство и обеспечивает его изолированность операционная система. Выделяет требуемые задаче ресурсы тож е система. При чем тут процессор?

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

В состояние задачи входят, например, адресное пространство, состояние ввода/вывода, состояние процессора. Вот именно состояние процессора и имеет самое непосредственное отношение к процессору. Поскольку состояние процессора определяется большим числом параметров, полное его сохранение и восстановление чисто программными средствами может занимать много времени. Кроме того, не все можно сохранить программно.

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

Аппаратная поддержка задач

Поддержка задач реализована в виде сегментов состояния задачи, регистра текущей задачи и набора команд выполняющих переключение задач. Сегмент состояния задачи (TSS) хранит информацию о состоянии процессора, именно формат TSS изменился в процессоре 80386. Регистр текущей задачи (TR) является просто указателем на размещенный в GDT дескриптор сегмента TSS текущей задачи, так же как регистр LDTR. Однако видимая программно часть регистра TR является лишь малой частью реально присутствующей в этом регистре информации. Тот самый дескриптор сегмента TSS, на который указывает регистр TR, при переключении задачи загружается в теневую, не доступную программно, часть регистра TR. Делается это для ускорения доступа. Переключение задач выполняется командами JMP, CALL и INT, а так же по аппаратному прерыванию. Такой неожиданный подход позволяет организовывать обработчики прерываний и сервисы операционной системы не только в виде подпрограмм, выполняющихся в адресном пространстве задач пользователя, но и в виде отдельных задач, выполняющихся в собственном адресном пространстве, что существенно повышает надежность и защищенность системы в целом и ядра ОС в частности.

Сегмент состояния задачи 80286

Как видно, процессор 80286 аппаратно поддерживает достаточно ограниченный TSS. Но даже это позволяет существенно сократить время переключения задач. Давайте рассмотрим сегмент состояния подробнее. Поле Back Link предназначено для организации связанного списка задач для конкретного пользователя. Это поле используется совместно с флажком NT - вложенная задача. Далее идут указатели стеков для трех колец защиты, с 0 по 2. Затем все регистры, которыми собственно и определяется состояние процессора на момент переключения задач. Завершает сегмент TSS регистр LDTR.

Формат сегмента состояния задачи 80386+

Сегмент TSS процессоров, начиная с 80386, является 32-х разрядной версией сегмента TSS процессора 80286. Кроме увеличения разрядности в TSS добавлены регистры FS и GS, отсутствующие в 80286. А вот битовая карта разрешения ввода-вывода это новинка, которой не было в 16-ти разрядных процессорах. Карта представляет собой массив бит, как видно из ее названия. Каждый бит в этой карте соответствует одному порту ввода-вывода. Нулевое состояние бита разрешает доступ к соответствующему порту, а единичное запрещает. При этом следует помнить, что запись слова, фактически требует доступа к двум последовательным портам, а двойного слова к четырем портам. Например, самый первый байт в карте с значением 01011110, разрешает доступ к портам 1, 2, 3, 4 и 6 для записи/чтения байт, и к порту 2 для записи/чтения слова. Общая длина карты ввода-вывода может достигать 8 К байт. Если задаче не требуется такого количества портов, то карту можно ограничить с помощью поля LIMIT в дескрипторе TSS. В любом случае последним байтом карты должен быть байт FF. Еще одним отсутствующим в 80286 элементом TSS является бит Т. Если он установлен, то при переключении на такую задачу возникает прерывание отладки.

Тут нужно сделать небольшое лирическое отступление. В современных компьютерах адресное пространство ввода-вывода занимает гораздо больше 64 К портов (8 К байт, каждый байт 8 портов), которое можно определить битовой картой ввода-вывода в сегменте TSS. Но не будем забывать, что процессор, а мы рассматриваем именно (и только) процессор, является далеко не единственным компонентом вычислительной системы, которую все привыкли называть компьютером. Периферийные устройства (диски, клавиатура, сетевая карта и прочее), могут размещать свои управляющие регистры не только в области портов ввода-вывода, но и в области памяти. Другими словами, обмен данными с внешним устройством ввода-вывода может производиться не только через команды IN и OUT, а и через чтение/запись областей "оперативной" памяти. И доступ к периферийному устройству при таком способе подключения определяется записями в LDT или GDT. Битовая карта ввода-вывода в TSS влияет только на выполнение команд IN и OUT.

В сегменте TSS 80386 есть одна интересная особенность. Если присмотреться внимательно, то станет видно, что между основной частью сегмента TSS и картой ввода-вывода есть свободное место, причем структура этого промежутка не описана. Более того, этого промежутка может и не быть. Это так называемая системо- (точнее процессоро-) зависимая область. Чаще всего эта область не используется, однако процессоры с поддержкой VME ее используют для переадресации аппаратных прерываний. В любом случае про наличие и использование этой области читайте в описании конкретного процессора.

Аппаратно поддерживаемая часть сегмента TSS позволяет сократить время переключения задач и обеспечивает хранение состояния процессора. Но состояние задачи определяется не только состоянием процессора. Для сохранения и восстановления системного контекста задачи можно использовать различные методы. Можно создавать отдельные блоки памяти. А можно использовать для этой цели и не используемую аппаратно часть TSS. Вот с этим методом и познакомимся.

Программно-аппаратная поддержка задач

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

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

Переключение задач

Как я уже говорил, для переключения задач нет специальных команд. Переключение выполняется командами: FAR CALL или FAR JMP, при чем селектор в этих командах может адресовать как шлюз задачи, так и дескриптор TSS; командой IRET для возврата в предыдущую задачу (переключение выполняется только если в регистре EFLAGS установлен бит NT). Кроме того переключение задач может вызвать аппаратное прерывание, если соответствующий дескриптор в IDT содержит шлюз задачи. Понятие шлюза относится к реализации защиты в процессорах 80х86, сейчас достаточно представлять шлюз как бирку, указывающую на действительный дескриптор. При этом права доступа к шлюзу и собственно дескриптору могут различаться.

Переключение задач выполняется за 10 шагов:

  1. Если переключение задачи не вызвано аппаратным прерыванием, особым случаем или командой IRET, производится контроль привилегий дескриптора. Значение в поле DPL дескриптора TSS или шлюза задачи должно быть численно не больше CPL текущей задачи и RPL селектора.

  2. Проверяются бит присутствия и предел дескриптора TSS выходящей задачи, что бы TSS присутствовал в основной памяти и имел размер, достаточный для хранения минимум 104 байт (для 80386+) информации. При успешной проверке производится сохранение контекста выходящей задачи, а при неуспешной формируется особый случай.

  3. Проверяются бит присутствия и предел дескриптора TSS входящей задачи. Если TSS не присутствует, или слишком мал, генерируется особый случай, иначе восстанавливается контекст входящей задачи. Если изменилось значение в регистре CR3, опустошается буфер TLB страничного преобразования. В этой точке загружены все регистры общего назначения и сегментные регистры, но теневые регистры еще не кэшированы. Однако состояние выходящей задачи сохранено и все возникающие далее особые случаи относятся к входящей задаче, хотя дескриптор для регистра CS еще не передан в процессор.

  4. Устанавливается связь с входящей задачей, поэтому действия процессора зависят от причины переключения задач. Если переключение произошло по команде FAR JMP, то дескриптор TSS выходящей задачи не помечается как занятый, а дескриптор входящей задачи помечается. Если переключение произошло по прерыванию или команде CALL, выходящая задача остается занятой, а дескриптор TSS входящей задачи помечается как занятый, кроме того бит NT в EFLAGS устанавливается и в поле BackLink входящей задачи помещается селектор TSS выходящей задачи. Если переключение произошло по команде IRET, выходящая задача помечается как не занятая.

  5. Бит TS в регистре CR0 устанавливается и текущий уровень привилегий для входящей задачи берется из поля RPL селектора CS в сегменте TSS.

  6. Загружается теневой регистр для LDTR, если регистр LDT содержит правильный селектор. Если в регистре LDTR содержится 0, то загрузка теневого регистра не производится. При обнаружении неверного селектора или неприсутствия новой локальной дескрипторной таблицы генерируется особый случай.

  7. В теневые регистры для CS, SS, DS, ES, FS, GS загружаются необходимые дескрипторы. Все дескрипторы проверяются по правилу привилегий, так как поле CPL уже определено, и должны быть присутствующими. При нарушении этих условий генерируется особый случай.

  8. >Биты локального разрешения в регистре отладки DR7 сбрасываются.

  9. Если в TSS входящей задачи бит ловушки T установлен, то генерируется особый случай отладки.

  10. Начинается выполнение новой задачи с адреса задаваемого CS:EIP.

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

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