Максимальный режим работы ЦП
Максимальный режим работы ЦП служит для реализации встроенного в него механизма защиты программ и данных друг от друга, что является необходимым условием многозадачной обработки. Основой защищённого режима являются уровни привилегий. Уровень привилегий - это степень использования ресурсов процессора. Всего таких уровней четыре и они имеют номера от 0 до 3. Уровень номер 0 - самый верхний - программе на этом уровне "можно всё". Уровень 1 – следующий в иерархии и запреты, установленные на уровне 0 действуют для уровня 1. Наконец, 3-ий уровень - имеет самый низкий приоритет. Оптимальная схема работы программ по уровням привилегий будет следующая:
- уровень 0: ядро операционной системы,
- уровень 1: драйверы ОС,
- уровень 2: интерфейсы ОС,
- уровень 3: прикладные программы.
Сам по себе уровень привилегий ещё ничего не значит, его нельзя "установить в процессоре". Уровень привилегий применяется как одно их свойств при описании различных объектов, например, программного сегмента и действует при работе только с этим объектом. Уровень привилегий обозначается как "PL" (от Privilege Level) и применяется в сочетаниях, например, IOPL – Input-Output Privilege Level - уровень привилегий ввода-вывода.
Первое, с чем сталкивается программа при переходе в защищённый режим - это совершенно другая система адресации памяти. Напомним, что в режиме реальных адресов - для обращения к памяти используется пара 16-разрядных объектов - сегментный регистр и смещение.
Адресация памяти в защищенном режиме также производится через сегмент и смещение в сегменте, для чего используется пара регистров, но для описания сегмента используется больше информации, а именно:
- 32-разрядный адрес,
- 20-разрядный предел сегмента (предел = размер - 1),
- номер уровня привилегий сегмента,
- тип сегмента (программный, стек, данные или системный объект)
- и некоторые дополнительные свойства, о которых будет сказано ниже.
Для того, чтобы хранить эту информацию, используется специальная область памяти. Сегмент по-прежнему указывается в сегментном регистре, но теперь в нём хранится номер сегмента из списка определённых сегментов. Этот номер называется селектор. В качестве смещения используется 16- или 32-разрядный регистр. При обращении к памяти, процессор проверяет возможность доступа к сегменту по уровню привилегий, проверяет, не превысил ли адрес предел сегмента и можно ли обращаться к этому сегменту в данном случае (например, запрещена передача управления в сегмент, описывающий данные или стек). Если в результате проверки будет обнаружено нарушение какого-либо условия, то процессор сгенерирует исключение и тем самым обеспечит защиту.
Предел сегмента - это максимально допустимое смещение внутри него, таким образом, предел сегмента определяет его размер: размер_сегмента = предел_сегмента + 1. Поскольку значение предела - 20-разрядная величина, это значит, что максимальное значение предела равно 2 20 - 1. Процессор измеряет размер сегмента двумя типами величин: либо байтами, либо страницами. Страница - это блок памяти размером в 4Кб. В описании сегмента можно указать, в каких единицах измеряется сегмент и тогда можно получить два типа сегментов с максимальными размерами:
в 1Мб ( 2 20 байт ) или
в 4Гб ( 2 20 страниц = 2 20 * 4Кб = 2 20 * 2 12 = 2 32 байт )
Эта способность измерять сегмент либо байтами, либо страницами, называется гранулярность. Значение предела сегмента может быть любым, от 0 до 2 20 - 1, гранулярность устанавливается по усмотрению программиста и может быть либо байтная, либо страничная. Всё это позволяет определять сегменты любого размера - от 0 байт до 4Гб.
Сегмент определяется в виде структуры данных, которая называется дескриптор. Размер дескриптора - 8 байт, все дескрипторы хранятся последовательно в специально отведённой области памяти - глобальной дескрипторной таблице. На рис. 5.1. приведен формат структуры дескриптора.
Рис. 5.1. Структура дескриптора
Значения предела и адреса сегмента "разбросаны" по всей структуре дескриптора потому, что впервые защищённый режим появился в 16-разрядном процессоре Intel 80286 и для совместимости с ним дескриптор не переделывали, а расширили дополнительными полями (биты с 49 по 63). Практически, в программах формат дескриптора удобнее использовать в следующем виде:
descriptor:
dw limit_low ; младшее слово предела
dw address_low ; младшее слово адреса
db address_hi ; 3-й (из четырёх) байт адреса
db access_rights ; права доступа
db limit_hi_and_flags ; старшая часть предела и флаги
db address_hi ; 4-й байт адреса
Байт прав доступа (40-47) имеет следующий формат:
40: A – бит доступа (Accessed)
41-43: Тип сегмента (табл. 5.1)
44: S – бит системного сегмента (System)
45-46: DPL – уровень привелегий (Descriptor Privelege Level )
47: P – бит присутствия (Present) сегмента в ОЗУ
Таблица 5.1
Тип | Назначение сегмента |
Сегмент данных только для чтения | |
Сегмент данных для чтения и записи | |
Сегмент стека только для чтения | |
Сегмент стека для чтения и записи | |
Сегмент кода с разрешением только выполнения | |
Сегмент кода с разрешением выполнения и чтения из него | |
Подчиненный сегмент кода с разрешением выполнения | |
Подчиненный сегмент кода с разрешением выполнения и чтения из него |
Тетрада флагов (52-55) G,D,X,U имеет формат:
52: бит U – пользователь (User)
53: бит X – зарезервирован
54: бит D – размерность (Default size) операндов
55: бит G – бит гранулярности (Granularity)
При адресации памяти в защищённом режиме команды ссылаются на сегменты, указывая не их адреса (как в режиме реальных адресов), а описания сегментов (их дескрипторы). Указатель на описание сегмента называется селектор. Другими словами, селектор - это номер дескриптора из таблицы дескрипторов. Адресация производится через пару регистров сегмент:смещение, причём, в качестве сегментного регистра используются обычные CS, SS, DS, ES, FS и GS (последние два появились в ЦП Intel 80386), но в них указывается не адрес сегмента, а селектор дескриптора. Селекторы нужны, по крайней мере, по трём причинам:
· Описание сегмента занимает 8 байт и использовать 8-байтные сегментные регистры было бы крайне неэффективно.
· Селекторы имеют размер в 16 бит, благодаря чему их можно использовать в сегментных регистрах и обращаться к памяти можно по-прежнему через пару регистров.
· Параметры всех сегментов хранятся в отдельной области памяти, доступ к которой имеет только операционная система. Программа, используя селектор, сможет получить о сегменте совсем немного информации и не сможет изменить параметры сегмента, благодаря чему очень удачно реализуется механизм защиты
Адрес памяти можно указывать не только через пару регистров, но и в переменных, через пару значений селектор:смещение.
Можно было бы определить селектор просто, как номер сегмента, но в защищённом режиме кроме сегментов, дескриптор может определять целый ряд других системных объектов (например, задач), поэтому лучше не упрощать понятия селектора и дескриптора, а привыкнуть к этой терминологии.
Селектор имеет следующий формат:
Рис. 5.2. Формат селектора.
- Двухбитовое поле RPL (Requested Privilege Level) содержит номер уровня привилегий, которое имеет текущая программа. Значение этого поля процессор использует для защиты по привилегиям. К одному и тому же дескриптору можно обращаться, используя селекторы с разными значениями RPL, но процессор позволит доступ только при определённых условиях (подробнее об этом см. в главе "Защита по привилегиям").
- Бит TI (Table Indicator) определяет таблицу, из которой выбирается нужный дескриптор. Если бит TI = 0, то обращение производится к глобальной дескрипторной таблице GDT (она одна на всю систему), если TI = 1 - то к текущей локальной дескрипторной таблице LDT (таких может быть много).
- Index - это собственной номер дескриптора, от 0 до 8191. Т.к. поле индекса состоит из 13 бит, то максимальное число дескрипторов, одновременно существующих в системе равно 2 13, т.е. 8192. Как видите, это довольно-таки много и вполне удовлетворяет любым системным запросам. На самом деле, число дескрипторов можно значительно увеличить за счёт использования множества дополнительных локальных дескрипторных таблиц.
Использование селекторов достаточно просто. Для тех дескрипторов, которые будут определены заранее, например, сегментов кода, стека и данных, селекторы подготавливаются как константы и затем используются для загрузки в сегментные регистры. Для дескрипторов, которые программа будет динамически создавать, селекторы придётся определять в переменных и загружать в сегментные регистры из памяти либо конструировать "на ходу", или даже как константы - всё зависит от конкретных условий. Способы использования селекторов и дескрипторов вы можете найти в примерах, которые будут следовать в дальнейших главах.
Обращение к дескрипторной таблице процессор производит только в момент загрузки в сегментный регистр нового селектора. После этого содержимое дескриптора копируется в так называемый "теневой регистр", к которому имеет доступ только сам процессор и из которого оно в дальнейшем используется. Любое последующее обращение к сегменту будет происходить с помощью теневого регистра, без обращения к дескрипторной таблице и не потребует лишних тактов на циклы чтения памяти. Правда, эти такты тратятся каждый раз, когда вы загружаете новый селектор, но это не высокая плата за защиту дескрипторов от недозволенного доступа.
При загрузке недопустимого значения селектора процессор будет генерировать исключение, даже если вы не обращались через него к памяти.
В защищённом режиме работа прерываний происходит следующим образом:
Во-первых, вводится новый класс прерываний, генерируемых самим процессором при нарушениях условий защиты - так называемые исключения (exceptions). Число возможных вектором прерываний по-прежнему равно 256, но 32 из них - от 00h до 1Fh используются исключениями.
Во-вторых, вместо дальних адресов в таблице прерываний используются дескрипторы специальных системных объектов, так называемых шлюзов.
В-третьих, сама таблица прерываний, которая называется IDT (Interrupt Descriptors Table), может находится по любому адресу памяти.
Все эти особенности появились в процессоре Intel 80386 и в полном объёме, с небольшими дополнениями, используются во всех 32-разрядных процессорах.
Прерывания с векторами от 00 до 1Fh, т.е. исключения - это основа защищённого режима. Благодаря исключениям процессор автоматически реагирует на любые попытки нарушить защиту системы и позволяет их корректно обработать. Благодаря разделению кода и данных по уровням привилегий, обработчики прерываний можно надёжно изолировать от других программ.
В грамотно построенной операционной системе никакая программа не сможет перехватить прерывание, изменить код или даже просто прочитать его(!), выйти за предел отведённых ей адресов и пр. Благодаря исключениям, операционная система может контролировать любые нарушения условий, поставленных ею. Когда срабатывает прерывание, процессор должен передать управление соответствующей процедуре-обработчику. В режиме реальных адресов это происходит сразу - из памяти выбирается вектор и по dw:dw адресу происходит переход. В защищённом режиме ситуация обстоит сложнее - перед передачей управления процессор производит множество проверок возможности доступа к обработчику прерывания - обеспечивает защиту.
Адрес, по которому произойдёт переход на обработчик прерывания, находится в дескрипторе прерывания. Каждому вектору прерыванию соответствует свой дескриптор, все они (до 256) объединяются в специальную таблицу дескрипторов прерываний (IDT) и по формату похожи на дескрипторы сегментов, которые мы рассматривали в разделе "Защищённый режим".
Дата добавления: 2018-11-26; просмотров: 651;