Сегментная адресация памяти доклад

Обновлено: 14.05.2024

Перед изучением команд и регистров процессора 8086, очень важно понять, как он получает доступ к оперативной памяти, чтобы записывать в нее значения и читать их от туда.
Почему именно процессор 8086? Просто потому, что режим совместимости с командами этого процессора есть во всех старших моделях. И начинать изучать язык ассемблера проще с этого процессора.
Процессор 8086, мог работать только в одном режиме адресации памяти. Все следующие модели, начиная с процессора 80286, сохранили режим совместимости с 8086. Этот режим получил название реального режима ( Real Address Mode ), или R-режима.

Итак, ближе к делу.

Где Segment – адрес сегмента, а Offset – смещение от начала этого сегмента.

Но постойте! Два 16-разрядных регистра дают 32 разряда. Как же из этого получается 20 битный адрес? Давайте разбираться где тут собака порылась.

Для определения начала сегментов памяти процессор 8086 использует четыре 16-битных сегментных регистра (CS, DS, SS, ES). Смещение внутри сегмента выбирается из регистров-указателей SP, BP, SI, DI или регистра IP (указателя команд - Instructions Pointer). Для получения 20-битного физического адреса, процессор размещает на адресной шине значение сегментного регистра и сдвигает его влево на четыре бита, заполняя младшие четыре бита адресной шины нулями (умножение на десятичное 16 или шестнадцатеричное 10 ), затем к этому значению прибавляется смещение и адрес сформирован.

Исходя из этого получается что границы сегментов (16-битное значение + 4 нулевых бита ) располагаются через каждые 16 байт физических адресов. 4 битами можно адресовать 16 (байт) ячеек памяти, каждая из которых как мы помним содержит один байт . Каждый из этих 16-байтовых фрагментов называется параграфом . 16-разрядные сегментные регистры могут адресовать 65536 (2 16 ) параграфов (границ сегментов). А параграф, как уже говорилось, это 16 байт. 65536(параграфов) умножаем 16(байт) получаем 1048576 байт или округленно 1 Мбайт. Хотя и тут не все гладко :). Здесь порылась вторая собака. Откапывать ее будет чуть позже.

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

-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13DF ES=13DF SS=13DF CS=13DF IP=0100 NV UP EI PL NZ NA PO NC
13DF:0100 0000 ADD [BX+SI],AL DS:0000=CD
-d cs:100
13DF:0100 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .
13DF:0110 00 00 00 00 00 00 00 00-00 00 00 00 34 00 CE 13 . 4.

Например, в сегментном регистре (CS -выделен ярким желтым цветом) хранится значение 13DFh, при умножении его на 10h получаем 13DF0h. Стоит обратить внимание, что младшая шестнадцатеричная цифра в адресе каждого сегмента всегда равна 0. То есть адрес любого сегмента всегда кратен 16 десятичному (10h). Поскольку последняя цифра в адресе сегмента всегда равна 0, то ее можно не хранить. В действительности 8086 вместо умножения на 16 использовал содержимое регистра так, как если бы оно имело четыре дополнительных нулевых бита (см. картинку).

Максимальный размер сегмента определяется теми же 16 битами регистра, в котором хранится смещение. Следовательно, максимальный размер сегмента может быть 65536 байт (2 16 ). Минимальный – 16 байт (размер параграфа). Таким образом, сегменты – это виртуальные умозрительные части с максимальным объемом 64 Кбайт каждая.

Вместе со второй собакой мы откопали и большую свинью, которую, сами того не ведая, подложили разработчики процессора 8086 и сегментной адресации памяти.

Факты о "свинье":
* Нет никаких препятствий для обращения к физически не существующей памяти.
* При обращении к несуществующей памяти результат непредсказуем (все зависит от разработчика материнской платы и другого аппаратного обеспечения компьютера).
* Программа может обращаться к любому сегменту как для считывания, так и для записи данных и команд.

И еще немного правды о сегментах:
* Сегменты физически не выделены в памяти. Сегменты - это логические окна, через которые программы просматривают области памяти удобными, в 64 Кбайт порциями.
* Размеры сегментов могут изменятся от 16 байт до 64 Кбайт (65536 байт).
* Сегменты не обязательно в памяти располагаются один за другим. Хотя такое бывает достаточно часто.
* Сегменты могут перекрываться один другим; поэтому один и тот же физический байт памяти может иметь различные логические адреса , определяемые разными, но при этом эквивалентными парами сегмент-смещение. Например, пары логических адресов 0000:0010 и 0001:0000 указывают на один и тот же физический адрес ячейки памяти - 0010h.
* Назначением базовых адресов сегментов занимается операционная система, а внутри каждого сегмента адреса формируются программой.
* сегментная организация обеспечивает создание позиционно – независимых или динамически перемещаемых в памяти программ.

Рассмотрим пример, когда программа использует одно адресное пространство.

программа использует одно адресное пространство

Недостатки такой системы:

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

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

Рассмотрим то же пример с использованием сегментов:

Каждый сегмент может расти или уменьшаться независимо от других.

Сегмент - это логический объект.

В этом случае адрес имеет две части:

адрес в сегменте

Сегменты не мешают друг другу.

Начальный адрес процедуры всегда начинается с (n,0). Что упрощает программирование.

Облегчает совместное использование процедур и данных.

Раздельная защита каждого сегмента (чтение, запись).

8.2 Реализация сегментации

Если страницы имеют фиксированный размер, то сегменты нет.

У сегментов так же, как и у страниц, существует проблема фрагментации.

Т.к. памяти часто не хватает, стали использовать страничную организацию сегментов. При которой в памяти может находиться только часть сегмента.

8.2.1 Сегментация с использованием страниц: MULTICS

В одной из первых, где была применена страничная сегментация, была система MULTICS .

Каждая программа обеспечивалась до 2^18 сегментов (более 250 000), каждый из которых мог быть до 65 536 (36-разрядных) слов длиной.

Таблица сегментов - хранит дескриптор для каждого сегмента. У каждой программы своя таблица.

Т.к. записей в таблице более 250 000, она сама разбита на страницы.

Сама таблица является отдельным сегментом.

Сегмент с таблицей дескрипторов указывающих на таблицы страниц для каждого сегмента

Нормальный размер страницы равен 1024 словам. Если сегмент меньше 1024, то он либо не разбит на страницы, либо разбит на страницы по 64 слова.

Когда происходит обращение к памяти, выполняется следующий алгоритм:

По номеру сегмента находится дескриптор сегмента.

Проверяется, находиться ли таблица страницы в памяти. Если в памяти, определяется ее расположение. Если нет, вызывается сегментное прерывание.

Проверяется, находиться ли страница в памяти. Если в памяти, определяется ее расположение в памяти. Если нет в памяти, вызывается страничное прерывание.

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

Происходит запись или чтение.

Преобразование адреса в системе MULTICS

Так как такой алгоритм будет работать достаточно медленно. Аппаратура системы MULTICS содержит высокоскоростной буфер быстрого преобразования адреса (TLB) размером в 16 слов. Адреса 16 наиболее часто использующихся страниц хранятся в буфере.

8.2.2 Сегментация с использованием страниц: Intel Pentium

Каждая программа обеспечивается до 16К сегментов, каждый из которых может быть до 1 млдр 36-разрядных слов длиной.

Основа виртуальной памяти системы Pentium состоит из двух таблиц:

Локальная таблица дескрипторов LDT (Local Descriptor Table) - есть у каждой программы, и описывает сегменты программы.

Глобальная таблица дескрипторов GDT (Global Descriptor Table) - одна для всех программ, и описывает системные сегменты (включая саму ОС).

Каждый селектор (указывает на дескриптор) представляет собой 16-разрядный номер.

Селектор в системе Pentium

13 битов определяют номер записи в таблице дескрипторов, поэтому эти таблицы ограничены, каждая содержит 8К (2^13) сегментных дескрипторов.

1 бит указывает тип используемой таблицы дескрипторов LDT или GDT.

Уровни привилегированности в системе Pentium

Уровни привилегированности запрещают выполняемому коду обратиться к более низкому уровню.

С учетом максимального размера сегмента - 4 Гбайта - каждая задача, при чисто сегментной организации виртуальной памяти, работает в виртуальном адресном пространстве в 64 Тбайта (4 Гбайта * 16К, где 16К=8К*2 т.к. LDT и GDT).

Дескриптор программного (не данных) сегмента в системе Pentium (всего 8 байт (64 бита)).

База (Base) - базовый адрес сегмента (32-бита), разделен на три части из-за совместимости с i286, в котором это поле имеет только 24 бита.

Размер (Limit) - размер сегмента (20 бит), разнесен на две части.

Если размер сегмента указан в страницах, он может достигать 2^32 байтов (2^20 * 4Кбайт (2^12) (размер страницы в Pentium)).

Алгоритм получение физического адреса:

Селектор загружается в регистр (для сегмента команд в CS, для сегмента данных в DS).

Определяется глобальный или локальный сегмент (LDT или GDT).

Дескриптор извлекается из LDT или GDT, и сохраняется в микропрограммных регистрах.

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

Система Pentium прибавляет базовый адрес к смещению, и получает линейный адрес,
- если страничная организация памяти не используется, то он является физическим адресом (адрес получен),
- если страничная организация памяти используется, то он является виртуальным адресом.

В случае, если используется страничная организация памяти, линейный адрес переводится в физический с помощью таблицы страниц.

Преобразование пары (селектора, смещение) в физический адрес

При 32-разрядном (2^32=4Гбайт) адресе и 4Кбатной странице, сегмент может содержать 1 млн страниц (4Гбайт/4Кбайта). Поэтому используется двухуровневое отображение (создана таблица (страничный каталог) содержащая список из 1024 таблиц страниц), благодаря чему можно снизить количество записей в таблице страниц до 1024.

В этом случае сегмент в 4 Мбайта (1024 записи по 4 Кбайта страницы), будет иметь страничный каталог только с одной записью (и 1024 в таблице страниц), вместо 1 млн в одной таблице.

Отображение линейного адреса на физический адрес

8.3 Особенности реализации в UNIX

В LUNIX системе на 32-разрядной машине каждый процесс получает 3Гбайта виртуального пространства для себя, и 1Гбайт для страничных таблиц и других данных ядра.

На компьютерах Pentium, используется двухуровневые таблицы страниц, и размер страниц фиксирован 4Кбайта

На компьютерах Alpha, используется трехуровневые таблицы страниц, и размер страниц фиксирован 8Кбайт

Учебный курс. Часть 31. Сегментная адресация

Автор: xrnd | Рубрика: Учебный курс | 14-04-2011 | Распечатать запись

До этой части учебного курса мы создавали только COM-программы, в которых один и тот же сегмент использовался для кода, данных и стека. Однако, для дальнейшего изложения необходимо подробнее разобраться с сегментной адресацией.

Формирование адреса в реальном режиме

Основная идея сегментной адресации в том, что адрес состоит из двух частей — сегментной и смещения. Обычно их записывают через двоеточие (например 0100:0500). Линейный адрес любой ячейки памяти получается в результате сложения смещения и сегментной части, сдвинутой на 4 бита влево.


Начало сегмента всегда выровнено на границу параграфа (адрес кратен 16 байтам). Максимальный размер сегмента равен 2 16 = 64 КБайта. А всего можно адресовать 2 20 = 1 МБайт памяти. Конечно, сейчас такой объем памяти кажется смешным, но раньше это было очень много 🙂

Одна из особенностей сегментной адресации — неоднозначность представления адреса. Допустим, требуется обратиться к ячейке памяти по адресу 00400. Этот адрес может быть представлен как 0000:0400, 0040:0000, 0020:0200 и так далее.

Загруженная в память программа может одновременно работать с четырьмя сегментами. Сегменты могут перекрываться или даже совпадать, как это было в случае с COM-программой.


Создание DOS EXE

Возможности сегментной адресации полностью реализуются в исполняемом файле DOS EXE. Не путайте этот формат с исполняемым файлом Windows (PE EXE)! Расширение такое же, но файл имеет совершенно другую структуру.

format MZ ;Исполняемый файл DOS EXE (MZ EXE) entry code_seg:start ;Точка входа stack 200h ;Размер стека ;---------------------------------------------------------------------- segment data_seg ;Cегмент данных hello db 'Hello, asmworld!$' ;Строка ;---------------------------------------------------------------------- segment code_seg ;Сегмент кода start: ;Отсюда начинается выполнение программы mov ax,data_seg ;\ mov ds,ax ;/ Инициализация регистра DS mov ah,09h ;\ mov dx,hello ; > Вывод строки int 21h ;/ mov ax,4C00h ;\ int 21h ;/ Завершение программы

В первой строке после директивы format нужно поставить MZ, чтобы FASM сгенерировал нужный нам файл.

Во второй строке указывается точка входа, то есть метка, с которой начинается выполнение программы. Имя метки дополняется названием сегмента, в котором она находится.

После директивы stack можно указать требуемый размер сегмента стека в байтах (по умолчанию используется 4096). Дальше файл состоит из сегментов, которые объявляются с помощью директивы segment. После директивы записывается название сегмента.

В моём примере файл состоит из двух сегментов. В первом находятся данные (а точнее строка), во втором — код. Выполнение программы начинается с метки start в сегменте кода. После запуска необходимо инициализировать регистр ds, чтобы выбрать нужный сегмент данных. Для этого используются 2 команды, так как невозможно напрямую записать значение в сегментный регистр (нет команды MOV ds,значение).

Работу программы с сегментами можно увидеть в отладчике. Обратите внимание, что cs, ds, es и ss имеют разные значения:


Префиксы переопределения сегментов

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

Особенность синтаксиса FASM в том, что префикс пишется внутри квадратных скобок (так как по смыслу является частью адреса):

format MZ ;Исполняемый файл DOS EXE (MZ EXE) entry code_seg:start ;Точка входа stack 200h ;Размер стека ;---------------------------------------------------------------------- segment data_seg ;Cегмент данных hello db 'Hello, asmworld!$' ;Строка ;---------------------------------------------------------------------- segment code_seg ;Сегмент кода start: ;Отсюда начинается выполнение программы mov ax,data_seg ;\ mov ds,ax ;/ Инициализация регистра DS mov ah,09h ;\ mov dx,hello ; > Вывод строки int 21h ;/ mov ax,eseg ;\ mov es,ax ;/ Инициализация регистра ES mov al,[cs:start] ;Чтение байта, с которого начинается код cmp al,0xB8 jnz exit mov word[es:0000h],1234h ;Запись значения в сегмент eseg exit: mov ax,4C00h ;\ int 21h ;/ Завершение программы ;---------------------------------------------------------------------- segment eseg rw 1 ;Зарезервировать 1 слово

Дальние переходы, вызовы процедур и возвраты

Дальними (far) называются переходы в другой сегмент кода. При их выполнении меняется содержимое регистра cs. Они могут только безусловными. Ближние (near) переходы осуществляются в пределах одного сегмента. Аналогично есть дальние и ближние вызовы процедур, а также дальние и ближние возвраты.

Команда дальнего вызова процедуры сохраняет в стек не только ip, но и cs, чтобы можно было вернуться в текущий сегмент кода. Команда RET является синонимом ближнего возврата RETN. Дальний возврат осуществляется командой RETF. Она восстанавливает из стека регистры ip и cs.

Для наглядности пример:

format MZ ;Исполняемый файл DOS EXE (MZ EXE) entry seg1:start ;Точка входа ;------------------------------------------------------------------------- segment seg1 ;Сегмент первый hello db 'Hello, asmworld!$' ;Строка start: ;Отсюда начинается выполнение программы push cs ;\ pop ds ;/ Инициализация регистра DS jmp seg2:do_it exit: mov ah,08h int 21h mov ax,4C00h ;\ int 21h ;/ Завершение программы ;------------------------------------------------------------------------- segment seg2 ;Сегмент второй do_it: mov dx,hello ;DX = СМЕЩЕНИЕ строки в seg1 call seg3:print_str ;Дальний вызов (cs,ip в стек) jmp seg1:exit ;Дальний переход ;------------------------------------------------------------------------- segment seg3 ; Дальняя процедура для вывода строки (ds:dx = адрес строки) print_str: mov ah,09h int 21h retf ;Дальний возврат (восстанавливает ip,cs)

Программа состоит из трёх сегментов. Сначала выполняется переход во второй, затем вызов процедуры в третьем сегменте. Кстати, сегмент может содержать код и данные вместе — я поместил строку в начало первого сегмента.

Упражнение

Напишите программу, которая сравнивает две переменные и выполняет переход в другой сегмент в зависимости от результата сравнения. Если меньше, переход в сегмент 1. Если больше — в сегмент 2. Иначе в сегмент 3.

Комментарии:

(JXX — conditions) не хотят прыгать в соседние сегменты … почему ?
Нужно бы написать статью по условным дерективам …. 😉
или я просто неправильно написал или недописал

format MZ
entry code_seg:Start

macro dosfn_ah [n_func]
mov ah, n_func
int 21h
>

segment data_seg
var1 db ‘7’
less_str db 0ah, 0dh, ‘less’, 0dh, 0ah, ‘$’
great_str db 0ah, 0dh, ‘great’, 0dh, 0ah, ‘$’
segment code_seg
Start:

push data_seg
pop ds

dosfn_ah 0x1 ; input char
cmp al, [var1]
jz .equ ; ‘jz’ neho4et prigat za predeli segmenta ((
js .less ; NUJNI uslovnii derectivi
jmp isgreat_seg:0
.equ:
jmp equ_seg:eStart
.less:
jmp less_seg:0

.Exit:
dosfn_ah 0x8, 0x4c

;——————
segment isgreat_seg
mov dx, great_str
dosfn_ah 0x9
jmp code_seg:Start.Exit

;—————
segment less_seg
mov dx, less_str
dosfn_ah 0x9
jmp code_seg:Start.Exit

;—————
segment equ_seg
text db 0dh, 0ah, ‘equal!’, 0dh, 0ah, ‘$’
eStart:
push ds
push equ_seg
pop ds

mov dx, text
dosfn_ah 0x9

pop ds
jmp code_seg:Start.Exit

спасибос за очередной урок!

(JXX – conditions) не хотят прыгать в соседние сегменты … почему ?

Нет такой машинной команды. Для JMP есть ближний и дальний вариант команды, а для JXX — только ближний. В этом и прикол упражнения.

Условные директивы — это как аналог условных операторов if /else в других языках?
Лично я их не очень люблю — не всегда эффективный код генерируют.
Но написать можно. Если не ошибаюсь, в FASMе они реализованы в виде макросов.

Теперь о твоей программе. Всё правильно, суть ты понял.
Макрос dosfn_ah довольно удачный и используется уместно.
Вот только непонятно, с каким кодом завершится процесс.
При вызове функции DOS 4Ch в AL должен быть код 0, если это нормальное завершение процесса.

Сегмент представляет из себя участок физической памяти определенного размера(не фиксированного, это важно!). Соответственно вся память делится на сегменты, и любой процесс для того, чтобы обратиться к участку памяти должен знать два числа: номер сегмента и смещение от начала сегмента. Вместе они образуют логический(его еще называют сегментный) адрес, который затем будет преобразован уже в настоящий физический адрес в оперативной памяти.

Селектор сегмента

Первое из двух чисел в логическом адресе называется селектором, он 16-ти разрядный, среди которых старшие 13 бит являются индексом(порядковым номером) сегмента в таблице дескрипторов. Остальные три бита делятся следующим образом: 1 бит называется TI и указывает на тип дескрипторной таблицы(0 — глобальная, 1 — локальная), еще два бита отданы под RPL — запрашиваемый уровень привилегий.


При обращении к нужному дескриптору RPL будет сравниваться с DPL этого сегмента. Как вы уже могли догадаться, DPL(Descriptor privilege level) это уровень привилегий дескриптора.

Кстати, о доступе. Благодаря делению памяти на сегменты появилась возможность разграничивать права доступа и пресекать подлые попытки переписать чужую память, это стало настоящим прорывом. Всего существует 4 уровня привилегий: 0-2 соответствуют супервизору, при этом на нулевом уровне доступны самые привилегированные действия, 3 — уровень пользователя(пользовательских программ).

Таблицы дескрипторов

Глобальная таблица дескрипторов — GDT

Чуть более подробно об этой таблице. Все сегменты хранятся в определенной структуре, называемой GDT — Global Descriptor Table. Из названия видно, что она представляет из себя простую таблицу. Хранится в оперативной памяти всегда, подгружается при запуске операционной системы, на ее начало указывает специальный регистр GDTR. Кстати, это один из немногих регистров процессора, который хранит в себе физический адрес. И он же хранит размер таблицы, таким образом мы и операционная система всегда можем иметь доступ к нашим сегментам.

Из размерности поля индекса в селекторе можно посчитать максимальное возможное количество дескрипторов в таблице — 213 = 8192 дескриптора.

Размер дескриптора равен 8 байтам или 32 битам. Вот как выглядит такая запись, называемая дескриптором.


Базовый адрес есть ни что иное как физический адрес сегмента в памяти. Об остальных битах подробно расписано на википедии, я не стану повторяться.

Локальная таблица дескрипторов — LDT

Кроме GDT еще существует LDT — Local Descriptor Table — которая отвечает за сегменты каждого отдельного процесса. Ссылки на все LDT содержатся в GDT, но в любой локальной таблице не может быть ссылок на другую локальную. А во всем остальном локальные таблицы не отличаются от глобальной.

Трансляция адресов в сегментной модели памяти

И последнее, поговорим конкретнее о преобразовании логического адреса в физический. У нас есть такая штука, как селектор:смещение, где смещение это просто линейный адрес внутри самой программы(например адрес переменной). И еще у нас есть базовый адрес сегмента, полученный из глобальной таблицы дескрипторов по индексу в селекторе.

  • Первым делом сравниваются RPL селектора и DPL сегмента, если запрашиваемый уровень привилегий меньше или равен уровню доступа сегмента, то доступ разрешен.
  • Теперь мы должны взять базовый адрес сегмента и прибавить к нему смещение из логического адреса.
  • Проверить, что полученный физический адрес не выходит за границы сегмента. Для этого существует поле limit в дескрипторе.
  • Если все условия соблюдены, мы получаем доступ к ячейке памяти по физическому адресу(база сегмента + смещение).


Заключение

Читайте также: