Адресное пространство это что

Адресное пространство (информатика)

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

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

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

Связанные понятия

Упоминания в литературе

Связанные понятия (продолжение)

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

Источник

Организация памяти процесса

Адресное пространство это что. b27d87ea315ccc5f2ee945df3c9f34c3. Адресное пространство это что фото. Адресное пространство это что-b27d87ea315ccc5f2ee945df3c9f34c3. картинка Адресное пространство это что. картинка b27d87ea315ccc5f2ee945df3c9f34c3
Управление памятью – центральный аспект в работе операционных систем. Он оказывает основополагающее влияние на сферу программирования и системного администрирования. В нескольких последующих постах я коснусь вопросов, связанных с работой памяти. Упор будет сделан на практические аспекты, однако и детали внутреннего устройства игнорировать не будем. Рассматриваемые концепции являются достаточно общими, но проиллюстрированы в основном на примере Linux и Windows, выполняющихся на x86-32 компьютере. Первый пост описывает организацию памяти пользовательских процессов.

Каждый процесс в многозадачной ОС выполняется в собственной “песочнице”. Эта песочница представляет собой виртуальное адресное пространство, которое в 32-битном защищенном режиме всегда имеет размер равный 4 гигабайтам. Соответствие между виртуальным пространством и физической памятью описывается с помощью таблицы страниц (page table). Ядро создает и заполняет таблицы, а процессор обращается к ним при необходимости осуществить трансляцию адреса. Каждый процесс работает со своим набором таблиц. Есть один важный момент — концепция виртуальной адресации распространяется на все выполняемое ПО, включая и само ядро. По этой причине для него резервируется часть виртуального адресного пространства (т.н. kernel space).

Это конечно не значит, что ядро занимает все это пространство, просто данный диапазон адресов может быть использован для мэппирования любой части физического адресного пространства по выбору ядра. Страницы памяти, соответствующие kernel space, помечены в таблицах страниц как доступные исключительно для привилегированного кода (кольцо 2 или более привилегированное). При попытке обращения к этим страницам из user mode кода генерируется page fault. В случае с Linux, kernel space всегда присутствует в памяти процесса, и разные процессы мэппируют kernel space в одну и ту же область физической памяти. Таким образом, код и данные ядра всегда доступны при необходимости обработать прерывание или системный вызов. В противоположность, оперативная память, замэппированная в user mode space, меняется при каждом переключении контекста.

Адресное пространство это что. ce2f75212cc1f7ca73d92b07546cff39. Адресное пространство это что фото. Адресное пространство это что-ce2f75212cc1f7ca73d92b07546cff39. картинка Адресное пространство это что. картинка ce2f75212cc1f7ca73d92b07546cff39

Синим цветом на рисунке отмечены области виртуального адресного пространства, которым в соответствие поставлены участки физической памяти; белым цветом — еще не использованные области. Как видно, Firefox использовал большую часть своего виртуального адресного пространства. Все мы знаем о легендарной прожорливости этой программы в отношении оперативной памяти. Синие полосы на рисунке — это сегменты памяти программы, такие как куча (heap), стек и так далее. Обратите внимание, что в данном случае под сегментами мы подразумеваем просто непрерывные адресные диапазоны. Это не те сегменты, о которых мы говорим при описании сегментации в Intel процессорах. Так или иначе, вот стандартная схема организации памяти процесса в Linux:

Адресное пространство это что. 4686460f9c5995b4300ae5f8d6275f91. Адресное пространство это что фото. Адресное пространство это что-4686460f9c5995b4300ae5f8d6275f91. картинка Адресное пространство это что. картинка 4686460f9c5995b4300ae5f8d6275f91

Давным давно, когда компьютерная техника находилась в совсем еще младенческом возрасте, начальные виртуальные адреса сегментов были совершенно одинаковыми почти для всех процессов, выполняемых машиной. Из-за этого значительно упрощалось удаленное эксплуатирование уязвимостей. Эксплойту часто необходимо обращаться к памяти по абсолютным адресам, например по некоторому адресу в стеке, по адресу библиотечной функции, и тому подобное. Хакер, рассчитывающий осуществить удаленную атаку, должен выбирать адреса для обращения в слепую в расчете на то, что размещение сегментов программы в памяти на разных машинах будет идентичным. И когда оно действительно идентичное, случается, что людей хакают. По этой причине, приобрел популярность механизм рандомизации расположения сегментов в адресном пространстве процесса. Linux рандомизирует расположение стека, сегмента для memory mapping, и кучи – их стартовый адрес вычисляется путем добавления смещения. К сожалению, 32-битное пространство не очень-то большое, и эффективность рандомизации в известной степени нивелируется.

В верхней части user mode space расположен стековый сегмент. Большинство языков программирования используют его для хранения локальных переменных и аргументов, переданных в функцию. Вызов функции или метода приводит к помещению в стек т.н. стекового фрейма. Когда функция возвращает управление, стековый фрейм уничтожается. Стек устроен достаточно просто — данные обрабатываются в соответствии с принципом «последним пришёл — первым обслужен» (LIFO). По этой причине, для отслеживания содержания стека не нужно сложных управляющих структур – достаточно всего лишь указателя на верхушку стека. Добавление данных в стек и их удаление – быстрая и четко определенная операция. Более того, многократное использование одних и тех же областей стекового сегмента приводит к тому, что они, как правило, находятся в кеше процессора, что еще более ускоряет доступ. Каждый тред в рамках процесса работает с собственным стеком.

Возможна ситуация, когда пространство, отведенное под стековый сегмент, не может вместить в себя добавляемые данные. В результате, будет сгенерирован page fault, который в Linux обрабатывается функцией expand_stack(). Она, в свою очередь, вызовет другую функцию — acct_stack_growth(), которая отвечает за проверку возможности увеличить стековый сегмент. Если размер стекового сегмента меньше значения константы RLIMIT_STACK (обычно 8 МБ), то он наращивается, и программа продолжает выполняться как ни в чем не бывало. Это стандартный механизм, посредством которого размер стекового сегмента увеличивается в соответствии с потребностями. Однако, если достигнут максимально разрещённый размер стекового сегмента, то происходит переполнение стека (stack overflow), и программе посылается сигнал Segmentation Fault. Стековый сегмент может увеличиваться при необходимости, но никогда не уменьшается, даже если сама стековая структура, содержащаяся в нем, становиться меньше. Подобно федеральному бюджету, стековый сегмент может только расти.

Динамическое наращивание стека – единственная ситуация, когда обращение к «немэппированной» области памяти, может быть расценено как валидная операция. Любое другое обращение приводит к генерации page fault, за которым следует Segmentation Fault. Некоторые используемые области помечены как read-only, и обращение к ним также приводит к Segmentation Fault.

Под стеком располагается сегмент для memory mapping. Ядро использует этот сегмент для мэппирования (отображания в память) содержимого файлов. Любое приложение может воспользоваться данным функционалом посредством системного вызовома mmap() (ссылка на описание реализации вызова mmap) или CreateFileMapping() / MapViewOfFile() в Windows. Отображение файлов в память – удобный и высокопроизводительный метод файлового ввода / вывода, и он используется, например, для загрузки динамических библиотек. Существует возможность осуществить анонимное отображение в память (anonymous memory mapping), в результате чего получим область, в которую не отображен никакой файл, и которая вместо этого используется для размещения разного рода данных, с которыми работает программа. Если в Linux запросить выделение большого блока памяти с помощью malloc(), то вместо того, чтобы выделить память в куче, стандартная библиотека C задействует механизм анонимного отображения. Слово «большой», в данном случае, означает величину в байтах большую, чем значение константы MMAP_THRESHOLD. По умолчанию, это величина равна 128 кБ, и может контролироваться через вызов mallopt().

Кстати о куче. Она идет следующей в нашем описании адресного пространства процесса. Подобно стеку, куча используется для выделения памяти во время выполнения программы. В отличие от стека, память, выделенная в куче, сохранится после того, как функция, вызвавшая выделение этой памяти, завершится. Большинство языков предоставляют средства управления памятью в куче. Таким образом, ядро и среда выполнения языка совместно осуществляют динамическое выделение дополнительной памяти. В языке C, интерфейсом для работы с кучей является семейство функций malloc(), в то время как в языках с поддержкой garbage collection, вроде C#, основной интерфейс – это оператор new.

Если текущий размер кучи позволяет выделить запрошенный объем памяти, то выделение может быть осуществлено средствами одной лишь среды выполнения, без привлечения ядра. В противном случае, функция malloc() задействует системный вызов brk() для необходимого увеличения кучи (ссылка на описание реализации вызова brk). Управление памятью в куче – нетривиальная задача, для решения которой используются сложные алгоритмы. Данные алгоритмы стремятся достичь высокой скорости и эффективности в условиях непредсказуемых и хаотичных пэттернов выделения памяти в наших программах. Время, затрачиваемое на каждый запрос по выделению памяти в куче, может разительно отличаться. Для решения данной проблемы, системы реального времени используют специализированные аллокаторы памяти. Куча также подвержена фрагментированию, что, к примеру, изображено на рисунке:

Адресное пространство это что. a27329ae21ae0d2ecb6efd0ced4a6f54. Адресное пространство это что фото. Адресное пространство это что-a27329ae21ae0d2ecb6efd0ced4a6f54. картинка Адресное пространство это что. картинка a27329ae21ae0d2ecb6efd0ced4a6f54

Наконец, мы добрались до сегментов, расположенных в нижней части адресного пространства процесса: BSS, сегмент данных (data segment) и сегмент кода (text segment). BSS и data сегмент хранят данные, соответствующий static переменным в исходном коде на C. Разница в том, что в BSS хранятся данные, соответствующие неинициализированным переменным, чьи значения явно не указаны в исходном коде (в действительности, там хранятся объекты, при создании которых в декларации переменной либо явно указано нулевое значение, либо значение изначально не указано, и в линкуемых файлах нет таких же common символов, с ненулевым значением. – прим. перевод.). Для сегмента BSS используется анонимное отображение в память, т.е. никакой файл в этот сегмент не мэппируется. Если в исходном файле на C использовать int cntActiveUsers, то место под соответствующий объект будет выделено в BSS.

В отличии от BSS, data cегмент хранит объекты, которым в исходном коде соответствуют декларации static переменных, инициализированных ненулевым значением. Этот сегмент памяти не является анонимным — в него мэппируется часть образа программы. Таким образом, если мы используем static int cntWorkerBees = 10, то место под соответствующий объект будет выделено в data сегменте, и оно будет хранить значение 10. Хотя в data сегмент отображается файл, это т.н. «приватный мэппинг» (private memory mapping). Это значит, что изменения данных в этом сегменте не повлияют на содержание соответствующего файла. Так и должно быть, иначе присвоения значений глобальным переменным привели бы к изменению содержания файла, хранящегося на диске. В данном случае это совсем не нужно!

Адресное пространство это что. aa9400b1449331d2694fc2e45ee6a639. Адресное пространство это что фото. Адресное пространство это что-aa9400b1449331d2694fc2e45ee6a639. картинка Адресное пространство это что. картинка aa9400b1449331d2694fc2e45ee6a639

Мы можем посмотреть, как используются области памяти процесса, прочитав содержимое файла /proc/pid_of_process/maps. Обратите внимание, что содержимое самого сегмента может состоять из различных областей. Например, каждой мэппируемой в memory mapping сегмент динамической библиотеке отводится своя область, и в ней можно выделить области для BSS и data сегментов библиотеки. В следующем посте поясним, что конкретно подразумевается под словом “область”. Учтите, что иногда люди говорят “data сегмент”, подразумевая под этим data + BSS + heap.

Можно использовать утилиты nm и objdump для просмотра содержимого бинарных исполняемых образов: символов, их адресов, сегментов и т.д. Наконец, то, что описано в этом посте – это так называемая “гибкая” организация памяти процесса (flexible memory layout), которая вот уже несколько лет используется в Linux по умолчанию. Данная схема предполагает, что у нас определено значение константы RLIMIT_STACK. Когда это не так, Linux использует т.н. классическую организации, которая изображена на рисунке:

Адресное пространство это что. 2da3b92412203e699b9a05d4651e6c05. Адресное пространство это что фото. Адресное пространство это что-2da3b92412203e699b9a05d4651e6c05. картинка Адресное пространство это что. картинка 2da3b92412203e699b9a05d4651e6c05

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

Источник

Организация памяти

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

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

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

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

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

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

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

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, так как они нам не понадобятся. Итак наше небольшое отступление про регистры и стек закончилось переходим обратно к организации памяти.

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

линейный адрес=Базовый адрес сегмента(на картинке это начало сегмента) + смещение
Сегмент кода
Сегмент данных
Сегмент стека

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

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

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

Кстати интересно почему база и предел так рвано располагаются в дескрипторе. Дело в том что процессоры х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, и производится повторное обращение к дескриптору, сегмент которого находится уже в памяти.

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

Источник

Что такое адресное пространство?

Согласно определению, это совокупность допустимых адресов каких либо объектов вычислительной системы. Это могут быть ячейки памяти. Вот о них и поговорим. Насколько это важный вопрос? Да без него и не стоит затрагивать операционные системы вообще, это один из основных вопросов. В этом цикле статей мы не собираемся учиться по программе учебной дисциплины полгода, а то и больше. Рассмотрим наиболее важные вопросы, легко, приятно и на оптимальную глубину.

Простой пример адресного пространства

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

Простейший процессор с регистром вывода

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

Адресное пространство простейшего компьютера (процессора)

Нижняя ячейка это слово в памяти данных по нулевому адресу. А по адресу 31 уже регистр вывода. Такая схема наглядно демонстрирует адресное пространство этой вычислительной системы.

Проекция оборудования на память

Логический и физический адрес

Теперь перейдем к недавно разработанной архитектуре учебного компьютера.

Архитектура учебного компьютера

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

Формирование логического адреса

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

Адресные регистры компьютера

В нашей архитектуре выделено 16 адресных регистров. На схеме адресного пространства они занимают самые верхние адреса 16-битного адресного пространства. Каждый регистр имеет разрядность 16 бит. Их имена это аббревиатуры PAR, что читается как Page Address Register или по-русски адресный регистр. Они имеют номера от 0 до 15. Эти регистры необходимы для вычисления реального физического адреса инструкций либо данных внутри памяти. Как же это происходит. На самом деле все довольно просто. В составе контроллера памяти имеется арифметико-логическое устройство разрядностью 24 бита.

Формирование физического адреса

Рассчитанный 16-битный логический адрес складывается с содержимым адресного регистра, смещенного на 8 бит. Все неиспользованные линии являются нулями. Результатом суммы является реальный физический адрес инструкции либо данных. Разрядность адреса 24 бита. Этой разрядностью можно охватить пространство в 16 мегабайт. Действительно, два в степени 24 это более 16 миллионов адресов. В данном примере логический адрес это 0. Содержимое адресного регистра это тоже 0. Как и следует ожидать, физически мы обращаемся к нулевой ячейке памяти.

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

Страницы памяти

За выбор адресного регистра отвечают старшие 4 бита логического адреса.

Выбор адресного регистра

Остальные же 12 бит являются смещением внутри так называемой страницы памяти. В нашем примере под страницу выделено 12 бит, поэтому размер страницы это 2 в двенадцатой степени, а это 4 килобайта. Начало страницы задается содержимым адресного регистра, смещенным влево на 8 бит. Еще для лучшего понимания можно рассмотреть такой рисунок.

Логическое и физическое адресные пространства

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

Атрибуты страниц

Каждому адресному регистру, описывающему где находится страница, соответствует регистр атрибутов страницы.

Регистры атрибутов страниц

Итого, мы имеем еще 16 регистров, их адреса указаны на рисунке. Обращаясь по этим адресам, программа заносит данные не в оперативную память, а в эти служебные регистры. Зеленым цветом помечена область, отвечающая за размер страницы. При ограничениях доступных программе адресов схема контроля позволяет выявлять нарушения и сообщать об этом пользователю. Такой подход хорош тем, что защищает другие данные и не позволяет их случайно повредить. Все что произойдет, это вызовется другая программа, которая уберет из памяти программу нарушителя. При этом другие приложения спокойно продолжат свою работу. Иногда нужно чтобы данные никуда не перемещались с места на место. Красный бит позволяет защитить такие страницы от перемещения. Зачем такие опции также разберемся позже. Наконец, младшие три бита содержимого регистров атрибутов страниц отвечают за разрешение доступа к странице. Если из страницы можно считывать, то третий справа бит будет единицей. Если в страницу можно записывать, то второй бит справа будет единицей. И, наконец, страницы с машинными инструкциями помечаются единицей в самом правом бите. Цифровая схема внутри контроллера памяти позволяет выявлять все нарушения доступа и вызывать соответствующие программы, чтобы обработать ту или иную ситуацию.

Регистры присутствия страниц и регистр дескриптора процесса

Еще в контроллере памяти присутствуют 4 регистра.

Регистры присутствия страниц и регистр дескриптора процесса

Три из них показывают признак наличия страницы памяти в соответствующих местах. Страниц памяти у каждой программы может быть до шестнадцати, каждая страница, соответствует одному из битов. Единица — страница есть, ноль — страницы нет. Такие регистры со специальной схемой позволяют организовать перекачку страниц между быстрой кэш памятью и оперативной памятью, также, в случае нехватки оперативной памяти страницы могут быть откачены на диск. Регистр с именем PD это указатель на область памяти, где лежит структура, описывающая работающую программу. Подробности этой структуры сейчас тоже не будут рассмотрены. Красный бит отвечает за привилегии. Если бит нулевой, то программе можно обращаться по любым адресам. Если бит единица, то для этой программы будет работать схема контроля адресов. Такой порядок в той или иной разновидности принят в более-менее сложных процессорах с давних пор и традиции продолжаются по сей день. Операционная система, драйвера устройств это программный код с высокой степенью привилегий. Им можно работать со всем доступным адресным пространством. Прикладным программам запрещено обращаться за пределы допустимых адресов. Любое нарушение карается аварийным завершением программы, сообщением пользователю об ошибке.

Обработка неправильных действий программы

Проекция контроллера шины на память

Еще одной выделенной областью в адресном пространстве являются адреса для работы с регистрами контроллера шины.

Проекция контроллера шины на память

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

Архитектура х86

Насколько рассмотренная архитектура близка к реальной жизни? Для этого рассмотрим одну из самых популярных архитектур компьютеров. Она носит название x86. В начале восьмидесятых годов прошлого века компания Intel и IBM начали подминать под себя весь рынок настольных компьютеров.

Процессор Intel 8086 и его архитектура

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

Адресное пространство процессора Intel 8086

Для операционной системы и прикладных программ было доступно около 640 килобайт свободной памяти. Как же 16-битные регистры могли организовать доступ за пределы 64 килобайт? Да так же просто.

Формирование физического адреса процессора Intel 8086

Физический адрес вычисляется путем сложения содержимого регистров. Один из регистров, называемый сегментным перед сложением сдвигается влево на 4 бита. Разрядность физического адреса составляет 20 бит, что позволяет адресовать до мегабайта информации, куда входят программный код, данные и регистры всевозможного оборудования.
Эта архитектура впоследствии увеличила разрядность с 16 бит до 32, а потом и до 64 бит. Необходимость в обратной совместимости программ принуждало разработчиков аппаратуры совершать очередные подвиги. Старые программы все еще было необходимо запускать на новых компьютерах. Цена таких подвигов это крайне запутанная система команд, дескрипторов и вообще сейчас новичкам уже почти нет шансов в этом разобраться. Это тяжелое наследие десятилетий.

Материал в формате видео

Поддержите статью репостом если понравилось и подпишитесь чтобы ничего не пропускать, а также посетите канал на YouTube c интересными материалами в формате видео.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *