Страничная память

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

Страничная память — способ организации виртуальной памяти, при котором единицей отображения виртуальных адресов на физические является регион постоянного размера (т. н. страница). Типичный размер 4096 байт, для некоторых архитектур до 128 КБ.

Поддержка такого режима присутствует в большинстве 32битных и 64битных процессоров. Такой режим является классическим для почти всех современных ОС, в том числе Windows и семейства UNIX. Широкое использование такого режима началось с процессора VAX и ОС VMS с конца 70х годов (по некоторым сведениям, первая реализация). В семействе x86 поддержка появилась с поколения 386, оно же первое 32битное поколение.

Решаемые задачи[править | править вики-текст]

  • поддержка изоляции процессов и защиты памяти путём создания своего собственного виртуального адресного пространства для каждого процесса
  • поддержка изоляции области ядра от кода пользовательского режима
  • поддержка памяти «только для чтения» и неисполняемой памяти
  • поддержка отгрузки давно не используемых страниц в область подкачки на диске (см. свопинг)
  • поддержка отображённых в память файлов, в том числе загрузочных модулей
  • поддержка разделяемой между процессами памяти, в том числе с копированием-по-записи для экономии физических страниц
  • поддержка системного вызова fork() в ОС семейства UNIX

Концепции[править | править вики-текст]

Адрес, используемый в машинном коде, то есть значение указателя, называется «виртуальный адрес».

Адрес, выставляемый процессором на шину, называется «линейный адрес» (который позже преобразовывается в физический).

Процессор содержит в себе небольшой объём сверхбыстрой ассоциативной памяти, т. н. TLB (Translation Lookaside Buffer), в котором содержится преобразование нескольких (часто 64) виртуальных адресов страниц в физические. Все обращения процессора к памяти подлежат трансляции адресов через TLB.

Так как 64 строк таблицы явно недостаточно для реальных задач, в архитектуре используются таблицы страниц, размещённые в основной памяти. Каждая таблица страниц сама является страницей с теми же требованиями по выравниваю и тем же размером, и состоит из записей таблицы страниц (page table entries — PTE). Широко используется и отображение самой таблицы страниц как одной из страниц данных для внесения изменений в записи.

Запись таблицы страниц обычно содержит в себе следующую информацию:

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

Так как число записей в одной таблице ограничено и зависит от размера записи и размера страницы, используется многоуровневая организация таблиц, часто 2 или 3 уровня, иногда 4 уровня (для 64-х разрядных архитектур). В случае 2 уровней используется «директория» страниц, имеющая в себе записи, указывающие на физические адреса таблиц страниц. Таблицы содержат в себе записи, указывающие уже на страницы данных. В случае 3 уровней возникает ещё и супер-директория, содержащая в себе записи, указывающие на несколько директорий.

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

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

Исторически, x86 использует 32битные PTE, 32битные виртуальные адреса, 4KB страницы, 1024 записи в таблице, двухуровневые таблицы, старшие 10 бит виртуального адреса — номер записи в директории, следующие 10 — номер записи в таблице, младшие 12 — адрес внутри страницы. Начиная с Pentium Pro процессор поддерживает страницы размером 4Мб.

Процессор x86 в режиме PAE (Physical Address Extension) и в режиме x86_64 (long mode), использует 64битные PTE (из них реально задействованы не все биты физического адреса, от 36 в PAE до 48 в некоторых x86_64), 32битные виртуальные адреса, 4KB страницы, 512 записей в таблице, трехуровневые таблицы с 4 директориями и 4 записями в супер-директории, старшие 2 бита виртуального адреса — номер записи в супер-директории, следующие 9 — в директории, следующие 9 — в таблице. Физический адрес директории или же супер-директории загружен в один из управляющих регистров процессора.

При использовании PAE вместо 4Мб страниц используются двухмегабайтные. См. также PSE.

В архитектуре x86_64 возможно использовать страницы размером 4 килобайта (4096 байт), 2 мегабайта, и (в некоторых AMD64) 1 гигабайт.

Если обращение к памяти не может быть оттранслировано через TLB, то микрокод процессора обращается к таблицам страниц и пытается загрузить PTE оттуда в TLB. Если и после такой попытки сохранились проблемы, то процессор исполняет специальное прерывание, называемое «отказ страницы» (page fault). Обработчик этого прерывания находится в подсистеме виртуальной памяти ядра ОС.

Некоторые процессоры (MIPS) не имеют обращающегося к таблице микрокода, и генерируют отказ страницы сразу после неудачи поиска в TLB, обращение к таблице и её интерпретация возлагаются уже на обработчик отказа страницы. Это лишает таблицы страниц требования соответствовать жёстко заданному на уровне аппаратуры формату.

Причины отказа страницы (page fault):

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

Обработчик отказов в ядре может загрузить нужную страницу из файла или же из области подкачки (см. свопинг), может создать доступную на запись копию страницы «только для чтения», а может и возбудить исключительную ситуацию (в терминах UNIX — сигнал SIGSEGV) в данном процессе.

Каждый процесс имеет свой собственный набор таблиц страниц. Регистр «директория страниц» перегружается при каждом переключении контекста процесса. Также необходимо сбросить ту часть TLB, которая относится к данному процессу.

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

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

Так как память ядра одинакова у всех процессов, соответствующие ей TLB не нужно перегружать после переключения процесса. Для этой оптимизации x86 поддерживает флаг «глобальный» у PTE.

Отображаемые в память файлы[править | править вики-текст]

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

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

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

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

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

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

Страничная и сегментная виртуальная память[править | править вики-текст]

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

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

Кроме того, сегментные архитектуры имеют тяжелейшую проблему SS != DS, широко известную в начале 90х годов в программировании под 16битные версии Windows. Эта проблема приводит к затруднениям в реализации динамических библиотек, ибо они имеют свой собственный DS, и SS текущего процесса, что приводит к невозможности использования «ближних» указателей в них. Также наличие своего собственного DS в библиотеках требует устанавливающих правильное значение DS заплаток (MakeProcInstance) для обратных вызовов из библиотеки в вызвавшее приложение.

Виртуальная память и дисковый кэш[править | править вики-текст]

Основная статья: Страничный кэш (англ.)

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

Совершенно очевидно, что данная структура является уже готовым дисковым кэшем. Использование её в качестве кэша также решает проблему когерентности файла, доступного через read/write, и его же, отображенного в память.

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

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

Соображения безопасности[править | править вики-текст]

Первоначально архитектура x86 не имела флага «страница недоступна на исполнение» (NX).

Поддержка данного флага появилась в архитектуре x86 как часть режима PAE (Physical Address Extension) в поколении Pentium 4, под большим давлением со стороны специалистов по безопасности (см. архивы NTBugTraq). Установка данного флага на страницах стека и кучи (heap) позволяет реализовать аппаратно защиту от исполнения данных, что делает невозможной работу многих разновидностей вредоносного ПО, в том числе, например, злонамеренную эксплуатацию многих брешей в Internet Explorer (брешь декабря 2008 года, см. MS knowledge base, не может быть задействована в случае включенной DEP).

Поддержка PAE в Windows, дающая возможность включения защиты от исполнения данных, появилась в Windows 2000, она включена по умолчанию в серверных версиях Windows и отключена в клиентских.

Поддержка памяти свыше 4Гб в Windows[править | править вики-текст]

Устройства PCI, в том числе память видеокарты, обычно поддерживают только 32битные адреса. Следовательно, им должны быть выданы физические адреса ниже отметки 4Гб. Эта «апертура» уменьшает объем видимой физической памяти ниже отметки 4Гб до примерно 3.2Гб. Остальная часть физической памяти переотображается контроллером выше отметки 4Гб.

Для любого обращения к памяти свыше отметки 4Гб (то есть более, чем примерно 3.2Гб) требуется поддержка контроллером (то есть северным мостом чипсета) такой конфигурации. Современные чипсеты (например Intel G33) такую поддержку имеют.

Также требуется настройка BIOS под названием memory remapping, отображающая регион [3.2…4] на [4..4.8].

Процессор x86 вне режима PAE использует 32битные PTE и физические адреса, то есть ему недоступно ничто, находящееся выше отметки 4Гб (см. также PSE-36 об одном из вариантов обхода данного ограничения). Таким образом, для использования памяти более, чем примерно 3.2Гб в ОС она должна поддерживать PAE. Для Windows это опция загрузки, для Linux — опция построения ядра.

Кроме того, Microsoft принудительно отключила поддержку физических адресов выше 4Гб по политико-маркетинговым соображениям в следующих ОС:

  • 32битная Windows XP
  • 32битный Windows Server 2003 Web Edition
  • 32битная Windows Vista (подключение поддержки требует набора команд в командной строке: «BCDEdit /set PAE forceenable», «BCDEdit /set nolowmem on»)

Поддержка физических адресов выше 4Гб имеется в следующих версиях:

  • всe 64битные версии
  • 32битная Windows Vista SP1 (поддержка включена по умолчанию, но её подключение нередко может требовать набора команд в командной строке)
  • 32битный Windows Server 2003, отличный от Web Edition
  • 32битный Windows Server 2008

Таким образом, для того, чтобы использовать память выше 3.2Гб в Windows, нужны а) поддержка чипсетом б) правильные настройки BIOS в) правильная версия Windows г) правильная опция загрузки (с поддержкой PAE) д) поддержка 36-битного адресного пространства драйверами устройств.

Тем не менее даже в «урезанной» версии Windows, не поддерживающей адреса выше 4Гб, имеет смысл всегда использовать PAE, ибо (см. выше) защита от исполнения данных (DEP) тоже требует PAE. При включении PAE может перестать работать небольшая часть ПО, например, эмулятор windows mobile. Согласно официальной версии Microsoft, введение 4 ГБ ограничения адресного пространства связано с отсутствующей или плохой поддержкой 36-битного адресного пространства некоторыми драйверами устройств, это следует иметь в виду, по причине аппаратных ограничений или неподходящих драйверов невозможно подключить PAE, на версиях имеющих поддержку физических адресов выше 4Гб. Возможность включения или выключения PAE не зависит от драйверов, но, если драйвер какого-то старого PCI оборудования не поддерживает правильно физические адреса, не умещающиеся в 32 бита, то данное устройство будет работать неверно и может привести к зависанию всего компьютера.

См. также[править | править вики-текст]