Какие три устройства указывают адрес ячейки памяти

Обновлено: 04.07.2024

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

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

Каждая ячейка памяти имеет адрес, который используется для ее нахождения. Адреса - это числа, начиная с нуля для первой ячейки, увеличивающиеся по направлению к последней ячейке памяти. Поскольку адреса - это те же числа, компьютер может использовать арифметические операции для вычисления адресов памяти.

Архитектура каждого компьютера накладывает собственные ограничения на величину адресов. Наибольший возможный адрес определяет объем адресного пространства компьютера или то, какой объем памяти он может использовать. Обычно компьютер использует память меньшего объема, чем допускается его возможностями адресации. Если архитектура компьютера предусматривает наибольшее адресное пространство, это накладывает суровые ограничения на возможности такого компьютера. Адреса в 8088 имеют длину 20 бит, следовательно, процессор позволяет адресовать два в двадцатой степени байта или 1024 К.

3.5. Регистры процессора

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

Перечислим основные регистры и кратко определим их назначение.

Сегментные регистры (CS, DS, SS, ES).

Регистр сегмента кода (CS) содержит начальный адрес сегмента кода. Этот адрес плюс значение смещения в командном указателе (IP) определяет адрес команды, которая должна быть выбрана для выполнения.

Регистр сегмента данных (DS) содержит начальный адрес сегмента данных. Этот адрес плюс значение смещения, определенное в команде, указывают на конкретную ячейку в сегменте данных.

Регистр сегмента стека (SS) содержит начальный адрес сегмента стека.

Регистр ES. Некоторые операции над строками используют дополнительный сегментный регистр для управления адресацией памяти. В этом случае этот регистр связан с индексным регистром DI. Если необходимо использовать регистр ES, ассемблерная программа должна его инициализировать.

Регистры общего назначения (AX, BX, CX, DX). Особенность этих регистров состоит в том, что возможна адресация их как одного целого слова, так и однобайтовой части. Левый байт является старшей частью (High), а правый - младшей частью (Low) соответствующего регистра. Например, двухбайтовый регистр CX состоит из двух однобайтовых CH и CL, и ссылки на регистр возможны по любому из этих трех имен.

Разберем назначение каждого из регистров.

Регистр AX. Он является основным сумматором и применяется для всех операций ввода-вывода, некоторых операций над строками и некоторых арифметических операций. Например, команды умножения, деления и сдвига предполагают использование регистра AX. Некоторые команды генерируют более эффективный код, если они имеют ссылки на регистр AX.

Регистр BX. Он является базовым регистром. Это единственный регистр общего назначения, который может использоваться в качестве "индекса" для расширенной адресации. Также он используется при организации вычислений.

Регистр CX. Это счетчик, используемый для управления числом повторений циклов и для операций сдвига влево или вправо. Регистр CX используется также при вычислениях.

Регистр DX. Он является регистром данных и применяется для некоторых операций ввода-вывода и тех операций умножения и деления над большими числами, которые используют регистровую пару DX:AX.

Регистровые указатели (SP, BP). Они обеспечивают системе доступ к данным в сегменте стека. Реже используются в арифметических операциях.

Регистр SP. Указатель стека обеспечивает использование стека в памяти, позволяет временно хранить адреса и иногда данные. Этот регистр связан с регистром SS для адресации стека.

Регистр BP. Указатель базы облегчает доступ к параметрам (данным и адресам, переданным через стек).

Индексные регистры (SI, DI). Оба индексных регистра могут применяться для расширенной адресации и для использования в арифметических операциях.

Регистр SI. Этот регистр является индексом источника данных и применяется для некоторых операций над строками. В этом случае он адресует память в паре с регистром DS.

Регистр DI. Этот регистр является индексом назначения и применяется также для строковых операций. В данном случае он используется совместно с регистром ES.

Регистр командного указателя (IP). Он содержит смещение на команду, которая должна быть выполнена. Обычно этот регистр в программе не применяется, но он может изменять свое значение при использовании отладчика в процессе тестирования программы.

Флаговый регистр (иногда мы будем обозначать его через FLAGS). Девять из шестнадцати бит флагового регистра являются активными и определяют текущее состояние машины и результаты выполнения команд:

За последнюю неделю дважды объяснял людям как организована работа с памятью в х86, с целью чтобы не объяснять в третий раз написал эту статью.

И так, чтобы понять организацию памяти от вас потребуется знания некоторых базовых понятий, таких как регистры, стек и тд. Я по ходу попробую объяснить и это на пальцах, но очень кратко потому что это не тема для этой статьи. Итак начнем.

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

Так ладно, хватит о печальном, переходим к делу.
Рассмотрим адресное пространство программного режима 32 битного процессора (для 64 бит все по аналогии)
Адресное пространство этого режима будет состоять из 2^32 ячеек памяти пронумерованных от 0 и до 2^32-1.
Программист работает с этой памятью, если ему нужно определить переменную, он просто говорит ячейка памяти с адресом таким-то будет содержать такой-то тип данных, при этом сам програмист может и не знать какой номер у этой ячейки он просто напишет что-то вроде:
int data = 10;
компьютер поймет это так: нужно взять какую-то ячейку с номером стопицот и поместить в нее цело число 10. При том про адрес ячейки 18894 вы и не узнаете, он от вас будет скрыт.

Все бы хорошо, но возникает вопрос, а как компьютер ищет эту ячейку памяти, ведь память у нас может быть разная:
3 уровень кэша
2 уровень кэша
1 уровень кэша
основная память
жесткий диск

Это все разные памяти, но компьютер легко находит в какой из них лежит наша переменная int data.
Этот вопрос решается операционной системой совместно с процессором.
Вся дальнейшая статья будет посвящена разбору этого метода.

Архитектура х86 поддерживает стек.

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

push operand
помещает операнд в стек

pop operand
изымает из вершины стека значение и помещает его в свой операнд

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

Теперь кратко рассмотрим что такое регистры.
Это ячейки памяти в самом процессоре. Это самый быстрый и самый дорогой тип памяти, когда процессор совершает какие-то операции со значением или с памятью, он берет эти значения непосредственно из регистров.
В процессоре есть несколько наборов логик, каждая из которых имеет свои машинные коды и свои наборы регистров.
Basic program registers (Основные программные регистры) Эти регистры используются всеми программами с их помощью выполняется обработка целочисленных данных.
Floating Point Unit registers (FPU) Эти регистры работают с данными представленными в формате с плавающей точкой.
Еще есть MMX и XMM registers эти регистры используются тогда, когда вам надо выполнить одну инструкцию над большим количеством операндов.

Рассмотрим подробнее основные программные регистры. К ним относятся восемь 32 битных регистров общего назначения: EAX, EBX, ECX, EDX, EBP, ESI, EDI, ESP
Для того чтобы поместить в регистр данные, или для того чтобы изъять из регистра в ячейку памяти данные используется команда mov:

mov eax, 10
загружает число 10 в регистр eax.

mov data, ebx
копирует число, содержащееся в регистре ebx в ячейку памяти data.

Регистр ESP содержит адрес вершины стека.
Кроме регистров общего назначения, к основным программным регистрам относят шесть 16битных сегментных регистров: CS, DS, SS, ES, FS, GS, EFLAGS, EIP
EFLAGS показывает биты, так называемые флаги, которые отражают состояние процессора или характеризуют ход выполнения предыдущих команд.
В регистре EIP содержится адрес следующей команды, которая будет выполнятся процессором.
Я не буду расписывать регистры FPU, так как они нам не понадобятся. Итак наше небольшое отступление про регистры и стек закончилось переходим обратно к организации памяти.

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

Логический адрес --> Линейный (виртуальный)--> Физический

image


Все линейное адресное пространство разбито на сегменты. Адресное пространство каждого процесса имеет по крайней мере три сегмента:
Сегмент кода. (содержит команды из нашей программы, которые будут исполнятся.)
Сегмент данных. (Содержит данные, то бишь переменные)
Сегмент стека, про который я писал выше.

Линейный адрес вычисляется по формуле:
линейный адрес=Базовый адрес сегмента(на картинке это начало сегмента) + смещение
Сегмент кода

Базовый адрес сегмента кода берется из регистра CS. Значение смещения для сегмента кода берется из регистра EIP, в котором хранится адрес инструкции, после исполнения которой, значение EIP увеличивается на размер этой команды. Если команда занимает 4 байта, то значение EIP увеличивается на 4 байта и будет указывать уже на следующую инструкцию. Все это делается автоматически без участия программиста.
Сегментов кода может быть несколько в нашей памяти. В нашем случае он один.
Сегмент данных

Данные загружаются в регистры DS, ES, FS, GS
Это значит что сегментов данных может быть до 4х. На нашей картинке он один.
Смещение внутри сегмента данных задается как операнд команды. По дефолту используется сегмент на который указывает регистр DS. Для того чтобы войти в другой сегмент надо это непосредственно указать в команде префикса замены сегмента.
Сегмент стека

Используемый сегмент стека задается значением регистра SS.
Смещение внутри этого сегмента представлено регистром ESP, который указывает на вершину стека, как вы помните.
Сегменты в памяти могут друг друга перекрывать, мало того базовый адрес всех сегментов может совпадать например в нуле. Такой вырожденный случай называется линейным представлением памяти. В современных системах, память как правило так организована.

Теперь рассмотрим определение базовых адресов сегмента, я писал что они содержаться в регистрах SS, DS, CS, но это не совсем так, в них содержится некий 16 битный селектор, который указывает на некий дескриптор сегментов, в котором уже хранится необходимый адрес.

Так выглядит селектор, в тринадцати его битах содержится индекс дескриптора в таблице дескрипторов. Не хитро посчитать будет что 2^13 = 8192 это максимальное количество дескрипторов в таблице.
Вообще дескрипторных таблиц бывает два вида GDT и LDT Первая называется глобальная таблица дескрипторов, она в системе всегда только одна, ее начальный адрес, точнее адрес ее нулевого дескриптора хранится в 48 битном системном регистре GDTR. И с момента старта системы не меняется и в свопе не принимает участия.
А вот значения дескрипторов могут меняться. Если в селекторе бит TI равен нулю, тогда процессор просто идет в GDT ищет по индексу нужный дескриптор с помощью которого осуществляет доступ к этому сегменту.
Пока все просто было, но если TI равен 1 тогда это означает что использоваться будет LDT. Таблиц этих много, но использоваться в данный момент будет та селектор которой загружен в системный регистр LDTR, который в отличии от GDTR может меняться.
Индекс селектора указывает на дескриптор, который указывает уже не на базовый адрес сегмента, а на память в котором хранится локальная таблица дескрипторов, точнее ее нулевой элемент. Ну а дальше все так же как и с GDT. Таким образом во время работы локальные таблицы могут создаваться и уничтожаться по мере необходимости. LDT не могут содержать дескрипторы на другие LDT.
Итак мы знаем как процессор добирается до дескриптора, а что содержится в этом дескрипторе посмотрим на картинке:
Дескрипторы состоит из 8 байт.
Биты с 15-39 и 56-63 содержат линейный базовый адрес описываемым данным дескриптором сегмента. Напомню нашу формулу для нахождения линейного адреса:

линейный адрес = базовый адрес + смещение
[база; база+предел)

В зависимости от 55 G-бита(гранулярити), предел может измеряться в байтах при нулевом значении бита и тогда максимальный предел составит 1 мб, или в значении 1, предел измеряется страницами, каждая из которых равна 4кб. и максимальный размер такого сегмента будет 4Гб.
Для сегмента стека предел будет в интервале:
(база+предел; вершина]

Кстати интересно почему база и предел так рвано располагаются в дескрипторе. Дело в том что процессоры х86 развивались эволюционно и во времена 286х дескрипторы были по 8 бит всего, при этом старшие 2 байта были зарезервированы, ну а в последующих моделях процессоров с увеличением разрядности дескрипторы тоже выросли, но для сохранения обратной совместимости пришлось оставить структуру как есть.
Значение адреса «вершина» зависит от 54го D бита, если он равен 0, тогда вершина равна 0xFFF(64кб-1), если D бит равен 1, тогда вершина равна 0xFFFFFFFF (4Гб-1)
С 41-43 бит кодируется тип сегмента.
000 — сегмент данных, только считывание
001 — сегмент данных, считывание и запись
010 — сегмент стека, только считывание
011 — сегмент стека, считывание и запись
100 — сегмент кода, только выполнение
101- сегмент кода, считывание и выполнение
110 — подчиненный сегмент кода, только выполнение
111 — подчиненный сегмент кода, только выполнение и считывание

44 S бит если равен 1 тогда дескриптор описывает реальный сегмент оперативной памяти, иначе значение S бита равно 0.

Самым важным битом является 47-й P бит присутствия. Если бит равен 1 значит, что сегмент или локальная таблица дескрипторов загружена в оперативку, если этот бит равен 0, тогда это означает что данного сегмента в оперативке нет, он находится на жестком диске, случается прерывание, особый случай работы процессора запускается обработчик особого случая, который загружает нужный сегмент с жесткого диска в память, если P бит равен 0, тогда все поля дескриптора теряют смысл, и становятся свободными для сохранения в них служебной информации. После завершения работы обработчика, P бит устанавливается в значение 1, и производится повторное обращение к дескриптору, сегмент которого находится уже в памяти.

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

Это шестая статья цикла о микроконтроллерах. В предыдущей статье " Микроконтроллеры для начинающих. Часть 5. Архитектура. Адресные пространства и память " мы узнали, как процессор связан с памятью и что такое адресные пространства. Сегодня мы рассмотрим как устроена работа с памятью внутри этих адресных пространств. Это обязательная к изучению тема, имеющая непосредственное отношение к практике использования микроконтроллеров.

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

Линейная адресация памяти

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

Пример линейной адресации памяти не зависящий от физической реализации модуля памяти. Иллюстрация моя Пример линейной адресации памяти не зависящий от физической реализации модуля памяти. Иллюстрация моя

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

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

Мы можем пройтись по всем ячейкам просто последовательно перебирая их адреса, без дополнительных ухищрений. Это простейший способ организации памяти (с точки зрения процессора). И самый удобный, с точки зрения программиста. Но у него есть и недостатки.

Ограничения линейной адресации

Так адрес ячейки памяти может оказаться слишком длинным. Так адресация памяти из 256 ячеек требует адреса диной 1 байт (8 бит). А для 64К ячеек (65536 ячеек) нужно уже 16 бит, или 2 байта. Как я уже говорил в " Микроконтроллеры для начинающих. Часть 2. Процессор микроконтроллера ", каждая машинная инструкция, за некоторым исключением, включает в себя один или несколько операндов. Если операнд представляет собой адрес ячейки памяти, а у нас адрес занимает 2 байта, то машинная инструкция не может занимать менее 3 байт (нужно же и код команды где то разместить).

Размер памяти программ в микроконтроллерах ограничен, поэтому такое расточительство недопустимо. Производители микроконтроллеров решают проблему по разному.

Во первых, сразу можно заметить, что у нас разные адресные пространства программ и данных, так как используется Гарвардская архитектура (о чем я писал в предыдущей статье). Объем памяти данных часто меньше, чем объем памяти программ. А значит, в инструкция работающих с данными мы можем уменьшить размер поля адреса операнда. А в инструкциях работающих с адресами инструкция (инструкции перехода) выделить под адрес больше места.

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

Страничная (банковая) адресация памяти

Если у нас инструкция содержит не полный адрес, а лишь его часть, то как мы сможем задать полный адрес? Очень просто, достаточно разбить память на блоки меньшей длины. И указывать где то номер блока, с которым мы работаем. А в самой машинной инструкции потребуется только указать номер ячейки в блоке, что требует меньше места.

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

Блоки, на которые разбивается единое пространство памяти называют страницами. Думаю, многим это понятие знакомо. Как минимум, по процессорам 80х86. Существует и еще одно название - банк. Иногда, блоки памяти программ называют страницами, а блоки памяти данных банками.

Пример показывающий принцип страничной адресации памяти объемом 2К ячеек разбитой на 4 страницы по 256 ячеек каждая. Иллюстрация моя Пример показывающий принцип страничной адресации памяти объемом 2К ячеек разбитой на 4 страницы по 256 ячеек каждая. Иллюстрация моя

Что то не то с этой иллюстрацией? Вы правы! Здесь показан сам принцип деления адреса на две части, но не понятно, в чем же тут выгода. Действительно, ведь длина адреса, если судить по иллюстрации не изменилась. Дело в том, что полный адрес физической ячейки памяти на самом деле формируется так

Пример страничной организации памяти и формирования физического адреса из логического и номера страницы хранящейся в регистре PAGE REG. Иллюстрация моя Пример страничной организации памяти и формирования физического адреса из логического и номера страницы хранящейся в регистре PAGE REG. Иллюстрация моя

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

Мы сэкономили немного памяти программ, но получили усложнение в работе с памятью. Теперь, последовательно увеличивая адрес ячейки памяти, мы можем перемещаться только внутри страницы. Так? Почти.

Для памяти данных это, обычно, действительно так. А вот для памяти программ, обычно, последовательный перебор адресов (последовательное выполнение инструкций) позволяет игнорировать границы страниц.

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

Пара слов о реальных микроконтроллерах

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

В микроконтроллерах STM8 адресация линейная. Причем, как я уже упоминал в прошлый раз, они имеют логическое единое адресное пространство. Поле адреса в инструкциях этих микроконтроллеров имеет переменную длину, но страничная адресация, в классическом виде, там не используется. А вот как быть с недостающей частью адреса я расскажу когда буду рассматривать эти микроконтроллеры. Но Flash память программ в этих микроконтроллерах организована в виде страниц, состоящих из блоков, состоящих из отдельных байт. И это нужно учитывать при записи в память программ.

В микроконтроллерах AVR Atmel (Microchip!) используется линейная адресация, но там свои тонкости. А запись с память программ тоже выполняется блоками.

Заключение

Мы почти (но только почти!) заканчиваем разбираться с организацией памяти в микроконтроллерах. В следующей статье я расскажу о методах адресации операндов в машинных инструкциях. После чего будут статьи с более подробным описанием памяти в различных реальных микроконтроллерах. Ну а там и до системы машинных инструкций доберемся. А дальше будет попроще.

Содержание

Типы адресов памяти

Физические адреса

Память цифрового компьютера (или основная память) состоит из множества ячеек памяти, каждая из которых имеет физический адрес. [Источник 1] - код, который центральный процессор (или другое устройство) может использовать для доступа к ней. Как правило, только системное программное обеспечение, то есть BIOS [Источник 2] , операционные системы, и некоторые специализированные вспомогательные программы (например, тестеры памяти), обращаются к физической памяти с использованием операторов машинного кода и регистров процессора, Инструктируя CPU направлять аппаратное устройство, называемое контроллером памяти, использовать шину памяти или системную шину или отдельные управляющие, адресные и информационные шины для выполнения команд программы. Шина контроллеров памяти состоит из нескольких параллельных линий, каждая из которых представлена двоичной цифрой (бит). Ширина шины и, следовательно, количество адресуемых единиц хранения и количество битов в каждой единице варьируется среди компьютеров. Физический адрес - это конечный результат всех преобразований других типов адресов, перечисленных далее. На нём кончается работа внутри центрального процессора по преобразованию адресов.

Эффективные адреса

Эффективный адрес — это начало пути вычисления физического адреса. Он задаётся в аргументах индивидуальной машинной инструкции, и вычисляется из значений регистров, смещений и масштабирующих коэффициентов, заданных в ней явно или неявно.

Логические адреса

Без знания номера и параметров сегмента, в котором указан эффективный адрес, последний бесполезен. Сам сегмент выбирается ещё одним числом, именуемым селектором. Пара чисел, записываемая как selector:offset, получила имя логический адрес. Так как активные селекторы хранятся в группе специальных регистров, чаще всего вместо первого числа в паре записывается имя регистра, например, ds:0x11223344 [Источник 3] . В старых компьютерах логические и физические адреса были согласованы, но с момента появления виртуальной памяти у большинства прикладных программ нет информации о физических адресах. Скорее, они адресуют логические адреса [Источник 4] или виртуальные адреса, используя блок управления памятью компьютера и отображение памяти операционной системы.

Линейные адреса

Эффективный адрес — это смещение от начала сегмента — его базы. Если сложить базу и эффективный адрес, то получим число, называемое линейным адресом:

lin_addr = segment.base + eff_addr

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

Виртуальные адреса

В литературе и в документации других архитектур встречается ещё один термин — виртуальный адрес. Он не используется в документации Intel на IA-32, однако встречается, например, в описании Intel® Itanium, в котором сегментация не используется. Можно смело считать, что для IA-32 виртуальный == линейный. В советской литературе по вычислительной технике этот вид адресов также именовался математическим.

Единица измерения адреса

Некоторые старые компьютеры (десятичные компьютеры) были десятизначными с цифровой адресацией. Например, каждый адрес в магнитной памяти IBM 1620 идентифицировал одну шестиразрядную двоично-кодированную десятичную цифру, состоящую из бита четности, бита флага и четырех числовых битов. В 1620 использовались пятизначные десятичные адреса, поэтому в теории максимально возможный адрес был 99,999. На практике CPU поддерживал 20000 ячеек памяти и мог добавить до двух дополнительных модулей внешней памяти, каждый из которых поддерживает 20 000 адресов, в общей сложности 60 000 (00000-59999).

Размер слова в зависимости от размера адреса

Размер слова является характеристикой для данной архитектуры компьютера. Он обозначает количество цифр, которое процессор может обрабатывать за один раз. Современные процессоры, включая встроенные системы, обычно имеют размер слова 8, 16, 24, 32 или 64 бита; Большинство современных компьютеров общего назначения используют 32 или 64 бита. В истории же использовалось много различных вариантов, включая 8, 9, 10, 12, 18, 24, 36, 39, 40, 48 и 60 бит.

Очень часто, когда речь идет о размере слова современного компьютера, также оценивается размер адресного пространства на этом компьютере. Например, компьютер, называемый «32-битным», также обычно разрешает 32-разрядные адреса памяти; 32-разрядный компьютер с байтовой адресацией может адресовать 2^32 = 4,294,967,296 байт памяти или 4 гибибайта (GB). Это позволяет эффективно хранить адрес памяти одним словом.

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

Содержание отдельной ячейки памяти

Каждая ячейка памяти на компьютере с хранимой программой хранит двоичное число или десятичное число некоторого типа. Эти числа определяются как данные или как команды, а их использование определяется командами, которые извлекают и взаимодействуют с ними. Некоторые «ранние» программисты сочетали команды и данные в словах как способ сэкономить память: «Манчестер-Марк-1» имел место в своих 40-битных словах для хранения нескольких бит данных - его процессор игнорировал небольшую секцию в середине слова - и это часто использовалось как эксплойт для хранения дополнительных данных. Самовоспроизводящиеся программы, такие как вирусы, иногда рассматривают себя как данные, а иногда как команды. Самовоспроизводящийся код в настоящее время устаревает, поскольку его тестирование и техническое обслуживание непропорционально сложно для экономии нескольких байт, а также он может выдавать неверные результаты из-за предположений компилятора или процессора относительно состояния машины, но все же он иногда используется намеренно, с большой осторожностью.

Адресное пространство в программировании приложений

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

  • Машинный код, в том числе:
    • Собственный код программы;
    • Совместно используемые библиотеки.
    • Инициализированные данные;
    • Неинициализированные (но выделенные) переменные;
    • Стек для переменных исполняемой программы;
    • Куча;
    • Совместно используемая память и отображенные в память файлы.

    Некоторые части адресного пространства могут вообще не отображаться.

    Схемы адресации

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

    Модели памяти

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

    Однако многие старые компьютеры не поддерживали плоскую модель памяти - в частности, аппараты архитектуры Harvard вынуждали память с командами полностью отделяться от памяти с данными. Многие современные DSP(digital signal processor) (такие как Motorola 56000) имеют три отдельные области хранения - хранение программ, хранение коэффициентов и хранение данных. Некоторые часто используемые команды извлекаются из всех трех областей одновременно - меньшее количество областей хранения (даже если бы были одинаковые общие байты памяти) приводило бы к замедлению выполнения этих команд.

    Модели памяти в х86 архитектуре

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

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