Управление памятью.

Глава 6. Включение страничного преобразования.

        Прежде, чем включить в программе страничное преобразование, нужно выполнить следующие действия:
1. Подготовить описания всех используемых в программе страниц, таблиц страниц и каталога страниц.
2. Загрузить адрес начала (т.е. базовый адрес) каталога страниц в CR3.
3. Разрешить в процессоре страничное преобразование.

1. Подготовка описаний страниц.

        Мы будем использовать формат страницы в следующем виде:


Рисунок 6-1. Простой вид элемента PTE.

        Формат элемента PTE (элемент таблицы страниц) будет в следующем виде:
Бит: Описание
0: P = 1/0, если страница определена / нет
1: R/W = 1/0, если страницу можно писать и читать / только читать
2: U/S = 0, т.к. наши примеры пока работают только на нулевом уровне привилегий
3..11: 0 (не используются)
12..31: Старшие 20 бит физического адреса, на который отображена страница.

        Как видите, нужно будет устанавливать только биты 0 и 1 в элементах PDE и PTE, что мы и будем делать примерно следующим образом:

	mov	eax,page_address	; Загружаем в EAX выравненный на
					;  границу 4Кб адрес страницы.

	mov	al,3		; Устанавливаем биты 0 и 1 (все остальные
				;  должны быть равны 0).

	mov	PTE_or_PDE,eax	; Записываем готовый элемент PDE
					;  или PTE.

        В примере к этой главе мы будем использовать только первый мегабайт адресного пространства, чтобы не перегружать исходник лишними деталями; первый мегабайт гарантированно есть в любом используемом сейчас компьютере и поэтому не нужно дополнительно определять количество присутствующей физической памяти. В нашем примере мы будем использовать только один элемент каталога страниц - PDE 0, который определит одну таблицу страниц. Этого достаточно, т.к. таблица страниц может покрыть до 4Мб адресного пространства.
        В таблице страниц мы определим только первые 256 страниц (т.е. элементов PTE) и зададим им тождественное отображение. Это значит, что линейные адреса будут совпадать с физическими и для элемента PTE 0 базовый адрес будет равен 0000 0000h, для PTE 1 - 0000 1000h, PTE 2 - 0000 2000h и т.д.
        256 страниц по 4Кб каждая образуют один мегабайт. Все остальные элементы PTE мы просто очищаем нулями - главное, чтобы бит P (0-й бит в каждом PTE) был равен 0 для неопределённых страниц - тогда процессор не допустит обращения к ним и сгенерирует исключения страничного нарушения.
        Все операции по установке элементов PDE и PTE выполняет процедура "set_pages". Эта процедура сбрасывает (т.е. записывает в них нули) все элементы PDE и все PTE в первой и единственной таблице страниц. Затем она устанавливает первый элемент каталога страниц (PDE 0), в котором указывает адрес таблицы страниц и, наконец, устанавливает первые 256 PTE в таблице страниц. В качестве указателя на каталог страниц используется содержимое переменной Page_Directory, которая записывается другой процедурой set_PDBR (о ней - см. дальше).


set_pages proc near ; Установка каталога страниц, PDE 0 и тождественного отображения страниц для ; первого мегабайта. mov ax,Data_selector mov es,ax mov di,Page_Directory ; ES:DI = указатель на каталог страниц. xor eax,eax mov cx,1024 + 1024 ; Очистка 2-х страниц памяти (по 4Кб). cld push di rep stosd ; Очищаем все элементы каталога страниц ; (PDE 0 .. PDE 1023) и всю первую таблицу ; страниц (соответствующую элементу PDE 0). pop di mov eax,cr3 ; EAX = адрес начала каталога страниц. add eax,4096 ; Первая таблица страниц (PDE 0) будет ; располагаться сразу за каталогом страниц. mov al,3 ; Флаги PDE: P=1, R/W = 1 stosd ; Записали PDE 0 add di,4096 - 4 ; Переходим к таблице страниц xor eax,eax ; Начнём с адреса 0 mov al,3 ; Флаги PTE: P=1, R/W = 1 mov cx,256 ; Установим 256 страниц (они покроют ; первый мегабайт физической памяти). spag_1: stosd add eax,4096 loop spag_1 ret endp

2. Загрузка базового адреса каталога страниц в CR3.

        Вообще говоря, загрузка регистра CR3 должна проводится после того, как будет определена вся используемая структура страниц, но на самом деле не имеет значения, когда вы запишите в CR3 базовый адрес каталога страниц - до тех пор, пока в процессоре не будет разрешено страничное преобразование, этот регистр также не будет использоваться.
        В нашем примере установка CR3 производится почти в самом начале программы, процедурой "set_PDBR". Дело в том, что каталог страниц и любые таблицы страниц должны находиться целиком в физической странице памяти, т.е. по адресам, кратным 4Кб. Определить эти адреса гораздо проще находясь в режиме реальных адресов, используя значения сегментных регистров - в защищённом режиме понадобилось бы обратиться к GDT, чтобы получить адрес текущего сегмента данных, либо вводить дополнительные переменные.
        Процедура "set_PDBR" вычисляет адрес каталога страниц как адрес ближайшей к концу программы свободной физической страницы памяти и помещает его в CR3:


set_PDBR proc near ; Установка значения в PDBR xor eax,eax mov edx,eax mov ax,ds shl eax,4 mov ebx,eax ; Сохраняем в EBX физический адрес начала ; сегмента данных. lea dx,Last_label ; Last_label - это последняя метка программы. add eax,edx ; EAX = физический адрес последней метки. mov dx,ax and ax,0f000h ; Сбрасываем младшие 12 бит адреса. sub dx,ax cmp dx,0 ; Адрес последней метки уже был выравнен ; на границу 4Кб ? je spdbr_1 add ah,10h ; Если нет, то выравниваем его. spdbr_1: mov cr3,eax ; Загрузка адреса в CR3 (т.е. в PDBR). ; По этому адресу будет размещён каталог страниц. sub eax,ebx ; EAX = AX = смещение каталога страниц ; относительно начала сегмента данных. mov Page_Directory,ax ret endp

Примечание:
        Команда ADD AH,10h здесь используется для выравнивания адреса на границу 4Кб. Эта команда эквивалентна команде:

	add	eax,1000h

но, т.к. в EAX уже находится адрес со сброшенными 12 младшими битами, а добавляем мы dw-число, то код можно сократить:

	add	ax,1000h

или

	add	ah,10h

        Всё-таки, мы пишем на Ассемблере...

3. Разрешение в процессоре страничного преобразования.

        Для того, чтобы включить или, другими словами, разрешить в процессоре страничное преобразование, необходимо установить бит PG (это 31-й бит) в регистре управления CR0.
        После того, как это будет сделано, нужно выполнить команду перехода - она заставит процессор сбросит конвейер и начать выборку команд уже используя механизм трансляции страниц.
        Перед возвратом программы в режим реальных адресов нужно запретить страничное преобразование - сбросить бит PG в CR0 и выполнить команду перехода - в этом случае она заставит процессор при выборке команд не использовать содержимое буферов TLB и программа корректно выйдет в R-Mode.

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

        Для разрешения страничного преобразования в наших примерах будет использоваться процедура "set_paging", для запрещения - "reset_paging":


set_paging proc near ; Включение страничного преобразования mov eax,cr0 bts eax,31 ; Устанавливаем 31-й бит (PG) mov cr0,eax jmp spg_1 spg_1: ret endp
reset_paging proc near ; Запрещение страничного преобразования mov eax,cr0 btc eax,31 ; Сбрасываем 31-й бит (PG) mov cr0,eax jmp rspg_1 rspg_1: ret endp

        В примере оставлен подсчёт времени - теперь только до 2-х секунд. Результатом работы страничного преобразования этого примера есть то, что он - работает. Это значит, что страничное отображение было установлено правильно и все элементы PTE и элемент PDE хранят правильные адреса и флаги; в противном случае, процессор попросту зависнет либо произойдёт аппаратный сброс, т.к. в программе разрешены прерывания.
        И всё же изменения в работе примера есть - число, которое останется на экране будет отличаться от того, которое там осталось, если бы вы закомментировали вызов процедуры "set_paging". Оно - больше (на 2-3 процента, но больше). Это результат работы страничного отображения - работают буферы TLB, кэшируя обращения к памяти (TLB - это отдельная тема).
        Ещё больший выигрыш в производительности видно с появлением мультизадачности, когда процессору приходится часто пропускать через себя большие объёмы данных.

        Итак, данный пример, предназначенный для демонстрации установки страничного отображения, вы можете скачать здесь: examp_7.asm, pmode_7.lib и examp_7.com в архиве examp_7.zip (10'602 байт).

Следующая глава Оглавление Вопросы? Замечания? Пишите: sasm@narod.ru

  Copyright © Александр Семенко.
TopList

Hosted by uCoz