Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Последние темы форума

Показать новые сообщения »

Почтовая рассылка

Подписчиков: 11829
Последний выпуск: 19.06.2015

Оперативная память. Эпизод II. Логическая структура

Автор: Шаймарданов Булат
8 августа 2006 года

Введение

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

Сегментная модель памяти

Когда-то давно, на заре рождения компьютерной техники, оперативная память была очень маленькой и для ее адресации использовались 2 байта (так называемое "слово"). Такой подход позволял адресовать 64 Кб памяти, и адресация была линейной - для указания адреса использовалось одно-единственное число. Позже, с усовершенствованием техники, производители поняли, что имеется возможность поддерживать бОльшие объемы памяти, но для этого нужно сделать размер адреса больше. Для совместимости с уже написанным программным обеспечением было решено сделать так: адресация теперь двухкомпонентная (сегмент и смещение), каждая из которых 16-битная, а старые программы как использовали одну 16-битную компоненту и ничего не знают о сегментах, так и продолжают работать. Физический адрес в такой модели вычисляется так: 16 * + , таким образом, модель позволяла адресовать от 0 до 16 * 65 535 + 65 535 = 1 114 095, т. е. 1 114 096 байт, что чуть больше 1 Мб. Одно важное свойство сегментной адресации: один и тот же физический адрес можно представить различными комбинациями сегмент + смещение. При этом смещения могут указываться в процессорных командах как непосредственно, так и с помощью регистров, а сегменты непосредственно указываться не могут: для указания сегмента используются специальные 16-битные сегментные регистры. Поначалу их было 4: CS, DS, SS, ES (сегмент команд, сегмент данных, сегмент стека, дополнительный сегмент). Появившиеся позже 2 дополнительных сегментных регистра мы рассмотрим позже. Большинство команд процессора по умолчанию используют тот или иной сегмент - например, команды чтения и записи данных используют сегмент данных, стековые команды (push, pop, pusha, popa, pushf, popf) используют SS. В некоторых командах есть возможность использовать другой сегмент, нежели сегмент по умолчанию - для этого используется специальная команда-префикс. Содержимое сегментных регистров также можно менять (кроме CS - изменить содержимое CS равнозначно дальнему переходу, поэтому для этого и используются дальние переходы).

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

С появлением 32-битных процессоров необходимость в такой модели отпала и современные 32-битные программы, как правило, с сегментами и сегментными регистрами не работают вообще. Но благодаря принципу совместимости, которого придерживается Intel как "законодатель" архитектур, сегментную модель было решено оставить. Следует отметить, что с появлением 32-битных процессоров сегментная часть адреса осталась 16-битной, а вот смещение стало 32-битным, а также все регистры, кроме сегментных, стали 32-битными. Изменился и принцип вычисления физического адреса: сегмент ни на что не умножается, просто есть специальная таблица, адрес и размер которой "знает" процессор, эта таблица содержит для каждого сегмента стартовый адрес. Так вот, само понятие "сегмент" теперь означает некоторую область данных, начинающуюся с произвольного 32-битного адреса и заканчивающуюся так же произвольным 32-битным адресом, а вышеупомянутая таблица хранит данные об этих сегментах - собственно стартовые адреса и размеры. Номер сегмента в этой таблице (нумерация начинается с нуля) называется селектором. Программист, указывая сегмент и смещение, на самом деле указывает селектор и смещение, а процессор, обращаясь к таблице сегментов, находит там стартовый адрес сегмента и прибавляет к нему смещение. Таким образом, теперь сегменты не обязательно начинаются на 16-байтной границе - их стартовый адрес произволен.

Следует оговориться: 32-битные процессоры имеют два режима работы - реальный и защищенный, и старая модель сегментации работает в реальном режиме процессора, а новая - в защищенном. (На самом деле здесь есть свои нюансы - например, можно "обхитрить" процессор и в реальном режиме адресовать все 4 Гб, благодаря так называемым "теневым" сегментным регистрам - но это выходит за рамки статьи).

Остается добавить, что большинство современного ПО вообще не трогает сегментные регистры - операционная система дает им один-единственный сегмент, которые начинаются с нулевого адреса и заканчиваются 2^32 - 1 (таким образом охватывая всю возможную оперативную память) - и загружает этими данными регистры CS, DS, SS ES.

Виртуальное адресное пространство процесса. Страничная трансляция

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

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

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

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

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

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

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

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

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

Читайте в следующей статье: управление памятью с точки зрения приложения. Функции выделения и освобождения памяти. Языки и системы со сборкой мусора. Преимущества и недостатки сборки мусора. Возможности реализации аналогичных механизмов в языках без сборки мусора.

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог