В.А. Барков

Средства поддержки ОС на аппаратном уровне

(Конспект лекций)

1. Архитектура машины I386
2. Регистры процессора
3. Принципы адресации
4. Сегментация
5. Виртуальная сегментная адресация
6. Запись дескрипторов GDT на ассемблере
7. Механизм защиты данных
8. Системные дескрипторы
9. Назначение LDT
10. Call gates
11. Мультизадачность
12. Прерывания
13. Paging
14. Внутренний cache

1. Архитектура машины I386

I386.bmp (9134 bytes)

Рис. 1.1. Архитектура машины I386

Длина машинного слова - 32 р. Процессор имеет два входа аппаратного прерывания - NMI (немаскируемое) и INTR (маскируемое).

2. Регистры процессора

Общие регистры (32 р.): EAX, EBX, ECX, EDX, EBP, ESI, EDI, ESP.
Сегментные регистры (16 р., *): CS, DS, SS, ES, FS, GS.
Регистры состояния (32 р.): EFLAGS, EIP.
Регистры защищённого режима (protected mode):
GDTR (48 р., *) - Global Descriptor Table (GDT) Register;
IDTR (48 р., *) - Interrupt Descriptor Table (IDT) Register;
LDTR (16 р., *) - Local Descriptor Table (LDT) Register;
TR (16 р., *) - Task Register. Содержит селектор TSS - Task State Segment.
Регистры управления: CR0..CR3.
Регистры тестирования и отладки: DR0..DR7.

Примечание. Звёздочкой отмечены регистры, имеющие теневую часть.

Важные биты некоторых регистров:
EFLAGS[17] = VM - Virtual 8086 Mode;
EFLAGS[14] = NT - Nested Task flag;
EFLAGS[13..12] = IOPL[1..2] - I/O Privilege Level (PL);
EFLAGS[9] = IF - Interrupt Enable flag. Маскирует внешние аппаратные прерывания.
CR0[31] = PG - Бит, который включает Paging.
CR0[0] = Protect Enable. Бит переключения процессора в защищённый режим.

3. Принципы адресации

32-разрядное машинное слово определяет линейное (linear) адресное пространство размером 2^32 байтов (4 Гб).  Обычно существует требование создания возможности выполнять задачи, размер которых превышает размер физического (physical) адресного пространства. Задачу отображения линейного адресного пространства на физическое решает paging. Реальное содержание линейного адресного пространства хранится на диске в виде последовательных страниц памяти. В каждый момент времени лишь некоторая их часть находится в ОП. ОС по мере необходимости освобождает не очень нужные страницы ОП, копируя их содержимое в соответствующие страницы на диске, и на освободившееся место загружает новые. При этом paging должен обеспечивать правильное преобразование каждого линейного адреса в соответствующий физический адрес ОП. ОС также должна перехватывать обращения к страницам, которым ещё нет соответствия в физической памяти, и подгружать их. Будем считать, что если paging выключить, то физические адреса просто будут совпадать с линейными.

Paging.bmp (135534 bytes)

Рис. 3.1. Paging

Кроме того естественно желание предоставить каждой задаче собственное адресное пространство, которое принято называть виртуальным (virtual). Это позволяет писать код задач совершенно независимо от его последующего размещения в линейном адресном пространстве. Задача преобразования виртуальных адресов в линейные наиболее просто решается на основе идеи сегмента (segment) памяти. Размер сегмента и его расположение в линейном адресном пространстве удобно определить с помощью специальной структуры данных - дескриптора сегмента памяти (descriptor).

4. Сегментация

MemDis.bmp (48406 bytes)

Рис. 4.1. Формат дескриптора сегмента памяти

Base (32 р.) - линейный адрес определяемого сегмента;
Limit (20 р.) - уменьшенный на единицу размер сегмента (доступны адреса 0 .. Limit);
G - Granularity. Значение единицы измерения размера сегмента в поле Limit (0 - 1 б; 1 - 4 Kб);
B - определяет режим работы процессора. Для сегментов данных B = 0, если размер сегмента не превышает 64 Kб, и B = 1 в противном случае. В случае сегментов кода этот бит называется D. При D = 0 процессор выполняет инструкции с 16-разрядными адресами, и с 32-разрядными - в противном случае;
AVL - Available (вспомогательный). Его смысл определяет программист. Обычно используется при сборке мусора:
Access rights - права доступа:
P - Present. Указывает на присутствие сегмента в физической памяти;
DPL - Descriptor PL (0..3);
S - Segment. Tип дескриптора. Для дескрипторов сенгментов памяти S = 1;

Type:
0 - Read only data segment;
1 - Read/write data segment;
3 - Read/write expand-down data segment. Используется для стеков (доступны адреса Limit+1 .. FFFFFFFFH);
4 - Execute only code segment;
5 - Execute/readable code segment.
Другие возможные значения здесь не рассматриваются.

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

Селектор - структура данных, которая используется для обращения к дескрипторам. Формат селектора (16 р.):

Selector.bmp (18438 bytes)

Index - индекс дескриптора в соответствующей дескрипторной таблице;
TI - Table Indicator bit (0 - GDT; 1 - LDT);
RPL - Requested PL.

VirtLin.bmp (185506 bytes)

Рис. 4.2. Схема преобразования виртуальных адресов в линейные

5. Виртуальная сегментная адресация

На основе сегментации может быть организована виртуальная сегментная адресация. Рассмотрим пример её реализации. Пусть объём физической ОП составляет 1000 Кб. Предположим также, что в режиме совмещения необходимо выполнять три задачи - A, B и C, причём каждая содержит 200 Кб исполняемого кода и 200 Кб данных. На рис. 5.1 показаны два варианта размещения соответствующих сегментов, которые возникают последовательно по ходу выполнения задач.

ViSegAdr.bmp (145078 bytes)

Рис. 5.1. Виртуальная сегментная адресация

Мы видим, что сегмент кода задачи A и сегмент кода задачи C занимают одно и то же место в ОП, и поэтому должны загружаться с диска по мере переключения с задачи на задачу. Отметим, что предпочтительнее загружать именно код, а не данные, так как последние могут изменяться в ходе выполнения задачи, а это приводит к необходимости их сохранения при переключении с задачи на задачу.

Преимущества и недостатки виртуальной сегментной адресации:
+ Простота разработки приложений.
+ Возможность "одновременного" выполнения нескольких задач, суммарный объём которых может превышать объём имеющейся физической ОП.
+ Возможность защиты памяти.
+ Возможность совмещения кода некоторых сегментов разными задачами (sharing).
+ Возможность устранения эффекта внешней фрагментации. Небольшие свободные фрагменты памяти легко могут быть объеденены в один фрагмент, имеющий практическое значение - для этого достаточно переместить существующие сегменты памяти.
- Усложнение архитектуры ЦВМ и ОС.

Следует отметить гибкость, которую даёт сегментация:
1) Сегментацию можно игнорировать (для этого надо создать один сегмент кода и один сегмент данных, размеры которых равны размеру ОП).
2) Можно использовать полную сегментацию (каждый объект в своём сегменте).
3) Можно использовать промежуточные формы сегментации.

6. Запись дескрипторов GDT на ассемблере

descr   struc
limit   dw      0  ; Граница (биты 0..15) 
base_l  dw      0  ; База (биты 0..15)
base_m  db      0  ; База (биты 16..23)
attr_1  db      0  ; Байт атрибутов 1
attr_2  db      0  ; Байт атрибутов 2
base_h  db      0  ; База (биты 24..31)
descr   ends
. . .
; GDT
gdt_null descr <0,0,0,0,0,0>          ; Нулевой дескриптор
gdt_data descr <data_size-1,0,0,92h>  ; Сел. 8, сег. данных
gdt_code descr <code_size-1,0,0,9Ah>  ; Сел.16, сег. кода
. . .

7. Механизм защиты данных

Значение поля DPL дескриптора сегмента памяти определяет уровень привилегий этого сегмента - 0 (высший) .. 3 (низший). Говорят также о внутренних и внешних кольцах защиты. Существует также понятие текущего уровня привилегий CPL (Current PL) - это значение поля RPL селектора в CS.

Условие доступа к данным: DPL >= max( RPL, CPL), где RPL (Requested PL) - это запрашиваемый уровень привилегий - значение поля RPL того селектора, который используется для выбора дескриптора сегмента памяти. Зависимость доступа от RPL позволяет ОС оперативно корректировать возможность доступа к данным в зависимости, например, от источника получения селектора. Важно, что при этом не надо изменять код процедуры, выполняющей доступ.

Условие доступа к сегментам кода ещё жёстче: DPL = CPL. Передача управления на другие уровни возможна только при посредничестве специальных сиcтемных объетов, которые называют шлюзами (gates), но и при этом доступ во внутренние кольца может быть только execute only.

Кроме того, введено понятие привилегированных команд процессора - команд, которые можно выполнять только на уровне 0. Вот список наиболее важных привилегированных команд:
LGDT - Load GDT register,
LIDT - Load IDT register,
LLDT - Load LDT register,
LTR - Load Task Register.

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

8. Системные дескрипторы

Системные дескрипторы определяют системные объекты - такие, как LDT, TSS, шлюзы. Следует отметить, что селекторы этих дескрипторов нельзя загружать в сегментные регистры, т.е. они не позволяют работать с этими объектами как со структурами данных. Для этих целей эти же объекты (это касается LDT и TSS) должны быть определены как обычные сегменты данных с теми же значениями полей Base и Limit в дескрипторах, которые принято называть алиасами.

SysDiscr.bmp (78910 bytes)

Рис. 8.1. Формат системных дескрипторов

Type:
2 - LDT;
5 - Task gate;
9 - TSS.  Type[1] = Busy. Если бит Busy установлен, то Type = B (Busy TSS);
C - Call gate;
E - Interrupt gate;
F - Trap gate.

Поля Selector и Offset дескрипторов шлюзов определяют точку в линейном адресном пространстве, в которую передаётся управление через данный шлюз. Поле Dword count используется только в Call gate.

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

9. Назначение LDT

На рис. 9.1 даны два варианта организации адресного пространства двух задач - A и B.

LdtUse.gif (4452 bytes)

Рис. 9.1. Назначение LDT

В варианте а) код задачи A не может изменить состояние GDT, так как она находится во внутреннем кольце защиты, но может "подделать" селектор и обратиться как к данным, так и к коду задачи B. В варианте б) в GDT размещены дескрипторы двух LDT, из которых только один может быть выбран загрузкой соответствующего селектора в LDTR, а это означает принципиальную недостижимость сегментов задачи B из задачи A и наоборот.

10. Call gates

Передача управления через них происходит при выполнении инструкции CALL.  Поле Dword Count определяет количество двойных слов, которые передаются из стека уровня вызывающей программы в стек уровня вызываемой. Такой механизм передачи параметров необходим по соображениям защиты данных. Эти же соображения приводят к необходимости иметь собственный стек на каждом уровне защиты. Для возврата в вызывающую программу используется команда RET.

11. Мультизадачность

Селектор дескриптора текущей TSS содержится в TR.

Tss.bmp (68794 bytes)

Рис. 11.1. Формат TSS

Минимальный размер TSS составляет 104 байта. Содержание TSS достаточно очевидно. На рисунке серым отмечены поля, которые не сохраняют соответствующие значения в TSS старой задачи, но используются для установки соответствующих величин из TSS новой задачи. Значения этих полей могут быть изменены только на нулевом уровне привилегий. Поле Back link содержит селектор TSS предыдущей задачи. Незаполненные клетки соответствуют полям с нулевыми значениями.

Переключение задач могут инициировать четыре события:
1) Текущая задача выполнила инструкцию CALL или JMP с селектором дескриптора TSS.
2) Текущая задача выполнила инструкцию CALL или JMP с селектором дескриптора Task gate.
3) Текущая задача выполнила инструкцию IRET и в EFLAGS NT = 1.
4) Происходит прерывание с Task gate в IDT.

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

Если переключение выполняется по инструкции JMP, то бит Busy дескриптора TSS старой задачи сбрасывается, а новой - устанавливается.

Если причиной переключения была инструкция CALL или прерывание, то бит Busy дескриптора TSS старой задачи остаётся установленным в 1. Бит NT в EFLAGS тоже устанавливается в 1.

Если переключение произошло в результате выполнения инструкции IRET, то бит Busy дескриптора TSS старой задачи устанавливается в 0.

Режим выполнения инструкции IRET зависит от значения бита NT. Если он установлен, то выполняется обратное переключение на задачу, селектор дескриптора которой находится в поле Back link TSS. В противном случае вполняется обычный возврат из программы обработки прерывания (через стек).

12. Прерывания

Классификация: Interrupts ( Hardware Interrupts, Exceptions ( Traps, Faults, Aborts ) ).

Каждый источник прерывания имеет номер прерывания, который определяет шлюз в IDT, определяющий в свою очередь программу обработки прерывания - обработчик прерывания (Interrupt Handler). CS:EIP и EFLAGS прерванной программы, а при Exceptions ещё и код ошибки (Error code), помещаются в стек перед передачей управления обработчику прерывания. В IDT можно включать только Interrupt gates, Trap gates и Task gates. Task gates в IDT работают точно так же, как и в GDT. Interrupt gates и Trap gates различаются только тем, что в первом случае происходит очистка бита IF в EFLAGS, а во втором - он остаётся без изменения. Для выхода из обработчика прерывания служит инструкция IRET, причём Error code надо предварительно вытолкнуть из стека. Интепретация Error code зависит от номера прерывания.

Stack.bmp (21202 bytes)

Рис. 12.1. Состояние стека в момент входа в обработчик прерывания

Traps (ловушки) - в стек помещается CS:EIP инструкции, следующей за инструкцией, в ходе которой возникает прерывание. Все программные прерывания (инициируемые инструкцией INT) обрабатываются как ловушки, причём DPL дескриптора шлюза должен быть равен уровню привилегий, на котором выполняется инструкция INT.

Faults (отказы) - в стек помещается CS:EIP инструкции, в ходе выполнения которой возникла ошибка. Предполагается, что если её устранить в ходе выполнения обработчика прерывания, то после выхода из него программа продолжит нормальную работу так, как будто прерывания и не было. Классический пример - прерывание 11 (Segment not present).

Aborts (аварии) - серъёзная ошибка с возможной утерей части контекста. Информация об ошибке может быть не достоверна.

Shut down - выключение процессора.

Таблица прерываний

Interrupt
number
Class Description
0 Fault Divide error
1 Fault or Trap Debugger interrupt
2 Interrupt Nonmaskable interrupt
3 Trap Breakpoint
4 Trap Interrupt on overflow (INTO)
5 Fault Array boundary violation (BOUND)
6 Fault Invalid opcode
7 Fault Coprocessor not available
8 Abort Double fault
9 Abort Coprocessor segment overrun
10 Fault Invalid TSS
11 Fault Segment not present
12 Fault Stack exception
13 Fault General protection violation
14 Fault Page fault
15 Reserved  
16 Fault Coprocessor error
17 Fault Alignment check
18 - 31 Reserved  

32 - 255

Interrupt
or Trap
System dependent

13. Paging

Физическая память делится на страницы по 4 Kб (page frames). Адрес страницы (page frame address) - старшие 20 битов физического адреса любого байта, принадлежащего странице.

LinToPhs.bmp (127974 bytes)

Рис. 13.1. Схема преобразования линейных адресов в физические

PgTbEntr.bmp (27774 bytes)

Рис. 13.2. Формат элемента таблицы страниц

Каталог страниц (page directory) и таблица страниц (page table) состоит из элементов (page table entry) одинакового формата. Бит P (Present) определяет наличие страницы в физической памяти. Другие биты мы здесь не рассматриваем - они необходимы для организации виртуальной страничной памяти. Так принято называть этот метод адресации. Слово "виртуальный" здесь имеет более общее значение и не имеет буквального отношения к виртуальной адресации в сегментах памяти. Адреса страниц и адрес в CR3 - физические.

Одна таблица страниц "обслуживает" 4 Mб физической ОП. Например, для ОП объёмом 32 Mб необходимо 8 таблиц страниц, что требует, включая необходимую страницу для каталога страниц, 36 Kб памяти!

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

Другой недостаток состоит в том, что из-за необходимости обращения к элементам таблиц страниц резко увеличивается количество обращений к ОП. С целью его уменьшения в архитектуру машины включено специальное буферное ЗУ (TLB - translation lookside buffer), в котором могут находиться 32 наиболее часто используемых элемента таблицы страниц. Утверждается, что TLB удовлетворяет до 98% обращений к ОП.

14. Внутренний cache

Вводится в архитектуру процессора с целью увеличения его производительности.

Cache.bmp (105310 bytes)

Рис. 14.1. Обращение к ОП с cache