После продолжительной паузы настал момент вернуться к написанию цикла по разработке учебной операционной системы. При разработке основного (не учебного) варианта PhantomEx возник ряд трудностей, которые требовали вдумчивого разбора.
На сегодня, после реализации в ядре PhantomEx работы с потоками накопилась уйма практического материала для продолжения, и мы будем продолжать, рассматривая тему страничной организации памяти.
До сих пор наше учебное ядро использовало сегментную модель организации памяти в защищенном режиме, однако такая схема к настоящему времени безнадежно устарела - все современные ОС на 32-разрядной архитектуре x86 используют страничную модель памяти, не говоря уже о том что в архитектуре x86-64 от сегментной модели осталось несколько рудиментарных вещей, для обратной совместимости со старым ПО, а полной поддержки сегментной модели и аппаратной многозадачности там уже нет.
Тема организации страничной памяти достаточно обширна, так что она растянется на несколько статей. Начнем с теории.
1. Страничная организация памяти в процессорах Intel
Рассмотрим схему, иллюстрирующую страничную адресацию в процессорах Intel x86.
Физическая память разбивается на участки, называемые страницами. Наиболее характерный размер страниц - 4 Кб. При использовании режима PAE, доступного в процессорах Intel начиная с Pentium Pro размер страниц может устанавливаться в 2 Мб, однако режим PAE мы здесь рассматривать не будем.
Каждая страница размещается в конкретном месте физической памяти, и адрес её начала в памяти называется адресом страницы, или адресом физического фрейма. Если мы обращаемся к памяти внутри страницы, то физический адрес будет равен адресу страницы + смещение относительно начала страницы.
С другой стороны, любая программа обращается к памяти используя так называемый виртуальный адрес. Виртуальное адресное пространство каждого процесса (ВАП) в 32-разрядной системе имеет диапазон адресов от 0x00000000 - 0xFFFFFFFF, то есть непрерывная область размером в 4 Гб. Это очень удобно для разработки программ так как компилятор, собирая программу не озабочен структурой физической памяти конкретного компьютера.
При обращении к памяти процессор выполняет трансляцию виртуального адреса в физический используя информацию о правилах формирования физического адреса, которая размещается в каталоге страниц. Каждому процессу, исполняемому в системе соответствует свой каталог страниц.
Алгоритм трансляции виртуального адреса в физически можно изобразить в виде схемы
Алгоритм трансляции виртуального адреса в физически можно изобразить в виде схемы
Первые 12 бит виртуального адреса задают смещение внутри страницы. Их как раз хватает чтобы адресовать 212 = 4096 байт внутри страницы. Биты с 12 по 21 кодируют индекс страницы внутри таблицы, таким образом в одной таблице возможно размещение 210 = 1024 дескрипторов страниц. И наконец биты 22 - 31 задают индекс таблицы в каталоге, то есть в каталоге возможно разместить 210 = 1024 дескрипторов таблиц. Располагая таким объемом информационных структур можно разметить по страницам 1024·1024·4096 = 4 Гб всей доступной физической памяти в 32-разрядной системе.
Биты 12 - 31 дескриптора страницы/таблицы задают адрес размещения страницы/таблицы в физической памяти, то есть очевидно что данный адрес имеет формат 0xYYYYY000. Этот адрес разыскивается процессором в каталоге страниц по индексу таблицы и страницы и складывается с 12-битным смещением внутри страницы, давая физический адрес размещения данных/кода в памяти.
И вот тут проявляется самая "вкусная" идея системы страничной адресации - физический адрес обладает известным произволом, определяемым адресом размещения страницы в ОЗУ. То есть одному и тому же виртуальному адресу может соответствовать несколько физических адресов, отличающихся старшими 20 битами. Таким образом возможна физическая изоляция друг от друга ВАП разных процессов - располагая код и данные формально по одному и тому же виртуальному адресу, на деле получаем размещение данных разных процессов в разных областях физической памяти.
Процедура трансляции виртуального адреса в физический достаточно трудоемка для процессора, поэтому в нем предусмотрен специальный кэш страниц - TLB, так что если к странице происходят достаточно частые обращения её физический адрес будет взят из TLB напрямую без "честной" трансляции.
Что касается полного формата дескриптора, то от выглядит так
Таблица 17. Формат дескриптора страницы/таблицы
Биты | Назначение |
31-12 | биты 31-12 физического адреса страницы |
11-9 | зарезервированы для операционной системы |
8 | G - глобальная страница. Страница не удаляется из буфера TLB при переключении задач или перезагрузке регистра CR3 |
7 | PS - размер страницы 1 - страница 2 или 4 Мб, иначе - 0 |
6 | D - "грязная" страница, устанавливается в 1 при записи в страницу |
5 | A - бит доступа, устанавливается в 1 при обращении к странице |
4 | PCD - бит запрещения кэширования |
3 | PWT - бит разрешения сквозной записи |
2 | U - страница доступна из пользовательского режима (с CPL=3) |
1 | W - страница доступна для записи |
0 | P - страница присутствует |
Таким образом младшие 12 бит используются для кодирования служебной информации, касающейся прав доступа к таблицам/страница и правил работы с ними процессора. Рассмотрим эти флаги подробнее
P - бит присутствия, если он взведен, страница присутствует в физической памяти. Если страницы нет в памяти, генерируется исключение #PF (прерывание INT 0Eh). Обработка данного исключения используется в частности для организации подкачки страниц с HDD.
W - бит разрешения записи. Если он сброшен, то страница будет доступна только для чтения, и попытка записи в неё генерирует исключение #PF.
U - доступ к странице из пользовательского режима, если этот бит взведен, то к странице можно обращаться из кода с уровнем привилегий 3 кольца защиты.
Для того чтобы включить страничную память необходимо:
- Сформировать в памяти структуру, определяющую отображение физических страниц в ВАП ядра ОС, то есть создать каталог страниц и разметить хотя бы ту физическую память, где размещено ядро и области ОЗУ к которым оно будет обращаться в процессе работы.
- Загрузить адрес каталога страниц в регистр CR3 процессора.
- Взвести бит 31 регистра CR0, это действие в общем-то и включает режим страничной адресации.
Таблица 18. Формат регистра CR3
Биты | Назначение |
31-12 | 20 старших бит физического адреса начала каталога страниц, если бит PAE в CR4 равен нулю, или |
31-5 | 27 старших бит физического адреса таблицы указателей на каталоги страниц, если бит PAE = 1. |
4 (80486+) | бит PCD - запрещение кэширования страниц - этот бит запрещает загрузку текущей страницы в кэш-память |
3 (80486+) | бит PWT - бит сквозной записи 0 управляет методом записи страниц во внешний кэш |
Мы не используем режим PAE, поэтому нас интересует адрес каталога страниц в формате 0xYYYYY000 - то есть выровненный по границе 4 Кб страницы. Первые 12 бит адреса, загруженного в CR3 просто игнорируются процессором и об этом необходимо помнить, формируя структуру каталога страниц! При невыполнения этого условия процессор просто не найдет необходимой ему корректной информации и перезагрузит компьютер.
Завершая теоретическую часть статьи, думаю уместно вскользь упомянуть о режиме PAE, который появился в процессорах Pentium Pro и выше. Суть его заключается в том, что данные процессоры оснащены 36-разрядной шиной адреса, то есть фактически способны адресовать 236 = 64 Гб оперативной памяти. При взведенном бите PAE в регистре CR4 этот режим становится доступным.
Таким образом даже в x86 системе доступна возможность адресовать память более 4 Гб, но эта фишка актуальна для систем технически допускающих установку на материнскую плату более 4 Гб оперативной памяти, и это в основном серверные решения. Ядро Linux например, в его 32-разрядной версии можно собрать с опцией поддержки PAE, однако в связи с широкой распространенностью архитектуры x86-64 даже на домашних ПК эта возможность теряет свою актуальность.
На этом теоретическую часть я завершу, в следующих статьях сосредоточившись на практической реализации описанного в нашем ядре.
Комментариев нет:
Отправить комментарий