## 9. Многопрограммный режим работы компьютера

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

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

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

Чтобы воспрепятствовать возможным конфликтам при использовании памяти необходимо иметь механизмы защиты участков памяти от неправильного использования.

Отметим следующие варианты защиты при различных операциях с памятью:

1. задается отношение к областям памяти чужой программы, определяющее, относится защита только к операции записи или к любому обращению к памяти;

2. задается одно из следующих отношений к области памяти собственной программы:

· разрешается полный доступ к данному блоку памяти;

· разрешается только считывание;

· разрешается обращение только через счетчик команд;

· разрешается обращение за исключением счетчика команд.

Если нарушается защита памяти, исполнение программы приостанавливается и вырабатывается запрос прерывания по нарушению защиты памяти.

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

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

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

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

### 9.1. Виртуальная память

В первых компьютерах память была маленькой по объему. Например, компилятор ALGOL был написан для компьютеров с объемом памяти в 1024 слова. Система с разделением времени на PDP-1 работала на машине с памятью в 4096 18-битных слов для операционной системы и пользовательских программ.

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

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

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

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

Задачи, решаемые виртуальной памятью:

1. Предоставление абстракции большой памяти.

2. Автоматизация перемещения частей программы между диском и основной памятью.

3. Возможность изменения размещения программы (elocation).

4. Защита кода и данных различных процессов.

5. Защита по привилегиям.

6. Возможность отображения разделяемых процессами объектов на различные виртуальные адреса (Virtual Address Aliasing).

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

Преимущества использования виртуальной памяти:

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

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

Классы систем с ВП:

1. основанные на сегментах;

2. основанные на страницах;

3. гибридные (сегмент включает целое число страниц).

####

#### 9.1.1. Организация виртуальной памяти

Принцип виртуальной памяти предполагает, что пользователь при подготовке своей программы имеет дело не с физической ОП, входящей в состав ВС и обладающей некоторой фиксированной емкостью, а с виртуальной одноуровневой памятью, объем которой равен всему адресному пространству (в х86 это 4 Гбайт).

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

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

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

Рис. 9.1

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

Если страничная таблица указывает на размещение страницы в ВЗУ, то обращение к ОП не может состояться немедленно, ОС должна организовать передачу из внешней памяти в ОП нужной страницы.

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

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

Элементы, общие для большинства реализаций:

1. Номер страничного кадра

2. Бит защиты, Protection bit (Read/Write/Execute) – тип доступа, разрешенного к странице: право чтения, записи, выполнения.

3. Бит модификации, Dirty bit, Modified bit – когда осуществляется запись на страницу выставляется этот бит. Если бит не выставлен, то страничный кадр можно не перезаписывать на жесткий диск.

4. Бит доступа, Referenced bit – страница была прочитана или изменена. Используется в алгоритмах вытеснения страниц из ОП на жесткий диск.

5. Caching disabled – запрет кэширования при работе с устройствами ввода/вывода.

Другие элементы, которые иногда встречаются:

6. Valid Bit – запись содержит корректное отображение.

7. ASID (Address Space Identifier) идентификатор для процесса, позволяет организовать защиту (MIPS R10000).

8. Global Bit – отменяет действие ASID, позволяет организовать разделяемую память.

#### 9.1.2. Вызов страниц по требованию и рабочее множество

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

Такой метод работы с виртуальной памятью называется вызовом страниц по требованию. Особенность: страницы вызываются по мере необходимости, а не заранее. Вопрос о целесообразности использования метода вызова страниц по требованию имеет смысл только в начале запуска программы, т.к. в процессе работы нужные страницы будут собраны в основной памяти. Если компьютер работает в режиме разделения времени и процессы откачиваются обратно, то каждая программа будет запускаться многократно. Для каждой программы распределение памяти уникально и при переключении программ оно изменяется. Поэтому в системах с разделением времени такой подход неприемлем.

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

#### 9.1.3. Политика замещения страниц

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

Алгоритм LRU (Least Recently Used – алгоритм удаления наиболее давно используемых элементов). Согласно этому алгоритму удаляется та страница, которая использовалась наиболее давно. В целом алгоритм работает достаточно хорошо, но возможны и исключения. Например, выполняется цикл, для реализации которого требуется n страниц, а в памяти может разместиться только n-1. После выполнения n-1 страницы происходит обращение за недостающей n-й, а выбрасывается первая, которая потребуется сразу же после выполнения n-й. Затем считывается первая, а выбрасывается вторая, которая тут же оказалась нужна и т.д. Очевидно, что в такой ситуации алгоритм LRU не работает; однако, в такой ситуации другие алгоритмы тоже работать не будут. Выход – расширение рабочего множества или учет малого объема основной памяти при составлении программ.

Алгоритм FIFO (First-in First-out – первым пришел, первым ушел) удаляет ту страницу, которая первая поступила в память. В такой ситуации возможно ошибочное удаление наиболее «активной» страницы.

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

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

Алгоритм замещения давно использовавшейся страницы (Not Recently Used) заключается в следующем. Проверяются биты R (Referenced) и M (Modified). При этом R устанавливается при чтении или записи страницы, M – только при записи. При первоначальной загрузке страницы в память оба бита сброшены. В алгоритме периодически (по таймеру) сбрасывается бит R. В результате при возникновении страничного прерывания выделяются 4 класса страниц: 0 – не использованная, не модифицированная; 1 – не использованная, модифицированная (например, R := 0 по таймеру); 2 – использованная, не модифицированная; 3 – использованная, модифицированная. Алгоритм удаляет случайную страницу из непустого класса с самым меньшим номером.

Алгоритм «Второй шанс» (Second Chance) представляет собой модификацию алгоритма FIFO. При удалении страницы проверяется бит R. Если он сброшен, страница удаляется из «головы» очереди, если он установлен, то бит сбрасывается, страница помещается в «хвост» очереди и проверяется следующая страница в «голове» очереди. Недостаток: много перемещений страниц.

Часовой алгоритм является модификацией алгоритма «Второй шанс». В этом случае страницы образуют циклический список. Имеется указатель на текущую страницу. При возникновении страничного прерывания проверяется бит R текущей страницы. Если он сброшен, то страница удаляется на ее место заносится новая, а значение указателя увеличивается на 1. Если бит установлен в 1, то значение указателя увеличивается на 1 и аналогичным образом проверяется следующая страница.

#### 9.1.4. Размер страницы и фрагментация

Если данные или программа пользователя занимает не целое число страниц, то на последней странице возникает свободное пространство, которое не может быть занято другим пользователем. Такой эффект называется внутренней фрагментацией.

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

#### 9.1.5. Сегментация

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

- таблица символов, которая содержит имена и атрибуты переменных;

- исходный текст;

- таблица, содержащая все используемые целочисленные константы и константы с плавающей точкой;

- дерево, содержащее результат синтаксического анализа программы;

- стек, используемый для вызова процедур в компиляторе.

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

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

Сегментированная модель имеет ряд преимуществ при работе с памятью.

1. Если каждая процедура занимает отдельный сегмент, у которого первый адрес равен 0, то связывание процедур, которые компилируются отдельно сильно упрощается. Для обращения к i-му слову n-й процедуры используем адрес (n, i).

2. Если процедура в некотором сегменте изменялась и перекомпилировалась, то остальные можно не трогать.

3. Сегментация облегчает разделение общих процедур и данных между несколькими программами.

4. Разные сегменты могут иметь разные виды защиты. Например, кодовый сегмент допускает только считывание выполнение, для массивов данных – запись и считывание и т.д.

В таблице 1 приведено сравнение страничной организации памяти и сегментации.

Таблица 1

Свойство | Страничная организация памяти | Сегментация
Необходимость учета программистом | Нет | Да
Количество линейных адресных пространств | 1 | Много
Возможность увеличения памяти для виртуального адресного пространства | Да | Да
Простота управления структурами переменного размера | Нет | Да
Назначение технологии | Имитация памяти большого размера | Предоставление нескольких адресных пространств

#### 9.1.6. Реализация сегментации

Сегментацию можно организовать одним из двух способов. Это подкачка и разбиение на страницы.

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

Отличие сегментации от разбиения на страницы: размер страниц фиксирован, а размер сегментов – нет. Поэтому при сегментации может возникнуть эффект внешней фрагментации, которая иллюстрируется рисунком 9.2. Для ввода нужной программы может понадобиться сдвиг содержимого памяти. На рис. 9.2 показано распределение памяти между программами ABCD, из которых две (A и D) являются в данный момент неактивными и могут быть удалены во внешнюю память. Если вновь вводимая программа E больше, чем имеющийся после удаления A и D участок памяти, то для ее размещения необходимо сдвигать программы B и C. Это перемещение связано с потерей времени. Подобная схема использовалась в ОС Windows 3.x в так называемом стандартном режиме работы. Если на уплотнение памяти требуется слишком много времени, нужен специальный алгоритм для определения, какую именно «дырку» лучше использовать для определенного сегмента. Для этого требуется список адресов и размеров всех «дырок». Популярный алгоритм оптимальной подгонки выбирает самую маленькую «дырку», в которую помещается сегмент.

Второй способ реализации сегментации – разделение каждого сегмента на страницы фиксированного размера и вызов страниц по требованию. Для того, чтобы разбить сегмент на страницы необходимо иметь отдельную таблицу страниц для каждого сегмента. MULTICS (Multiplexed Information and Computing Service – служба общей информации и вычислений) – это старая операционная система, которая совмещала сегментацию с разбиением на страницы. Она была разработана в МТИ совместно с компаниями Bell Labs и General Electric в 1971. Адреса в MULTICS состоят из двух частей: номера сегмента и адреса внутри сегмента. Для каждого процесса существовал сегмент дескриптора, который содержал дескрипторы для каждого сегмента. Адрес сегмента дескриптора хранился в специальном регистре.

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

### 9.2. Управление памятью в процессоре Pentium 4

#### 9.2.1. Дескрипторы сегментов

Pentium 4 имеет сложную систему виртуальной памяти, которая поддерживает вызов страниц по требованию, чистую сегментацию, сегментацию с разбиением на страницы. Виртуальная память состоит из двух таблиц: LDT, GDT. Каждая программа имеет свою дескрипторную таблицу, а единственная глобальная дескрипторная таблица разделяется всеми программами компьютера. Локальная таблица дескрипторов описывает локальные сегменты каждой программы (код, данные, стек и т.д.), глобальная дескрипторная таблица – системные сегменты, в том числе сегменты операционной системы.

Каждый сегмент в системе характеризуется специальной 64-разрядной (8 байт) структурой данных, называемой дескриптором сегмента. В описание сегмента включается базовый адрес сегмента, размер сегмента, тип, уровень привилегий и дополнительная информация. Формат дескриптора приведен на рис. 9.4.

|  |  |  |  |  | AR права доступа |
63 |  |  |  |  |  | 47 |  |  |  |  |  |  | 39 | 32
Базовый адрес 31-24 | G | D | X | U | Предел 19-16 | P | DPL | S | Тип | A | Базовый адрес 23-16
31 | 23 | 15 | 7

###### Базовый адрес 15-0

###### Предел 15-0

Рис.9.4

Рассмотрим назначение отдельных полей дескриптора.

Базовый адрес занимает 32 разряда (2,3,4,7 байты). Определяет начальный адрес сегмента в линейном адресном пространстве в 4 Гбайт. Именно этот адрес сформирует процессор при задании нулевого смещения.

Предел. Размер 20 бит. Задает размер сегмента. Этот размер определяется так. Если значение бита G = 0 (бит гранулярности), то размер сегмента в байтах равен значению этого поля минус 1. Если G = 1, то поле предела задает предел не в байтах, а в страницах (4 Кбайт). При G = 0 значение размера максимум 1 Мбайт, при G = 1 максимум размера сегмента 4 Гбайта. Смещение в команде не должно превышать размер сегмента.

Права доступа (Access Rights). Это байт 5 дескриптора сегмента и часто называется байтом AR.

Бит доступа A. Этот бит процессор автоматически устанавливает в 1, когда осуществляется обращение к тому сегменту памяти, который определяет дескриптор. Используется для выявления сегментов, к которым не было обращения.

Дополнительные биты. Бит D размера по умолчанию обеспечивает совместимость с 16-разрядным процессором 80286. Когда бит D=0, считается, что находящиеся в сегменте операнды или команды относятся к 16-разрядным, если D=1, то сегмент считается 32-разрядным и в нем используется 32-разрядные команды и 32-разрядная адресация.

Бит 53 – резервный.

Бит пользователя U предназначен для использования системными программистами по их усмотрению, а процессор игнорирует этот бит.

#### 9.2.2. Поле права доступа (AR)

Рассмотрим более подробно это поле. Отметим, что не все комбинации допустимы. Если возникает запрещенная комбинация, то процессор фиксирует особый случай

Бит P присутствия служит флажком, показывающим возможность доступа к сегменту. Если бит присутствия P установлен в 1, то сегмент доступен. Когда P = 0, то процессор отвергает все последующие попытки использовать дескриптор и все определяемое этим дескриптором адресное пространство как бы пропадает. Если P = 0, то процессор игнорирует остальные поля дескриптора. В этой ситуации системный программист может распоряжаться остальными байтами дескриптора по своему усмотрению. Например, в них можно закодировать нахождение выгруженного из памяти сегмента на диске.

Поле типа играет важную роль в определении сегментов памяти. Каждый бит этого поля несет свою смысловую нагрузку рис. 9.5.

Бит 3 AR различает сегменты кода (1) и данных (0). Для сегментов кода бит 2 называется битом подчинения С (об этом будем говорить при рассмотрении механизма защиты), а бит 1 называется битом считывания R. Он показывает возможность считывания кода как данных с помощью префикса замены сегмента.

7 | S | тип |  | Сегмент кода |

###### P

DPL

1

1

C

R

A

###### P

DPL

1

0

ED

W

A

Сегмент данных

###### P

DPL

0

x

X

x

x

Системный объект

Рис. 9.5

Для сегментов данных бит 2 называется битом расширения вниз ED, который различает сегменты стека ED=1 и сегменты собственно данных ED=0. Бит 1 записи W показывает возможность модификации содержимого сегмента посредством записи в него W=1.

Отметим, что сегменты типов 0 могут хранить только данные, и командами FAR JMP и FAR CALL невозможно заставить процессор выполнить данные как код.

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

Максимальный адрес стека: если D=0, то Аmax = Абаз + 0FFFF (64 Kб-1); если D=1, то Аmax = Абаз + 0FFFFFFFF (4 Гб-1).

На рис. 5.9 показаны сегмент стека и сегмент данных, имеющих предел 1FFFh (G=0, для упрощения считаем сегменты 16-битными).

Для сегмента стека первый адресуемый байт находится по смещению 2000H.

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

Типы сегментов 4–7 определяют исполняемые сегменты, т.е. сегменты, в которых находится код. В таких сегментах можно разрешить/запретить считывание. Отметим, что запись в сегмент кода никогда не допускается, поэтому организовывать трюки с самомодифицированными программами невозможно.

Трехбитное поле типа определяет использование сегмента:

Таблица 2

000 - | данные, только чтение;
001 - | данные, чтение/запись;
010 - | стек, только чтение (на практике не применяется);
011 - | стек, чтение/запись;
100 - | код, только выполнение;
101 - | код, выполнение/чтение
110 - | подчиненный сегменту код, только выполнение;
111 - | подчиненный сегменту код, выполнение/чтение

Например, в х86 нельзя загружать сегменты с типом 0-3 сегменты данных и заставить их выполняться как код. Нарушение этих правил вызывает особый случай защиты (а в 8086 допустимо).

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

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

Бит S (системный) установлен в 1 для сегментов памяти и равен 0 для специальных системных объектов.

#### 9.2.3. Дескрипторные таблицы

Область памяти, предназначенная для хранения дескрипторов, называется дескрипторной таблицей. Она представляет собой массив из 8–байтных элементов – дескрипторов. Порядок размещения дескрипторов в таблице не играет роли, а максимальное число дескрипторов составляет 8192, соответственно максимальный размер таблицы 64 Кбайт. В процессоре предусмотрены дескрипторные таблицы трех типов.

Глобальная дескрипторная таблица (GDT). Является главной общесистемной таблицей дескрипторов. Все программы (задачи), выполняющиеся в системе, могут использовать эту таблицу. Местонахождение этой таблицы определяет специальный регистр GDTR. В нем находится 32–разрядное поле линейного базового адреса и 16–разрядное поле предела L=8*N-1, где N – число дескрипторов.

Дескрипторная таблица прерываний (IDT). Является общесистемной и содержит дескрипторы специальных системных объектов, называемых «шлюзами» (gate), которые определяют точки входов в процедуры обработки прерываний и особых случаев. Системный регистр IDTR служит для локализации этой таблицы (аналогично по структуре с GDTR).

Локальная дескрипторная таблица (LDT). Для каждой задачи в дополнение к GDT можно построить LDT. Она определяет сегменты, доступные только этой конкретной задаче. Для локализации LDT служит 16-разрядный регистр LDTR, который содержит только селектор сегмента, содержащего LDT.

Загрузка регистров GDT и IDT осуществляется только один раз в ходе подготовки процессора к работе в защищенном режиме. Предварительно готовятся сами таблицы, после чего командами LGDT mem48 и LIDT mem48 из памяти загружаются 48-битная структура. (Команды SGDT mem48 и SIDR mem48 сохраняют содержимое регистра).

Таблицы LDT не являются обязательными и создаются по мере необходимости. Для хранения LDT применяются сегменты памяти, а дескрипторы этих сегментов хранятся в таблице GDT. Селектор из регистра LDTR выбирает в таблице GDT нужный дескриптор. Для ускорения работы этот дескриптор загружается в отдельный «теневой» регистр, ассоциированный с LDT автоматически всякий раз, когда содержимое LDTR меняется.

Команда LLDT reg16/mem16 загружает селектор в регистр LDTR, аналогично, команда SLDT reg16/mem16 сохраняет содержимое LDTR.

####

#### 9.2.4. Селекторы сегментов

Напомним, что отправной точкой доступа к дескриптору служит содержимое сегментного регистра, называемого селектором. Если в процессоре 8086 содержимое сегментного регистра служит просто старшими битами начального адреса сегмента, то в рассматриваемом процессоре селектор определяет сегмент памяти косвенно, через дескрипторную таблицу. Формат селектора приведен на рис. 9.7.

15 | 2 | 1 | 0

###### Индекс

TI

###### RPL

Рис. 9.7

Двухразрядное поле запрашиваемого уровня привилегий RPL не участвует в выборе дескриптора, а привлекается для контроля привилегий в механизме защиты.

Бит индикатора таблицы TI показывает, из какой дескрипторной таблицы выбирается дескриптор. Если TI равен 0, то это таблица GDT; при TI равном 1 – таблица LDT.

Старшие 13 бит селектора определяют нужный дескриптор в соответствующей таблице.

Отметим, что первый элемент таблицы GDT зарезервирован, тем не менее селектор может содержать значения индекса равное 0 и TI также равное 0. Такой селектор называется пустым селектором. Пустой селектор можно загружать в сегментные регистры ES, DS, GS, FS, но обращаться к памяти с ними нельзя.

#### 9.2.5. Формирование адреса.

Логический адрес – это адрес, которым обычно оперирует программное обеспечение. Он формируется из двух величин: 16-битного селектора (указателя) сегмента (берется из соответствующего сегментного регистра) и 32 (16) – разрядного смещения относительно начала сегмента. Логический адрес существует только внутри программного обеспечения. Его преобразование в физический адрес для преобразования в физический адрес осуществляется при помощи достаточно сложного механизма, функционирование которого зависит от текущего режима работы процессора.

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

Физический адрес – передается на внешнюю шину для обращения к ячейкам памяти. Адресуется 232 байт в обычном режиме и до 236 байт в случае поддержки механизма расширения физического адреса (CR4.PAE).

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

Real Address Mode – режим реальной адресации (или просто реальный режим) полностью совместим с 8086. В этом режиме возможна адресация до 1 Мб физической памяти. Механизм формирования физического адреса в этом режиме предельно упрощен и иллюстрируется рис. 9.8.

Protected Virtual Address Mode – защищенный режим виртуальной адресации (или просто защищенный режим). В этом режиме процессор позволяет адресовать до 4 Гб физической памяти. Действия механизма образования физического адреса основано на использовании дескрипторных таблиц. Схема формирования адреса приведена на рис. 9.9.

15 |  |  | 0 |  | 15 |  |  |  | 0

###### Селектор сегмента

###### Смещение

База сегмента | 19 | 3 | 2 | 1 | 0
| 0 | 0 | 0 | 0
+
Смещение | 19 | 18 | 17 | 16 |
=
Линейный адрес | 19 | 0
20-битный адрес

Рис. 9.8

Существенным дополнением является Virtual 8086 Mode – режим виртуального процессора 8086. Этот режим является особым состоянием задачи защищенного режима, в котором процессор функционирует как 8086. На одном процессоре в таком режиме могут параллельно выполняться несколько задач с изолированными друг от друга ресурсами. При этом использование физического адресного пространства памяти управляется механизмами сегментации и трансляции страниц. Попытки выполнения недопустимых команд, выхода за пределы отведенного пространства памяти контролируются системой защиты.

Процессоры, начиная с Pentium и некоторых моделей 486, имеют особый режим системного управления System Management Mode (SMM), в котором процессор выходит в иное, изолированное от остальных режимов пространство памяти. Этот режим используется в служебных и отладочных целях.

Адрес таблицы глобальных дескрипторов находится в системном регистре с названием GDTR (Global Descriptor Table Register). Обращение по адресу, указанному в этом регистре, сразу приведет к начальному элементу дескрипторной таблицы. Регистры системных адресов длиной 2 байта содержат селектор дескриптора сегмента памяти, в котором находится указываемый объект. С помощью селектора вначале нужно выбрать из таблицы GDT дескриптор соответствующего сегмента памяти, а потом из дескриптора выбрать адрес сегмента, в котором находится нужная таблица. Селектор дескриптора сегмента памяти, который содержит локальную дескрипторную таблицу находится в системном регистре LDTR (Local Descriptor Table Register).

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

Шестнадцатибитовый селектор содержит 13-битовый индекс дескриптора и находящийся во втором разряде бит признака дескрипторной таблицы TI (Table Indicator). Значение бита TI=0 означает, что дескриптор нужно выбирать из глобальной таблицы, а TI=1 означает, что нужный дескриптор находится в локальной таблице.

Для определения адреса поля памяти в защищенном режиме нужно знать адрес сегмента и внутрисегментное смещение. Способ определения внутрисегментного смещения – эффективного адреса не отличается от используемого в реальном режиме. Для сегментов кода он выбирается из регистра eip, а для сегментов данных и стека формируется из содержимого индексных регистров esi и edi, базовых регистров ebx, ebp и смещения, заданного в команде.

Более сложным способом определяется адрес сегмента. ОС формирует в регистре GDTR адрес глобальной дескрипторной таблицы. Эта таблица должна быть доступна любым выполняющимся программам, поэтому адрес в GDTR не изменяется при переключениях процессора на выполнение другой задачи. А вот в регистр LDTR ОС при каждом переключении процессора на новую задачу загружает индивидуальный для каждой задачи селектор дескриптора из GDT, в котором находится адрес ее локальной таблицы LDT. Этот селектор доступен только программе-хозяйке. Таким образом, ни одна из программ пользователей не может получить доступ к локальной таблице другой программы.

Из заданного в команде сегментного регистра извлекается 16-битовый селектор. Из него выбирается бит признака дескрипторной таблицы TI и 13-битовый индекс. Бит признака TI используется для определения таблицы, в которой находится дескриптор, а индекс служит для выборки из соответствующей дескрипторной таблицы (GDT при TI = 0 и LDT при TI = 1) конкретного дескриптора сегмента памяти. Для определения адреса глобального сегмента нужно сделать следующее.

1. Прочитать из регистра GDTR адрес глобальной дескрипторной таблицы GDT.

2. Выбрать селектор из заданного в команде сегментного регистра.

3. Выбрать из селектора индекс дескриптора.

4. Определить адрес дескриптора сегмента, умножив индекс на 8 (сдвинув влево на 3 бита) и сложив результат с адресом GDT.

5. Выбрать из найденного таким образом дескриптора искомый адрес глобального сегмента памяти.

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

1. Прочитать из регистра GDTR адрес глобальной дескрипторной таблицы GDT.

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

3. Выбрать из селектора индекс дескриптора в глобальной таблице.

4. Определить адрес дескриптора, умножив индекс на 8 и сложив результат с адресом GDT.

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

6. Выбрать селектор из заданного в команде сегментного регистра.

7. Выбрать селектор из заданного в команде сегментного регистра.

8. Определить адрес дескриптора, умножив его индекс на 8 и сложив результат с адресом таблицы LDT.

9. Выбрать из найденного дескриптора адрес искомого сегмента памяти.

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

Описанные выше операции обращения к сегменту данных требуют считывания из памяти 8-байтного дескриптора из GDT (а при обращении к LDT обращений к памяти будет еще больше). Это может существенно снизить производительность процессора. Во избежание этого в процессоре используется кэширование дескрипторов. Оно опирается на тот факт, что обращения к памяти производятся гораздо чаще, чем изменение содержимого сегментных регистров. Для этого с каждым сегментным регистром связывается «теневой» регистр (аналогично LDTR), в котором хранится дескриптор, соответствующий содержимому сегментного регистра. Когда программа загружает селектор в сегментный регистр, процессор автоматически считывает нужный дескриптор в «теневой» регистр.

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

1. проверяется поле Index селектора на предмет нахождения в пределах таблицы, определяемой TI;

2. при загрузке DS, ES, FS, GS дескриптор должен разрешать считывание из сегмента;

3. для SS в сегменте должны быть разрешены и чтение, и запись;

4. для CS сегмент должен быть исполняемым;

5. бит присутствия P должен быть равен 1.

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

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

Отметим в заключение, что локальная дескрипторная таблица является системным объектом, ее байт AR имеет следующий вид:

|  |  | S |  |  |  |

P

###### DPL

0

0

0

1

0

Рис. 9.10

Естественно, что в регистр LDTR можно загружать только селекторы таких дескрипторов.

#### 9.2.6 Виртуальная память со страничной организацией в процессорах х86

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

Как линейное, так и физическое адресное пространство х86 делится на 1 Мб страниц по 4 Кб. Границы сегментов и границы страниц не зависят друг от друга и не обязаны быть выровнены.

В процессе страничного преобразования старшие 20 бит 32-разрядного линейного адреса через страничную таблицу замещаются номером физической страницы. Младшие 12 бит остаются неизменными (рис. 9.11).

31 |  | 12 | 11 | 0 |

###### Виртуальная страница

###### Смещение

Линейный адрес

| Таблица страниц |  |  |
31 |  | 12 | 11 | 0 |

###### Номер физ. Страницы

Физический адрес

Рис. 9.11

Таблица страниц в 1 М элементов будет слишком большой, особенно с учетом того, что для каждой задачи нужна своя таблица. Поэтому в х86 на самом деле реализовано более гибкое двухбитное преобразование, показанное на рис. 9.12.

Основой страничного преобразования выступает регистр управления CR3, который содержит 20-битный физический базовый адрес каталога страниц текущей задачи (регистр PDBR). Отметим, что это единственный внутренний регистр процессора, который содержит физический адрес памяти. Младшие 12 бит адреса считаются нулевыми, т.е. каталог выровнен по границе страниц. Предполагается, что каталог страниц постоянно находится в ОП.

Корневая таблица называется таблицей страниц первого уровня или просто каталог страниц, содержит 1024 32-разрядных дескриптора, называемых элементами каталога страниц PDE. Каждый из них адресует подчиненную таблицу страниц (таблицу страниц второго уровня). Каждая подчиненная таблица содержит1024 32-разрядных дескриптора, называемых элементами таблицы страниц (PTE), каждый из которых адресует страницу в адресной памяти.

Преобразование линейного адреса в физический состоит из следующих действий:

1. старшие 10 бит (31–22) линейного адреса служат индексом в каталоге страниц, выбирая один из 1024 элементов PDE, который выбирает таблицу страниц;

2. средние 10 бит (21–12) индексируют таблицу страниц, выбирая из нее элемент PTE, который содержит 20–разрядный базовый физический адрес в памяти;

3. базовый адрес из PTE объединяется с 12 младшими битами линейного адреса, после чего получается физический адрес.

Формат элемента таблицы страниц. Элементы таблиц страниц обоих уровней т.е. элементы PDE и PTE имеют одинаковый формат, представленный на рис. 9.13.

31 | 12 | 11 |  | 9 |  |  |  |  |  |  |  |  | 0
Адрес страничного кадра | дост | 0 | 0 | D | A | PCD | PWT | U/S | R/W | P

Рис. 9.13

Адрес страничного кадра – в этом поле находится физический базовый адрес страницы (младшие 12 бит –0).

Биты системного программиста. Процессор никогда не использует биты 11, 10, 9. Программисты могут использовать их по своему усмотрению.

Бит присутствия P. Показывает, находится ли страница в физической памяти (P=1). Если P=0, то страницы в памяти нет и остальная часть элемента таблицы страниц доступна для ОС, например для информации о местонахождении отсутствующей страницы. При попытке использования такой страницы возникает особый случай страничного нарушения. При этом ОС должна подгрузить страницу в физическую ОП из ВЗУ.

Биты обращения A и «грязный» D содержат информацию об использовании страницы. Бит A сообщает об любом обращении к странице, бит D – об обращении к странице для записи. Эти биты аппаратно устанавливаются процессором. ОС по ним может следить за частотой использования страниц памяти.

Биты R/W счет/запись, U/S – супервизор/пользователь используются механизмом защиты.

Биты PCD – управление кэшированием и PWT сквозной записи употребляются при кэшировании страниц и их рассматривать не будем.

Страничное преобразование включается установкой в 1 старшего бита PG в регистре CR0 с помощью команды MOV CR0, src. Со следующей команды начинается страничное преобразование. При этом требуется соблюдать осторожность, например линейные адреса этой программы должны совпадать с физическими адресами и после включения страничного преобразователя. Также должен быть предусмотрен ряд других мер, например, запрещены прерывания.

### 9.3. Защита по привилегиям в архитектуре х86

Практически во всех ЭВМ для целей защиты предусматриваются как минимум два режима работы – системный режим, называемый также режимом супервизора (Superevisor) и пользовательский режим (User). Основное различие между ними состоит в том, что программам, работающим в режиме супервизора (ОС) доступны все ресурсы системы. В пользовательском режиме программам запрещается выполнение некоторых команд, влияющих на общесистемные ресурсы (т.н. привилегированные команды).

В архитектуре х86 этот принцип реализуется через поддержку 4-х уровней привилегий – PL. Механизм защиты х86 опирается на описание различных системных объектов с помощью дескрипторов. В каждом дескрипторе имеется двухбитное поле уровня привилегий DPL, которое определяет, каким программам разрешен доступ к описываемому им объекту.

#### 9.3.1. Уровни привилегий

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

Термин «привилегия» подразумевает права и возможности, которые обычно не разрешаются. Процессоры х86 поддерживают 4 уровня привилегий: 0, 1, 2, 3. Чем меньше номер уровня, тем он больше привилегирован. Уровни привилегий обычно изображаются в виде т.н. колец защиты (рис. 9.14). При выполнении почти каждой машинной команды осуществляется проверка защиты по привилегиям. Процессор в защищенном режиме постоянно контролирует, что текущая программа достаточно привилегированна, чтобы

1. выполнять некоторые команды;

2. обращаться к данным других программ;

3. передавать управление внешнему (по отношению к самой программе) коду командами передачи управления типа FAR JMP или FAR CALL.

С каждым сегментом кода, данных или стека ассоциируется уровень привилегий и все, что находится внутри этого сегмента имеет этот уровень привилегий. Уровень привилегий выполняющегося в данный момент сегмента кода называется текущим уровнем привилегий (CPL) и задается полем RPL селектора в регистре CS. При передаче управления сегменту кода с другим уровнем привилегий процессор будет работать на новом уровне привилегий. Часто говорят, например, «программа выполняется в кольце 0», подразумевая, что CPL процессора равен 0.

Правила доступа по привилегиям иллюстрируются на рис. 9.15. Видно, что программам не разрешается доступ к данным, которые имеют более высокий уровень привилегий. Передача управления (FAR JMP, FAR CALL) не допускается за пределы своего поля защиты.

Рассмотрим подробнее правила защиты.

#### 9.3.2. Привилегированные команды.

Команды, воздействующие на механизм сегментации и защиты могут выполняться только в нулевом кольце (PL0-программы). К ним, в частности относятся HLT, LGDT, LIDT, LLDT, команды MOV с регистрами управления CRn, отладки DRn, проверки TRn и некоторые другие.

Вторую группу образуют команды, которые изменяют состояние флажка прерываний IF и проводят ввод–вывод:

CLI (IF=0) IN INS

STI (IF+1) OUT OUTS

Для выполнения этих команд программа не обязательно должна иметь уровень привилегий 0. Однако их могут выполнять только те программы, уровень привилегий которых не ниже, определенного полем IOPL (2 бит) в регистре EFLAGS. Т.е. для выполнения этих команд требуется, чтобы CPL ≤ IOPL. Если IOPL = 3, то эти команды доступны всем программам.

Поскольку поле IOPL находится в доступном регистре EFLAGS, может показаться, что эту защиту можно преодолеть, например, рассмотрим код:

pushfd ; флажки в стек

or dwodprt ss:[esp], 3000h ; iopl = 3

popfd ; теперь ввод/вывод доступен.

Однако этот прием не срабатывает. Команда popfd сама по себе не является привилегированной, но их действие зависит от значения CPL. Команды popf и popfd могут изменить поле IOPL только если CPL = 0, т.е. на нулевом кольце. Если это не PL0-программа, процессор просто не модифицирует биты поля IOPL.

Примерно такая же ситуация характерна для флажка прерываний IF. Чтобы команды POPF или POPFD изменили состояние флажка IF, значение CPL должно быть меньше или равно значению IOPL.

#### 9.3.3. Защита доступа к данным.

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

CPL ≤ DPL.

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

Контроль реализуется двумя способами.

1. Контроль осуществляется при загрузке селектора в один из регистров DS, ES, FS, GS.

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

При загрузке селектора в сегментный регистр стека правила защиты ужесточаются, требуется, чтобы CPL = DPL, т.е. не разрешается использовать стек даже с меньшими привилегиями.

Поле RPL. Младшие два бита селектора сегмента содержат поле запрашиваемого уровня привилегий RPL. Это поле не влияет на выбор дескриптора, но учитывается при контроле привилегий.

Защита по значению DPL (CPL ≤ DPL) позволяет изолировать код и данные на различных уровнях привилегий. Однако возможна ситуация, когда ошибочный указатель, т.е. селектор, переданный более привилегированной программе, приводит к недопустимой модификации привилегированных данных.

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

max(CPL,RPL) ≤ DPL.

Например, привилегированная процедура ОС, получив указатель (селектор) от пользовательской программы может принудительно установить в нем RPL = 3, предотвратив тем самым в принципе попытки доступа к привилегированным сегментам.

#### 9.3.4. Защита сегментов кода.

Напомним, что х86 запрещает передачу управления сегменту кода, находящемуся на другом уровне привилегий, тем самым предотвращая произвольное изменение уровня привилегий. Если бы CPL можно было легко изменять, все остальные средства защиты стали бы бессмысленными.

Передачи управления в другой сегмент осуществляют команды FAR JMP, FAR CALL и FAR RET, поскольку при их выполнении меняются регистры CS и EIP.

При загрузке CS процессор предпринимает следующие проверки:

1. проверяется, что новый дескриптор является сегментом кода;

2. проверяется DPL = CPL, и бит присутствия P.

Если все проверки прошли, то дескриптор используется.

#### 9.3.5. Передача управления между уровнями привилегий

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

##### 9.3.5.1. Подчиненные сегменты кода.

Представим себе, что в системе имеется процедура для преобразования целых чисел в строку символов. Эта процедура должна находится в кольце 0 (чтобы ее можно было вызвать из этого кольца). Обычно такая процедура требует достаточных привилегий только для обращения к своему параметру (целому числу) и возвращению результата, т.е. таких же привилегий, что и вызывающая программа. Именно для таких случаев в х86 предусмотрены подчиненные сегменты и код.

Напомним, что в байте AR дескриптора сегмента кода есть бит C – подчинения. Если C = 1, то это подчиненный сегмент кода и защита по CPL и DPL не действует.

С подчиненными сегментами кода не ассоциируется конкретный уровень привилегий, т.к. они подчиняются уровню привилегий того кода, который передает управление с помощью команд CALL или JMP. Если, например, подчиненный сегмент кода вызывает программа из кольца 3, то он работает с CPL = 3, если из кольца 0, то с CPL = 0.

Применительно к подчиненному сегменту кода действует одно ограничение. Оно заключается в том, что значение DPL дескриптора подчиненного сегмента кода всегда должно быть меньше или равно текущему значению CPL. Другими словами, передача управления разрешается только во внутренне более защищенные кольца.

##### 9.3.5.2.Шлюзы вызова.

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

Формат шлюза вызова приведен на рис. 9.16.

63 | 47 |  |  |  |  |  |  |  |  |  |  | 36 | 32

Смещение (31-16)

P

## DPL

0

1

1

0

0

0

0

0

WC

| 15 | 0
Селектор | Смещение назначения (0-15)

Рис. 9.16

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

WC – поле счетчика дескриптора шлюза (Word Count)

Дескрипторы шлюзов вызова не определяют никакого адресного пространства, поэтому в них нет полей базы и предела. Селекторы шлюзов вызова можно загружать только в сегментный регистр CS. Более того, адресовать шлюз вызова можно только с помощью команды межсегментного вызова FAR CALL. Сама команда CALL должна адресовать шлюз вызова, а не сегмент кода подключения (рис. 9.17).

По существу, в команде FAR CALL, которая обращается к шлюзу вызова, имеющееся смещение игнорируется, а селектор определяет только шлюз вызова.

Реализованный в х86 такой косвенный вызов привилегированных процедур имеет несколько преимуществ:

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

2. Шлюзы вызова делают код процедуры невидимым для программ на внешних уровнях привилегий.

3. Так как вызывающая программа прямо адресует только шлюз, реализуемые процедурой функции можно изменять или перемещать в адресном пространстве, не затрагивая интерфейс со шлюзом.

##### 5.3.5.3. Доступность шлюза вызова.

Шлюзы вызова имеют определенное ограничение на использование. При этом учитывается

1. значение DPL самого шлюза вызова;

2. значение DPL дескриптора вызываемого (целевого) сегмента кода;

3. значение RPL селектора в команде FAR CALL;

4. значение CPL текущей (вызывающей) программы.

Правило разрешения вызова через шлюз принимает вид:

DPL целевого сегмента ≤ max(RPL,CPL) ≤ DPL шлюза.

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

Например, если процессор выполняет PL2 – программу (СPL=2), и ей требуется вызвать PL0-процедуру, (целевой DPL=0), необходимо использовать дескриптор шлюза со значением DPL равным 2 или 3.

Суммарные правила для использования шлюза вызова имеют следующий вид:

1. значение DPL шлюза вызова должно быть больше или равно значению текущего уровня привилегий CPL;

2. значение DPL шлюза вызова должно быть больше или равно значению поля RPL селектора шлюза;

3. значение DPL шлюза вызова должно быть больше или равно значению DPL целевого сегмента кода;

4. значение DPL целевого сегмента кода должно быть меньше или равно значению текущего уровня привилегий CPL.

##### 5.2.5.4. Переключение стека.

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

Необходимость переключения стека диктуется двумя основными причинами:

1. вызываемая процедура должна защищаться от возможного переполнения стека, возникающего в том случае, если вызывающая программа распределила недостаточное стековое пространство;

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

Необходимость переключения стека ставит вопрос о том, где взять указатель для регистров SS: ESP нового стека. Для этого придется привлечь еще один специальный сегментный дескриптор, который определяет сегмент состояния задачи TSS, ассоциируемый с каждой задачей (программой). Фрагмент сегмента состояния приведен на рис. 9.18.

В сегменте TSS находятся селекторы сегмента и указатели стека для уровней привилегий 0, 1 и 2. Пара SS: ESP для уровня 3 не нужны, т.к. не существует вызова, в котором вызывающая программа менее привилегированна, чем вызвавшая в кольце 3.

После успешного прохождения командой CALL шлюза вызова без нарушений правил защиты, процессор должен начать работать с новым стеком. Для этого в регистры SS: ESP из сегмента TSS загружаются селектор сегмента и указатель стека, соответствующий новому уровню привилегий. Например, если вызвана PL1 – процедура, в регистры SS: ESP загружаются значения SS1 и ESP1.

После загрузки регистров SS: ESP для адресации нового стека процессор сразу же включает в новый стек старые селектор сегмента и указатель стека, которые адресуют вершину старого стека. Затем процессор анализирует поле счетчика WC дескриптора шлюза вызова и автоматически копирует из старого стека в новый указанное число двойных слов. Они представляют собой параметры, передаваемые вызываемой процедуре. Эти параметры размещаются в новом стеке так же, как и в старом. Наконец, в новый стек включается адрес возврата CS: EIP. Таким образом, после осуществления этих действий новый стек выглядит точно так же, как будто во внутреннем кольце произведено включение в стек параметров и выполнена команда FAR CALL (рис. 9.19).

Начальные значения SS: ESP для трех уровней привилегий в TSS только считываются и никогда не изменяются при работе процессора.

Для инициирования выполнения вызванной процедуры остается только загрузить значения селектора и смещения из дескриптора шлюза вызова в регистры CS: EIP.

Внешний стек

(PL3)

### Внутренний стек

### (PL1)

Параметр 1

12

### SS3

28

Параметр 2

8

### ESP3

24

Параметр 3 | 4 | Параметр 1 | 20
Параметр 4 | SS3: ESP3 | Параметр 2 | 16
|  | Параметр 3 | 12
|  | Параметр 4 | 8
|  | CS | 4
| EIP | SS1:ESP1

Рис. 9.19

При переключении стека процессор осуществляет следующие защитные проверки:

1. контролирует задание правильного указателя стека для более привилегированного уровня;

2. проверяет нахождение сегмента стека на правильном уровне привилегий (например, селектор SS0 должен адресовать сегмент на уровне 0 и иметь поле RPL=0);

3. убеждается в том, что в новом стеке достаточно места для старого указателя стека (8 байт), копируемых параметров (0-128 байт), адреса возврата (8 байт) и тех локальных переменных, которые может включать в стек вызываемая процедура;

Привилегированная процедура осуществляет возврат при помощи команд FAR RET, FAR RETn. При этом происходит обратное переключение стека и удаление параметров из обоих стеков.

### 9.4. Виртуальная память UltraSPARC III

UltraSparc II – это 64-разрядная машина, которая поддерживает виртуальную память со страничной организацией и с 64-битными виртуальными адресами. Однако, по ряду причин программы не могут использовать полное 64-битное виртуальное адресное пространство. Допустимая виртуальная память делится на две зоны по 243 байтов каждая, одна из которых находится в верхней части виртуального адресного пространства, а другая – в нижней. Между ними находится «дырка», содержащая адреса, которые не используются.

Максимальная физическая память компьютера UltraSPARC III составляет 241 байт (2200 Гбайт). Поддерживается четыре размера страниц: 8, 64, 512 Кбайт и 4 Мбайта. Отображение этих страниц приведено на рис. 9.20.

Биты | 51 | 13 |  | 48 | 16 |  | 45 | 19 |  | 42 | 22
| Номер виртуальной страницы К8 | смещение |  | Номер виртуальной страницы К64 | смещение |  | Номер виртуальной страницы К512 | смещение |  | Номер виртуальной страницы М4 | смещение
| Страничный кадр К8 | смещение |  | Страничный кадр К16 | смещение |  | Страничный кадр К512 | смещение |  | Страничный кадр М4 | смещение
Биты | 28 | 13 |  | 25 | 16 |  | 22 | 19 |  | 19 | 22

Рис. 9.20

Из-за огромного виртуального адресного пространства обычная таблица страниц (как в Pentium II) не будет практичной. Здесь применяется следующий подход. Устройство управления памятью содержит таблицу TLB (Translation Lookaside Buffer – буфер быстрого преобразования адреса). Эта таблица отображает номера виртуальных страниц в номера физических страничных кадров. Для страниц размером в 8 К существует 231 номеров виртуальных страниц, все одновременно не могут быть отображены.

Поэтому TLB содержит только номера самых последних используемых виртуальных страниц. Страницы команд и страницы данных рассматриваются отдельно. Для каждой из этих категорий в TLB включены номера 64 последних используемых виртуальных страниц. Каждый элемент буфера включает номер виртуальной страницы и соответствующий номер физического страничного кадра. Когда номер процесса вызывает его контекст, виртуальный адрес в этом контексте передается в контроллер управления памятью, он с помощью специальной схемы сравнивает номер виртуальной страницы со всеми элементами буфера быстрого преобразования адреса TLB для данного контекста одновременно. Если обнаружится совпадение, номер страничного кадра в этом элементе буфера соединяется со смещением, взятым из виртуального адреса, что бы получить 41-битный физический адрес и обработать некоторые флаги. Буфер быстрого

TLB
Бит достоверности | Виртуальная страница | Контекст | Физическая страница | флаги
***

###### Рис. 9.21

преобразования адреса изображен на рис. 9.21. Если совпадение не обнаружено происходит промах в TLB, который вызывает ловушку в операционной системе. Данный промах отличается от ошибки из-за отсутствия страницы. Промах буфера TLB может произойти, даже если нужная страница присутствует в памяти. Теоретически операционная система может сама загрузить новый элемент этого буфера для нужной виртуальной страницы. Однако для ускорения данной операции к этой работе подключается аппаратное обеспечение, если программное обеспечение взаимодействует с ним.

Операционная система должна сохранять наиболее часто используемые элементы буфера TLB в таблице под названием буфер хранения преобразований (TSB – translation storage buffer). Эта таблица простроена как кэш-память прямого отображения виртуальных страниц. Каждый 16-байтный элемент данной таблицы содержит бит достоверности, номер контекста, тэг виртуального адресного пространства, номер физической страницы и несколько флаговых битов. Если размер кэш-памяти составляет, допустим, 8192 элемента, тогда все виртуальные страницы, у которых младшие 13 битов отображаются в 0000000000000, будут претендовать на элемент 0 в данной таблице (рис. 9.22). Размер таблицы определяется программным обеспечением и передается в контроллер управления памятью через специальные регистры, доступные только для операционной системы.

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

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

TLB (контроллер управления памятью + программное обеспечение)
Бит достоверности | Тег виртуальной страницы | Контекст | Физическая страница | флаги
***

######

######

######

###### Элемент 0 совместно используется всеми виртуальными страницами, оканчивающимися на 0 …00

######

######

######

######

###### Элемент 01 совместно используется всеми виртуальными страницами, оканчивающимися на 0 …01

######

###### Рис. 9.22

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

Причина этого различия состоит в том, что Pentium II использует 32-разрядные сегменты, а такие маленькие сегменты (1 млн страниц) могут обрабатываться только с помощью страничных таблиц. Теоретически у Pentium II могли бы возникнуть проблемы, если бы программа использовала тысячи сегментов, но т.к. ни в одной версии Windows или UNIX не поддерживается более одного сегмента на процесс, никаких проблем не возникает. UltraSPARC II — 64-битная машина. Она может содержать до 2 млрд страниц, поэтому таблицы страниц не работают. В будущем все машины будут иметь 64-битные виртуальные адресные пространства, и схема UltraSPARC II станет нормой.

### 9.5. Виртуальная память и кэширование

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

Различия в виртуальной и кэш-памяти. Промахи кэш-памяти обрабатываются аппаратным обеспечением, а ошибки из-за отсутствия страниц обрабатываются операционной системой. Блоки кэш-памяти обычно гораздо меньше страниц (64 байта или 8 Кбайт). Кроме того, таблицы страниц индексируются по старшим битам виртуального адреса, а кэш-память индексируется по младшим битам адреса памяти. Однако, по существу, различие существует только в реализации.