Devotes - Предисловие
Уважаеме посетители, Сайт переезжает на другой хост http://www.devote.ru/
   Поиск по сайту:  
» Главная
Книги
Музыка
Download
Форум
[Назад] [Далее]

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

Линейный адрес, который формируется процессором из логического адреса, соответствует адресу из линейного непрерывного пространства памяти. В обычном режиме в это пространство могут попадать области памяти, в которые нежелательно разрешать запись, — системные таблицы и процедуры, ПЗУ 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 (буфер с ассоциативной выборкой), так что, если к странице обращались не очень давно, процессор определит ее физический адрес сразу.

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

Процессоры 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

Hosted by uCoz