Относительный адрес означает расстояние между двумя местоположениями или адресами (которое может быть логическим, линейным / виртуальным или физическим, что на данный момент не важно).
Например, вызов x86 и переходинструкции имеют форму, которая определяет расстояние (отсчитываемое от байта после окончания инструкции call / jump) до вызова / jump.Это расстояние просто добавляется в регистр указателя инструкций ([R | E] IP), и это место, откуда будет приходить следующая инструкция (опять же, я игнорирую логические, ..., пока физические).
Если ваша программа содержит подпрограмму и вызывает ее с помощью такой инструкции, не имеет значения, где находится программа в памяти, поскольку расстояние между двумя местоположениями целого остается неизменным (все станет более сложным, если целоеПрограмма состоит из нескольких движущихся частей, включая одну или несколько библиотек, но давайте не будем идти туда).
Теперь предположим, что ваша программа имеет глобальную переменную и должна ее прочитать.Если есть инструкция чтения из памяти, аналогичная инструкции вызова, описанной выше, вы можете снова использовать расстояние от указателя инструкции до местоположения переменной.До 64-битных процессоров x86 не было такой инструкции / механизма для доступа к данным, только IP-адреса могли относиться только к вызовам и переходам.
В отсутствие такого механизма адресации данных относительно IP-адреса вам необходимознать фактический адрес переменной, который вы не будете знать, пока программа не будет загружена в память для выполнения.В этом случае выполняется то, что инструкция, которая читает переменную, изначально получает адрес переменной относительно IP (адрес инструкции, которая читает переменную) или просто запуск программы.И вот как программа хранится на диске, с относительным адресом внутри инструкции.После загрузки, но до того, как программа начинает выполнение, адрес переменной в инструкции, которая читает ее, корректируется таким образом, что она становится фактическим адресом, а не относительно чего-либо (IP или запуск программы).Чем дальше запуск программы от адреса 0, тем большую корректировку необходимо добавить к этому относительному адресу.
Понять идею?
А теперь что-то почти совершенно другое и не связанное ...
В контексте процессоров x86 существуют следующие типы адресов:
- Логический
- Линейный / виртуальный
- Физический
Если мы вернемся к 8086/8088 ... На самом деле, если мы вернемся еще дальше к 8080/8085, все адреса памяти будут 16-битными, они не будут переведеныCPU и представлены в памяти как есть, следовательно, они физические (здесь мы не говорим об инструкциях вызова / перехода по IP / PC).
16 бит позволяют 64 КБ памяти.8086/8088 расширил эти 16-битные адреса еще на 16 бит, чтобы адресовать более 64 КБ памяти, но он не просто расширил все регистры и адреса с 16 до 32 бит.Вместо этого он ввел специальные сегментные регистры , которые будут использоваться в паре с теми старыми 16-битными адресами 8080/8085.Таким образом, пара регистров, таких как DS
(сегментный регистр) и BX
(обычный регистр общего назначения), может адресовать память по адресу DS * 16 + BX
.Пара DS:BX
является логическим адресом, значение DS * 16 + BX
является физическим адресом.С помощью этой схемы мы можем получить доступ приблизительно к 1 МБ памяти (просто подключите 65535 для обоих регистров).
80286 немного изменил вышеприведенное, введя так называемый защищенный режим , в которомфизический адрес был рассчитан как segment_table[DS] + BX
(это позволило перейти от 1 МБ до 16 МБ), но идея осталась прежней.
Далее пришел по 80386 и расширил регистры до 32 бит и представил еще один уровеньКосвенная.Физический адрес был немного упрощен, page_tables[segment_table[DS] + EBX]
.
Пара DS:EBX
составляет логический адрес , это то, с чем программа манипулирует (например, в инструкции MOV EAX, DS:[EBX]
), это то, что она может наблюдать.
segment_table[DS] + EBX
составляет линейный / виртуальный адрес (который программа может не всегда знать, так как не может видеть segment_table[]
, таблицу, управляемую ОС).Если перевод страницы не включен, этот линейный / виртуальный адрес также равен окончательному физическому адресу.
При включенном переводе страницы физический адрес равен page_tables[segment_table[DS] + EBX]
.
Что еще нужно знать:
- логические адреса могут быть более сложными, например,
DS:[EAX + EBX * 2 + 3]
- ОС обычно настраивают
segment_table[]
таким образом, что segment_table[any segment register]=0
, эффективно удаляямеханизм сегментации вне изображения и заканчивается, например, физическим адресом = page_tables[EAX + EBX * 2 + 3]
.Хотя не совсем правильно говорить, что в такой конфигурации логические и линейные / виртуальные адреса совпадают (EAX + EBX * 2 + 3
), это определенно упрощает мышление.
Теперь, что делают эти сегмент и страницатаблицы имеют отношение к относительным адресам и перемещению, обсуждаемым в начале?Эти таблицы просто позволяют вам разместить вашу программу где-нибудь в физической памяти, часто очень прозрачно для самой программы.Ему не нужно знать, где он находится физически или включен ли перевод страниц.
Однако использование перевода страниц имеет определенные преимущества, но здесь это выходит за рамки.