Assembler - язык неограниченных возможностей

         

Страничная адресация


Линейный адрес, который формируется процессором из логического адреса, соответствует адресу из линейного непрерывного пространства памяти. В обычном режиме в это пространство могут попадать области памяти, в которые нежелательно разрешать запись, — системные таблицы и процедуры, ПЗУ BIOS и т.д. Чтобы этого избежать, система может разрешать программам создавать только небольшие сегменты, но тогда теряется такая привлекательная идея flat-памяти. Сегментация — не единственный вариант организации памяти, который поддерживают процессоры Intel. Существует второй, совершенно независимый механизм — страничная адресация (pagination).

При страничной адресации непрерывное пространство линейных адресов памяти разбивается на страницы фиксированного размера (обычно 4 Кб (4096 или 1000h байт), но Pentium Pro может поддерживать и страницы по 4 Мб). При обращении к памяти процессор физически обращается не по линейному адресу, а по тому физическому адресу, с которого начинается данная страница. Описание каждой страницы из линейного адресного пространства, включающее в себя ее физический адрес и дополнительные атрибуты, хранится в одной из специальных системных таблиц, как и в случае сегментации, но в отличие от сегментации страничная адресация абсолютно невидима для программы.

Страничная адресация включается при установке бита PG регистра CR0, если бит РЕ установлен в 1 (попытка установить PG, оставаясь в реальном режиме, приводит к исключению #GP(0)). Кроме того, предварительно надо поместить в регистр CR3 физический адрес начала каталога страниц — главной из таблиц, описывающих страничную адресацию. Каталог страниц имеет размер 4096 байт (ровно одна страница) и содержит 1024 4-байтных указателя на таблицы страниц. Каждая таблица страниц тоже имеет размер 4096 байт и содержит указатели до 1024 4-килобайтных страниц. Если одна страница описывает 4 килобайта, то полностью заполненная таблица страниц описывает 4 мегабайта, а полный каталог полностью заполненных таблиц — 4 гигабайта, то есть все 32-битное линейное адресное пространство. Когда процессор выполняет обращение к линейному адресу, он сначала использует его биты 31 – 22 как номер таблицы страниц в каталоге, затем биты 21 – 12 как номер страницы в выбранной таблице, а затем биты 11 – 0 как смещение от физического адреса начала страницы в памяти. Так как этот процесс занимает достаточно много времени, в процессоре предусмотрен специальный кэш страниц — TLB (буфер с ассоциативной выборкой), так что, если к странице обращались не очень давно, процессор определит ее физический адрес сразу.


Элементы каталога страниц и таблиц страниц имеют общий формат:

биты 31 – 12: биты 31 – 12 физического адреса (таблицы страниц или самой страницы)

биты 11 – 9: доступны для использования операционной системой

бит 8: G — «глобальная страница» — страница не удаляется из буфера TLB при переключении задач или перезагрузке регистра CR3 (только на Pentium Pro, если установлен бит PGE регистра CR4)

бит 7: PS — размер страницы. 1 — для страницы размером 2 или 4 мегабайта, иначе — 0

бит 6: D — «грязная страница» — устанавливается в 1 при записи в страницу; всегда равен нулю для элементов каталога страниц

бит 5: А — бит доступа (устанавливается в 1 при любом обращении к таблице страниц или отдельной странице)

бит 4: PCD — бит запрещения кэширования

бит 3: PWT — бит разрешения сквозной записи

бит 2: U — страница/таблица доступна для программ с CPL = 3

бит 1: W — страница/таблица доступна для записи

бит 0: Р — страница/таблица присутствует. Если этот бит — 0, остальные биты элемента система может использовать по своему усмотрению, например, чтобы хранить информацию о том, где физически находится отсутствующая страница

Процессоры Pentium Pro (и старше) могут поддерживать расширения страничной адресации. Если установлен бит РАЕ, физический адрес оказывается не 32-битным (до 4 Гб), а 36-битным (до 64 Гб). Если установлен бит PSE регистра CR4, включается поддержка расширенных страниц размером 4 Мб для РАЕ = 0 и 2 Мб для РАЕ = 1. Такие страницы описываются не в таблицах страниц, а прямо в основном каталоге. Intel рекомендует помещать ядро операционной системы и все, что ему необходимо для работы, на одну 4-мегабайтную страницу, а для приложений пользоваться 4-килобайтными страницами. Расширенные страницы кэшируются в отдельном TLB, так что, если определена всего одна расширенная страница, она будет оставаться в TLB все время.



Для расширенных страниц формат элемента каталога совпадает с форматом для обычной страницы (кроме того, что бит PS = 1), но в качестве адреса используются только биты 31 – 22 — они соответствуют битам 31 – 22 физического адреса начала страницы (остальные биты адреса — нули).

Для расширенного физического адреса (РАЕ = 1) изменяется формат регистра CR3 (см. главу 10.1.3), размеры всех элементов таблиц становятся равными 8 байтам (причем используются только биты 0 – 3 байта 4), так что их число сокращается до 512 элементов в таблице и вводится новая таблица — таблица указателей на каталоги страниц. Она состоит из четырех 8-байтных элементов, каждый из которых может указывать на отдельный каталог страниц. В этом случае биты 31 – 30 линейного адреса выбирают используемый каталог страниц, биты 29 – 21 — таблицу, биты 20 – 12 — страницу, а биты 11 – 0 — смещение от начала страницы в физическом пространстве (следовательно, если биты 29 – 21 выбрали расширенную страницу, биты 20 – 0 соответствуют смещению в ней).

Основная цель страничной адресации — организация виртуальной памяти в операционных системах. Система может использовать внешние устройства — обычно диск — для расширения виртуального размера памяти. При этом, если к какой-то странице долгое время нет обращений, система копирует ее на диск и помещает отсутствующей в таблице страниц. Затем, когда программа обращается по адресу в отсутствующей странице, вызывается исключение #РЕ Обработчик исключения читает адрес, вызвавший ошибку из CR2, определяет, какой странице он соответствует, загружает ее с диска, устанавливает бит присутствия, удаляет копию старой страницы из TLB командой INVLPG и возвращает управление (не забыв снять со стека код ошибки). Команда, вызывавшая исключение типа ошибки, выполняется повторно.



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

Второе не менее важное применение страничной адресации — безопасная реализация flat-модели памяти. Операционная система может разрешить программам обращаться к любому линейному адресу, но отображение линейного пространства на физическое не будет взаимно однозначным. Скажем, если система использует первые 4 Кб памяти, физическим адресом нулевой страницы будет не ноль, а 4096 и пользовательская программа даже не узнает, что обращается не к нулевому адресу. В этом случае, правда, и сама система не сможет обращаться к первой физической странице без изменения таблицы страниц, но эта проблема решается при применении механизма многозадачности, о котором рассказано далее.

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

; pm3.asm ; Программа, демонстрирующая страничную адресацию. ; Переносит одну из страниц, составляющих видеопамять, и пытается закрасить ; экран ; ; Компиляция: ; TASM: ; tasm /m pm3.asm ; tlink /x /3 pm3.obj ; MASM: ; ml /с pm3.asm ; link pm3.obj,,NUL,,, ; WASM: ; wasm pm3.asm ; wlink file pm3.obj form DOS

.386р RM_seg segment para public "CODE" use16 assume cs:RM_seg,ds:PM_seg,ss:stack_seg start: ; подготовить сегментные регистры push PM_seg pop ds ; проверить, не находимся ли мы уже в РМ mov еах,cr0 test al,1 jz no_V86 ; сообщить и выйти mov dx,offset v86_msg err_exit: push cs pop ds mov ah,9 int 21h mov ah,4Ch int 21h



; убедиться, что мы не под Windows no_V86: mov ax,1600h int 2Fh test al,al jz no_windows ; сообщить и выйти mov dx,offset win_msg jmp short err_exit

; сообщения об ошибках при старте v86_msg db "Процессор в режиме V86 - нельзя переключиться в РМ$" win_msg db "Программа запущена под Windows - нельзя перейти в кольцо 0$"

; итак, мы точно находимся в реальном режиме no_windows: ; очистить экран и переключиться в нужный видеорежим mov ax,13h int 10h ; вычислить базы для всех дескрипторов xor еах,еах mov ax,RM_seg shl eax,4 mov word ptr GDT_16bitCS+2,ax shr eax,16 mov byte ptr GDT_16bitCS+4,al mov ax,PM_seg shl eax,4 mov word ptr GDT_32bitCS+2,ax shr eax,16 mov byte ptr GDT_32bitCS+4,al ; вычислить линейный адрес GDT xor eax,eax mov ax,PM_seg shl eax,4 push eax add eax,offset GDT mov dword ptr gdtr+2,eax ; загрузить GDT lgdt fword ptr gdtr ; открыть А20 - в этом примере мы будем пользоваться памятью выше 1 Мб mov al,2 out 92h,al ; отключить прерывания cli ; и NMI in al,70h or al,80h out 70h,al ; перейти в защищенный режим (пока без страничной адресации) mov еах,cr0 or al,1 mov cr0,eax ; загрузить CS db 66h db 0EAh dd offset PM_entry dw SEL_32bitCS RM_return: ; переключиться в реальный режим с отключением страничной адресации mov eax,cr0 and eax,7FFFFFFEh mov cr0,eax ; сбросить очередь и загрузить CS db 0EAh dw $+4 dw RM_seg ; загрузить остальные регистры mov ax,PM_seg mov ds,ax mov es,ax ; разрешить NMI in al,70h and al,07FH out 70h,al ; разрешить другие прерывания sti ; подождать нажатия клавиши mov ah,1 int 21h ; переключиться в текстовый режим mov ax,3 int 10h ; и завершить программу mov ah,4Ch int 21h RM_seg ends

PM_seg segment para public "CODE" use32 assume cs:PM_seg ; таблица глобальных дескрипторов GDT label byte db 8 dup(0) GDT_flatDS db 0FFh,0FFh,0,0,0,10010010b,11001111b,0 GDT_16bitCS db 0FFh,0FFh,0,0,0,10011010b,0,0 GDT_32bitCS db 0FFh,0FFh,0,0,0,10011010b,11001111b,0 gdt_size = $ - GDT gdtr dw gdt_size-1 ; ее лимит dd ? ; и адрес SEL_flatDS equ 001000b ; селектор 4-гигабайтного сегмента данных SEL_16bitCS equ 010000b ; селектор сегмента кода RM_seg SEL_32bitCS equ 011000b ; селектор сегмента кода PM_seg



; точка входа в 32-битный защищенный режим PM_entry: ; загрузить сегментные регистры, включая стек xor еах,еах mov ax,SEL_flatDS mov ds,ax mov es,ax ; создать каталог страниц mov edi,00100000h ; его физический адрес - 1 Мб mov eax,00101007h ; адрес таблицы 0 = 1 Мб + 4 Кб stosd ; записать первый элемент каталога mov ecx,1023 ; остальные элементы каталога - xor еах,еах ; нули rep stosd ; заполнить таблицу страниц 0 mov eax,00000007h ; 0 - адрес страницы 0 mov ecx,1024 ; число страниц в таблице page_table: stosd ; записать элемент таблицы add еах,00001000h ; добавить к адресу 4096 байт loop page_table ; и повторить для всех элементов ; поместить адрес каталога страниц в CR3 mov еах,00100000h ; базовый адрес = 1 Мб mov cr3,еах ; включить страничную адресацию mov eax,cr0 or eax,80000000h mov cr0,eax ; а теперь изменить физический адрес страницы A1000h на А2000h mov eax,000A2007h mov es:00101000h+0A1h*4,eax ; если закомментировать предыдущие две команды, следующие четыре команды ; закрасят весь экран синим цветом, но из-за того, что мы переместили одну ; страницу, остается черный участок mov ecx,(320*200)/4 ; размер экрана в двойных словах mov edi,0A0000h ; линейный адрес начала видеопамяти mov eax,01010101h ; код синего цвета в VGA - 1 rep stosd ; вернуться в реальный режим db 0EAh dd offset RM_return dw SEL_16bitCS PM_seg ends

; Сегмент стека - используется как 16-битный stack_seg segment para stack "STACK" stack_start db 100h dup(?) stack_seg ends end start


Содержание раздела