Ответ 1
Версия этого ответа с хорошим TOC и большим количеством контента.
Я исправлю любую сообщенную ошибку. Если вы хотите сделать большие изменения или добавить недостающий аспект, сделайте их на свои собственные ответы, чтобы получить заслуженную репутацию. Незначительные изменения могут быть объединены напрямую.
Пример кода
Минимальный пример: https://github.com/cirosantilli/x86-bare-metal-examples/blob/5c672f73884a487414b3e21bd9e579c67cd77621/paging.S
Как и все остальное в программировании, единственный способ понять это - играть с минимальными примерами.
Что делает этот "жесткий" объект тем, что минимальный пример большой, потому что вам нужно создать свою собственную небольшую ОС.
Руководство Intel
Хотя невозможно понять без примеров, постарайтесь как можно скорее ознакомиться с руководствами.
Intel описывает пейджинг в Руководство по программированию Intel Guide Volume 3 - 325384-056US Сентябрь 2015 г. Глава 4 "Пейджинг" .
Особо интересна диаграмма 4-4 "Форматы CR3 и записи подкачки с 32-разрядным пейджингом", которая дает ключевые структуры данных.
MMU
Пейджинг выполняется блоком Блок управления памятью (MMU) ЦП. Как и многие другие (например, x88 co-processor, APIC), раньше это был отдельный чип в ранние дни, который позже был интегрирован в CPU. Но этот термин все еще используется.
Общие факты
Логические адреса - это адреса памяти, используемые в "обычном" пользовательском коде земли (например, содержимое rsi
в mov eax, [rsi]
).
Первая сегментация преобразует их в линейные адреса, а затем пейджинг затем преобразует линейные адреса в физические адреса.
(logical) ------------------> (linear) ------------> (physical)
segmentation paging
В большинстве случаев мы можем думать о физических адресах как об индексировании фактических ячеек памяти оперативной памяти, но это не 100% истинно из-за:
Пейджинг доступен только в защищенном режиме. Использование пейджинга в защищенном режиме является необязательным. Пейджинг включен, если установлен бит PG
регистра cr0
.
Пейджинг против сегментации
Одним из основных различий между поисковым вызовом и сегментацией является то, что:
- пейджинг разбивает RAM на равные куски, называемые страницами
- сегментация разбивает память на куски произвольных размеров
Это основное преимущество пейджинга, поскольку куски равного размера делают вещи более управляемыми.
Пейджинг стал настолько популярным, что поддержка сегментации была отброшена в x86-64 в 64-битном режиме, основной режим работы для нового программного обеспечения, где он существует только в режиме совместимости, который эмулирует IA32.
Применение
Пейджинг используется для реализации виртуальных адресных пространств процессов на современной ОС. С виртуальными адресами ОС может соответствовать двум или более параллельным процессам в одной ОЗУ таким образом, что:
- обе программы должны ничего не знать о других
- память обеих программ может расти и сокращаться по мере необходимости
- переключатель между программами очень быстрый
- одна программа никогда не сможет получить доступ к памяти другого процесса.
Пейджинг исторически появился после сегментации и в значительной степени заменил его на реализацию виртуальной памяти в современных ОС, таких как Linux, поскольку проще управлять блоками фиксированного размера памяти страниц вместо сегментов переменной длины.
Аппаратная реализация
Подобно сегментированию в защищенном режиме (где изменение регистра сегмента запускает нагрузку с GDT или LDT), оборудование подкачки использует структуры данных в памяти для выполнения своего задания (таблицы страниц, каталоги страниц и т.д.).
Формат этих структур данных фиксируется аппаратным обеспечением, но именно ОС должна правильно настроить и управлять этими структурами данных в ОЗУ и сообщить аппаратное обеспечение, где их можно найти (через cr3
).
Некоторые другие архитектуры оставляют пейджинг почти полностью в руках программного обеспечения, поэтому пропущенный TLB выполняет функцию, предоставляемую ОС, для перемещения таблиц страниц и вставки нового отображения в TLB. Это оставляет формат таблицы страниц, который будет выбран ОС, но делает его маловероятным для того, чтобы аппаратное обеспечение могло перекрывать переходы страниц с нарушением выполнения других инструкций, способом x86 может.
Пример: упрощенная одноуровневая схема поискового вызова
Это пример того, как пейджинг работает с упрощенной версией архитектуры x86 для реализации виртуального пространства памяти.
Таблицы страниц
ОС может предоставить им следующие таблицы страниц:
Таблица страниц, данная процессу 1 операционной системой:
RAM location physical address present
----------------- ----------------- --------
PT1 + 0 * L 0x00001 1
PT1 + 1 * L 0x00000 1
PT1 + 2 * L 0x00003 1
PT1 + 3 * L 0
... ...
PT1 + 0xFFFFF * L 0x00005 1
Таблица страниц, предоставленная операционной системе OS 2:
RAM location physical address present
----------------- ----------------- --------
PT2 + 0 * L 0x0000A 1
PT2 + 1 * L 0x0000B 1
PT2 + 2 * L 0
PT2 + 3 * L 0x00003 1
... ... ...
PT2 + 0xFFFFF * L 0x00004 1
Где:
-
PT1
иPT2
: начальное положение таблиц 1 и 2 в ОЗУ.Примеры значений:
0x00000000
,0x12345678
и т.д.Именно ОС определяет эти значения.
-
L
: длина записи в таблице страниц. -
present
: указывает, что страница присутствует в памяти.
Таблицы страниц расположены в ОЗУ. Например, они могут быть расположены как:
--------------> 0xFFFFFFFF
--------------> PT1 + 0xFFFFF * L
Page Table 1
--------------> PT1
--------------> PT2 + 0xFFFFF * L
Page Table 2
--------------> PT2
--------------> 0x0
Исходные местоположения в ОЗУ для обеих таблиц страниц произвольны и контролируются ОС. Это зависит от ОС, чтобы они не перекрывались!
Каждый процесс не может напрямую касаться таблиц страниц, хотя он может делать запросы к ОС, которые вызывают изменение таблиц страниц, например, запрашивая большие сегменты стека или кучи.
Страница представляет собой фрагмент 4 КБ (12 бит), а поскольку адреса имеют 32 бита, для идентификации каждой страницы требуется только 20 бит (20 + 12 = 32, а значит, 5 символов в шестнадцатеричной нотации). Это значение фиксируется аппаратным обеспечением.
Записи таблицы страниц
Таблица страниц - это таблица записей в таблице страниц!
Точный формат записей в таблице фиксируется аппаратным обеспечением.
В этом упрощенном примере записи в таблице страниц содержат только два поля:
bits function
----- -----------------------------------------
20 physical address of the start of the page
1 present flag
поэтому в этом примере разработчики аппаратного обеспечения могли выбрать L = 21
.
В большинстве записей таблицы таблицы есть другие поля.
Было бы нецелесообразно выравнивать объекты в 21 байт, поскольку память адресуется байтами, а не битами. Поэтому даже в этом случае нужны только 21 бит, разработчики аппаратного обеспечения, вероятно, предпочли бы L = 32
сделать доступ быстрее и просто зарезервировать биты оставшихся битов для последующего использования. Фактическое значение для L
на x86 - 32 бита.
Трансляция адресов в одноуровневой схеме
Как только таблицы страниц были настроены ОС, преобразование адресов между линейными и физическими адресами выполняется аппаратным обеспечением.
Когда ОС хочет активировать процесс 1, он устанавливает cr3
в PT1
, начало таблицы для первого процесса.
Если процесс 1 хочет получить доступ к линейному адресу 0x00000001
, аппаратная схема персонального вызова автоматически выполняет следующие действия для ОС:
-
разделите линейный адрес на две части:
| page (20 bits) | offset (12 bits) |
Итак, в этом случае мы имели бы:
- page = 0x00000
- offset = 0x001
-
загляните в таблицу страниц, потому что
cr3
указывает на нее. -
посмотреть запись
0x00000
, потому что это часть страницы.Аппаратное обеспечение знает, что эта запись находится в адресе RAM
PT1 + 0 * L = PT1
. -
поскольку он присутствует, доступ действителен
-
по таблице страниц, расположение номера страницы
0x00000
находится на0x00001 * 4K = 0x00001000
. -
чтобы найти окончательный физический адрес, нам просто нужно добавить смещение:
00001 000 + 00000 001 ----------- 00001 001
поскольку
00001
- физический адрес страницы, просмотренной в таблице, и001
- это смещение.Как видно из названия, смещение всегда просто добавляет физический адрес страницы.
-
аппаратное обеспечение затем получает память в этом физическом местоположении.
Таким же образом для процесса 1 произойдут следующие переводы:
linear physical
--------- ---------
00000 002 00001 002
00000 003 00001 003
00000 FFF 00001 FFF
00001 000 00000 000
00001 001 00000 001
00001 FFF 00000 FFF
00002 000 00002 000
FFFFF 000 00005 000
Например, при обращении к адресу 00001000
часть страницы 00001
аппаратное обеспечение знает, что его запись в таблице страниц находится по адресу RAM: PT1 + 1 * L
(1
из-за части страницы), и что где он будет искать его.
Когда ОС хочет переключиться на процесс 2, все, что ему нужно сделать, это сделать cr3
на стр. 2. Это просто!
Теперь для процесса 2 будут выполняться следующие переводы:
linear physical
--------- ---------
00000 002 00001 002
00000 003 00001 003
00000 FFF 00001 FFF
00001 000 00000 000
00001 001 00000 001
00001 FFF 00000 FFF
00003 000 00003 000
FFFFF 000 00004 000
Такой же линейный адрес преобразуется в разные физические адреса для разных процессов, в зависимости от значения внутри cr3
.
Таким образом, каждая программа может ожидать, что ее данные начнутся с 0
и заканчиваются на FFFFFFFF
, не беспокоясь о точном физическом адресе.
Ошибка страницы
Что делать, если процесс 1 пытается получить доступ к адресу внутри страницы, которой нет?
Аппаратное обеспечение уведомляет программное обеспечение через исключение Page Fault Exception.
Обычно обычно до ОС регистрируется обработчик исключений, чтобы решить, что нужно сделать.
Возможно, что доступ к странице, которая не находится в таблице, является ошибкой программирования:
int is[1];
is[2] = 1;
но могут быть случаи, когда это приемлемо, например, в Linux, когда:
-
программа хочет увеличить свой стек.
Он просто пытается получить доступ к определенному байту в заданном возможном диапазоне, и если ОС счастлив, он добавляет эту страницу в адресное пространство процесса.
-
страница была заменена на диск.
ОС потребуется выполнить некоторую работу за процессами, чтобы вернуть страницу в ОЗУ.
ОС может обнаружить, что это имеет место на основе содержимого остальной части записи в таблице страниц, поскольку, если текущий флаг ясен, другие записи в записи таблицы страниц полностью оставлены для ОС до того, что он хочет.
В Linux, например, если присутствует = 0:
-
если все поля записи в таблице страниц равны 0, неверный адрес.
-
else, страница была заменена на диск, и фактические значения этих полей кодируют положение страницы на диске.
-
В любом случае ОС необходимо знать, какой адрес сгенерировал ошибку страницы, чтобы иметь возможность справиться с этой проблемой. Вот почему хорошие разработчики IA32 устанавливают значение cr2
на этот адрес всякий раз, когда возникает ошибка страницы. Обработчик исключений может затем просто посмотреть cr2
, чтобы получить адрес.
Упрощения
Упрощение реальности, облегчающее этот пример:
-
все реальные пейджинговые схемы используют многоуровневый пейджинг для экономии места, но это показало простую одноуровневую схему.
-
таблицы страниц содержали только два поля: 20-битный адрес и 1-битовый флаг.
Таблицы реальных страниц содержат в общей сложности 12 полей и, следовательно, другие функции, которые были опущены.
Пример: многоуровневая схема поискового вызова
Проблема с одноуровневой схемой поискового вызова заключается в том, что для этого потребуется слишком много оперативной памяти: 4G/4K = 1M записей на процесс. Если каждая запись имеет длину 4 байта, это составит 4 М на каждый процесс, что слишком много для настольного компьютера: ps -A | wc -l
говорит, что я запускаю 244 процесса прямо сейчас, так что это займет около 1 ГБ моей памяти!
По этой причине разработчики x86 решили использовать многоуровневую схему, которая уменьшает использование ОЗУ.
Недостатком этой системы является то, что имеет немного более высокое время доступа.
В простой 3-уровневой схеме поискового вызова, используемой для 32-разрядных процессоров без PAE, 32 адресных бита делятся следующим образом:
| directory (10 bits) | table (10 bits) | offset (12 bits) |
Каждый процесс должен иметь один и только один каталог страниц, связанный с ним, поэтому он будет содержать не менее 2^10 = 1K
записей в каталоге страниц, намного лучше, чем минимум 1M, требуемый для одноуровневой схемы.
Таблицы страниц выделяются только по необходимости ОС. Каждая таблица страниц имеет 2^10 = 1K
записи в каталоге страниц
Каталоги страниц содержат... записи в каталоге страниц! Записи каталога страниц такие же, как и записи в таблице страниц, за исключением того, что они указывают на адреса ОЗУ таблиц страниц вместо физических адресов таблиц. Поскольку эти адреса имеют ширину всего 20 бит, таблицы страниц должны быть в начале страниц 4 КБ.
cr3
теперь указывает на расположение в ОЗУ каталога страницы текущего процесса вместо таблиц страниц.
Записи в таблицах страниц вообще не меняются из одноуровневой схемы.
Таблицы страниц меняются с одноуровневой схемы, потому что:
- каждый процесс может иметь до 1 тыс. таблиц страниц, по одному на странице каталога.
- каждая таблица таблицы содержит ровно 1K записей вместо 1M записей.
Причиной использования 10 бит на первых двух уровнях (а не, скажем, 12 | 8 | 12
) является то, что каждая запись в таблице страниц имеет длину 4 байта. Затем 2 ^ 10 записей в каталогах страниц и таблицах страниц прекрасно подойдут к страницам 4 КБ. Это означает, что быстрее и проще распределять и освобождать страницы для этой цели.
Трансляция адресов в многоуровневой схеме
Каталог страниц, предоставленный процессу 1 операционной системой:
RAM location physical address present
--------------- ----------------- --------
PD1 + 0 * L 0x10000 1
PD1 + 1 * L 0
PD1 + 2 * L 0x80000 1
PD1 + 3 * L 0
... ...
PD1 + 0x3FF * L 0
Таблицы страниц, предоставленные процессу 1 ОС в PT1 = 0x10000000
(0x10000
* 4K):
RAM location physical address present
--------------- ----------------- --------
PT1 + 0 * L 0x00001 1
PT1 + 1 * L 0
PT1 + 2 * L 0x0000D 1
... ...
PT1 + 0x3FF * L 0x00005 1
Таблицы страниц, переданные процессу 1 ОС на PT2 = 0x80000000
(0x80000
* 4K):
RAM location physical address present
--------------- ----------------- --------
PT2 + 0 * L 0x0000A 1
PT2 + 1 * L 0x0000C 1
PT2 + 2 * L 0
... ...
PT2 + 0x3FF * L 0x00003 1
где:
-
PD1
: начальная позиция каталога страниц процесса 1 в ОЗУ. -
PT1
иPT2
: начальная позиция таблицы страниц 1 и таблицы страниц 2 для процесса 1 в ОЗУ.
Итак, в этом примере каталог страниц и таблица страниц могут быть сохранены в ОЗУ примерно так:
----------------> 0xFFFFFFFF
----------------> PT2 + 0x3FF * L
Page Table 1
----------------> PT2
----------------> PD1 + 0x3FF * L
Page Directory 1
----------------> PD1
----------------> PT1 + 0x3FF * L
Page Table 2
----------------> PT1
----------------> 0x0
Постройте линейный адрес 0x00801004
.
Предположим, что cr3 = PD1
, то есть указывает на только что описанный каталог страниц.
В двоичном формате линейный адрес:
0 0 8 0 1 0 0 4
0000 0000 1000 0000 0001 0000 0000 0100
Группировка как 10 | 10 | 12
дает:
0000000010 0000000001 000000000100
0x2 0x1 0x4
который дает:
- запись в каталоге страницы = 0x2
- запись в таблице таблицы = 0x1
- offset = 0x4
Итак, аппаратное обеспечение ищет запись 2 каталога страницы.
В таблице каталога страниц указано, что таблица страниц находится в 0x80000 * 4K = 0x80000000
. Это первый доступ к оперативной памяти процесса.
Поскольку запись в таблице страниц 0x1
, аппаратное обеспечение просматривает запись 1 таблицы страниц в 0x80000000
, которая сообщает ему, что физическая страница находится по адресу 0x0000C * 4K = 0x0000C000
. Это второй доступ к оперативной памяти процесса.
Наконец, аппаратное обеспечение подкачки добавляет смещение, а конечный адрес - 0x0000C004
.
Другие примеры переведенных адресов:
linear 10 10 12 split physical
-------- --------------- ----------
00000001 000 000 001 00001001
00001001 000 001 001 page fault
003FF001 000 3FF 001 00005001
00400000 001 000 000 page fault
00800001 002 000 001 0000A001
00801008 002 001 008 0000C008
00802008 002 002 008 page fault
00B00001 003 000 000 page fault
Неисправности страниц возникают, если нет ни записи каталога страницы, ни записи таблицы страниц.
Если ОС хочет запустить другой процесс одновременно, это даст второму процессу отдельный каталог страниц и привяжет этот каталог к отдельным таблицам страниц.
64-разрядные архитектуры
64 бита по-прежнему являются слишком большим адресом для текущих размеров ОЗУ, поэтому большинство архитектур будут использовать меньше бит.
x86_64 использует 48 бит (256 TiB), а устаревший режим PAE уже позволяет 52-битные адреса (4 PiB).
12 из этих 48 бит уже зарезервированы для смещения, которое оставляет 36 бит.
Если используется подход с 2 уровнями, лучшим разделом будет два 18-разрядных уровня.
Но это означало бы, что каталог страниц имел бы записи 2^18 = 256K
, которые занимали бы слишком много ОЗУ: рядом с одноуровневым поисковым вызовом для 32-битных архитектур!
Поэтому 64-разрядные архитектуры создают еще более высокие уровни страниц, обычно 3 или 4.
x86_64 использует 4 уровня в схеме 9 | 9 | 9 | 12
, так что верхний уровень обрабатывает только записи 2^9
более высокого уровня.
PAE
Расширение физического адреса.
С 32 битами можно решить только 4 ГБ ОЗУ.
Это стало ограничением для больших серверов, поэтому Intel представила механизм PAE для Pentium Pro.
Чтобы устранить проблему, Intel добавила 4 новые адресные строки, чтобы можно было решить проблему 64 ГБ.
Структура таблицы страниц также изменяется, если PAE включен. Точный способ его изменения зависит от того, включен или выключен PSE погоды.
PAE включается и выключается через бит PAE
cr4
.
Даже если общая адресуемая память составляет 64 ГБ, отдельный процесс по-прежнему способен использовать до 4 ГБ. Однако ОС может устанавливать различные процессы на разных блоках 4 ГБ.
PSE
Расширение размера страницы.
Позволяет для страниц быть 4M (или 2M, если PAE включен) длиной, а не 4K.
PSE включается и выключается через бит PAE
cr4
.
Схемы таблиц страниц PAE и PSE
Если активны PAE и PSE, используются разные схемы уровня персонального вызова:
-
нет PAE и нет PSE:
10 | 10 | 12
-
нет PAE и PSE:
10 | 22
.22 - смещение на странице 4 Мб, так как 22 бита адресуют 4 Мб.
-
PAE и нет PSE:
2 | 9 | 9 | 12
Причина проектирования, почему 9 используется дважды вместо 10, состоит в том, что теперь записи больше не могут вписываться в 32 бита, которые были заполнены 20 адресными битами и 12 значащими или зарезервированными битами флага.
Причина в том, что 20 битов недостаточно, чтобы представить адрес таблиц страниц: теперь требуется 24 бита из-за 4 дополнительных проводов, добавленных в процессор.
Поэтому дизайнеры решили увеличить размер записи до 64 бит, и чтобы они вписывались в одну страницу таблицы, необходимо уменьшить количество записей до 2 ^ 9 вместо 2 ^ 10.
Стартовый 2 - это новый уровень страницы, называемый таблицей указателей страниц (PDPT), поскольку он указывает на каталоги страниц и заполняет 32-разрядный линейный адрес. PDPT также имеют ширину 64 бит.
cr3
теперь указывает на PDPT, которые должны быть на четвертом 4 ГБ памяти и выровнены по 32-разрядным кратным для эффективности адресации. Это означает, что теперьcr3
имеет 27 значащих битов вместо 20: 2 ^ 5 для 32 кратных * 2 ^ 27 для завершения 2 ^ 32 первых 4 ГБ. -
PAE и PSE:
2 | 9 | 21
Дизайнеры решили оставить поле шириной 9 бит, чтобы он вписывался в одну страницу.
Это оставляет 23 бита. Оставляя 2 для PDPT, чтобы сохранить вещи одинаковыми с корпусом PAE без PSE, оставляет 21 для смещения, а это означает, что страницы имеют ширину 2M вместо 4M.
TLB
Буфер перевода Lookahead (TLB) - это кеш для адресов подкачки.
Поскольку это кеш, он разделяет многие проблемы с дизайном кэша процессора, такие как уровень ассоциативности.
В этом разделе описывается упрощенный полностью ассоциативный TLB с четырьмя отдельными адресами. Обратите внимание, что, как и другие кеши, реальные TLB обычно не являются полностью ассоциативными.
Основная операция
После перевода между линейным и физическим адресом, он сохраняется в TLB. Например, TLB с 4 входами начинается в следующем состоянии:
valid linear physical
------ ------- ---------
> 0 00000 00000
0 00000 00000
0 00000 00000
0 00000 00000
>
указывает текущую запись, которую нужно заменить.
и после того, как линейный адрес страницы 00003
переведен на физический адрес 00005
, TLB становится:
valid linear physical
------ ------- ---------
1 00003 00005
> 0 00000 00000
0 00000 00000
0 00000 00000
и после второго перевода 00007
в 00009
он становится:
valid linear physical
------ ------- ---------
1 00003 00005
1 00007 00009
> 0 00000 00000
0 00000 00000
Теперь, если 00003
нужно снова перевести, аппаратное обеспечение сначала ищет TLB и обнаруживает его адрес с единственным доступом к оперативной памяти 00003 --> 00005
.
Конечно, 00000
не относится к TLB, так как никакая допустимая запись не содержит 00000
в качестве ключа.
Политика замены
Когда заполняется TLB, старые адреса перезаписываются. Как и для кэша CPU, политика замены является потенциально сложной операцией, но простая и разумная эвристика заключается в удалении последней используемой записи (LRU).
С LRU, начиная с состояния:
valid linear physical
------ ------- ---------
> 1 00003 00005
1 00007 00009
1 00009 00001
1 0000B 00003
добавление 0000D -> 0000A
даст:
valid linear physical
------ ------- ---------
1 0000D 0000A
> 1 00007 00009
1 00009 00001
1 0000B 00003
CAM
Использование TLB делает перевод быстрее, потому что исходный перевод занимает один доступ на уровень TLB, что означает 2 на простой 32-битной схеме, но 3 или 4 на 64-битных архитектурах.
TLB обычно реализуется как дорогостоящий тип оперативной памяти, называемый контент-адресной памятью (CAM). CAM реализует ассоциативную карту на оборудовании, то есть структуру, которая задает ключ (линейный адрес), извлекает значение.
Отображения также могут быть реализованы на адресах ОЗУ, но для сопоставлений CAM может потребоваться гораздо меньше записей, чем отображение ОЗУ.
Например, карта, в которой:
- оба ключа и значения имеют 20 бит (в случае простых схем персонального вызова)
- необходимо сохранить не более 4 значений
может храниться в TLB с 4 входами:
linear physical
------- ---------
00000 00001
00001 00010
00010 00011
FFFFF 00000
Однако для реализации этого с ОЗУ необходимо иметь 2 ^ 20 адресов:
linear physical
------- ---------
00000 00001
00001 00010
00010 00011
... (from 00011 to FFFFE)
FFFFF 00000
что будет еще дороже, чем использование TLB.
Недействительные записи
Когда cr3
изменяется, все записи TLB являются недействительными, потому что будет использоваться новая таблица страниц для нового процесса, поэтому маловероятно, чтобы какая-либо из старых записей имела какое-либо значение.
x86 также предлагает команду invlpg
, которая явно делает недействительной отдельную запись TLB. Другие архитектуры предлагают еще больше инструкций для недействительных записей TLB, таких как аннулирование всех записей в заданном диапазоне.
Некоторые процессоры x86 выходят за рамки требований спецификации x86 и обеспечивают большую согласованность, чем это гарантирует между изменением записи в таблице страниц и ее использованием, когда она была 't уже кэшируется в TLB. Видимо, Windows 9x полагалась на это для правильности, но современные процессоры AMD не обеспечивают согласованных переходов по страницам. Процессоры Intel делают, хотя им приходится обнаруживать ошибочные предположения. Пользуясь этим, возможно, это плохая идея, поскольку, вероятно, не так много, и большой риск вызвать тонкие чувствительные к времени проблемы, которые будет трудно отлаживать.
Использование ядра Linux
Ядро Linux широко использует пейджинговые функции x86 для быстрого переключения процессов с небольшой фрагментацией данных.
В v4.2
найдите под arch/x86/
:
-
include/asm/pgtable*
-
include/asm/page*
-
mm/pgtable*
-
mm/page*
Кажется, что не существует структур, предназначенных для представления страниц, особенно интересны только макросы: include/asm/page_types.h
. Выдержки:
#define _PAGE_BIT_PRESENT 0 /* is present */
#define _PAGE_BIT_RW 1 /* writeable */
#define _PAGE_BIT_USER 2 /* userspace addressable */
#define _PAGE_BIT_PWT 3 /* page write through */
arch/x86/include/uapi/asm/processor-flags.h
определяет cr0
и, в частности, битовую позицию PG
:
#define X86_CR0_PG_BIT 31 /* Paging */
Библиография
Free:
-
rutgers-pxk-416 глава "Управление памятью: лекции"
Хороший исторический обзор методов организации памяти, используемых более старой ОС.
Несвободное:
-
bovet05 глава "Адресация памяти"
Разумное введение в адресацию памяти x86. Отсутствуют некоторые хорошие и простые примеры.