Ostep глава 18. Paging: Introduction - перевод

Paging: Introduction

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

Таким образом, возможно, стоит рассмотреть второй подход: нарезать пространство на куски фиксированного размера. В виртуальной памяти мы называем эту идею пейджингом (paging), и она восходит к ранней и важной системе - Атласу [KE+62, L 78]. Вместо того чтобы разбивать адресное пространство процесса на некоторое количество логических сегментов переменного размера (например, код, куча, стек), мы разделяем его на блоки фиксированного размера, каждый из которых мы называем страницей. Соответственно, мы рассматриваем физическую память как массив слотов фиксированного размера, называемых фреймами страниц; каждый из этих фреймов может содержать одну страницу виртуальной памяти. Наша задача:

ЗАДАЧА:
КАК ВИРТУАЛИЗИРОВАТЬ ПАМЯТЬ С ПОМОЩЬЮ СТРАНИЦ
Как мы можем виртуализировать память с помощью страниц, чтобы избежать проблем сегментации? Каковы основные техники? Как сделать так, чтобы эти методы работали хорошо, с минимальными пространственными и временными накладными расходами?

Обзор и простой пример

Чтобы сделать этот подход более понятным, давайте проиллюстрируем его простым примером. На рис. 18.1 представлен пример крошечного адресного пространства, всего 64 байта, с четырьмя 16-байтовыми страницами (виртуальные страницы 0, 1, 2 и 3). Реальные адресные пространства, конечно, намного больше, обычно 32 бита и, следовательно, 4 ГБ адресного пространства или даже 64 бита*; в книге мы часто будем использовать крошечные примеры, чтобы облегчить их усвоение.

* 64-битное адресное пространство трудно себе представить, настолько оно удивительно велико. Аналогия может помочь: если вы думаете о 32-битном адресном пространстве размером с теннисный корт, 64-битное адресное пространство примерно равно размеру Европы(!).

Рисунок 18.1 - Простое 64 битное пространство
Рисунок 18.1 - Простое 64 битное пространство
Рисунок 18.1 - Простое 64 битное пространство

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

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

Рисунок 18.2 - 64 битное адресное пространство в 128 битной физической памяти
Рисунок 18.2 - 64 битное адресное пространство в 128 битной физической памяти
Рисунок 18.2 - 64 битное адресное пространство в 128 битной физической памяти

Еще одним преимуществом является простота управления свободным пространством, которую обеспечивает пейджинг. Например, когда ОС хочет поместить наше крошечное 64-байтовое адресное пространство в нашу восьмистраничную физическую память, она просто находит четыре свободные страницы; возможно, ОС хранит свободный список всех свободных страниц для этого и просто захватывает первые четыре свободные страницы из этого списка. В этом примере ОС поместила виртуальную страницу 0 адресного пространства (AS) в физический кадр 3, виртуальную страницу 1 AS в физический кадр 7, страницу 2 в кадр 5 и страницу 3 в кадр 2. Фреймы страниц 1, 4 и 6 в настоящее время свободны.

Чтобы записать, где каждая виртуальная страница адресного пространства помещается в физическую память, операционная система обычно хранит структуру данных для каждого процесса, известную как таблица страниц. Основная роль таблицы страниц состоит в том, чтобы хранить переводы адресов для каждой из виртуальных страниц адресного пространства, тем самым давая нам знать, где в физической памяти находится каждая страница. Для нашего простого примера (рис. 18.2) таблица страниц, таким образом, будет иметь следующие четыре записи: (Виртуальная страница 0 → Физический кадр 3), (VP 1 → PF 7), (VP 2 → PF 5) и (VP 3 → PF 2).

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

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

movl <virtual address>, %eax

В частности, давайте обратим внимание на явную загрузку данных из адреса <virtual address> в регистр eax (и, таким образом, проигнорируем выборку инструкций, которая должна была произойти ранее).

Чтобы перевести этот виртуальный адрес, сгенерированный процессом, мы должны сначала разделить его на два компонента: номер виртуальной страницы (VPN) и смещение внутри страницы. В этом примере, поскольку виртуальное адресное пространство процесса составляет 64 байта, нам нужно всего 6 бит для нашего виртуального адреса (2^6 = 64). Таким образом, наш виртуальный адрес можно концептуализировать следующим образом:

Ostep глава 18. Paging: Introduction - перевод

На этой диаграмме Va5 является битом самого высокого порядка виртуального адреса, а Va0-битом самого низкого порядка. Поскольку мы знаем размер страницы (16 байт), мы можем далее разделить виртуальный адрес следующим образом:

Ostep глава 18. Paging: Introduction - перевод

Размер страницы составляет 16 байт в 64-байтовом адресном пространстве; таким образом, мы должны иметь возможность выбрать 4 страницы, и верхние 2 бита адреса делают именно это. Таким образом, у нас есть 2-битный номер виртуальной страницы (VPN). Остальные биты говорят нам, какой байт страницы нас интересует, в данном случае 4 бита; мы называем это смещением.

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

movl 21, %eax

Превратив "21” в двоичную форму, мы получаем “010101”, и таким образом мы можем изучить этот виртуальный адрес и посмотреть, как он распадается на номер виртуальной страницы (VPN) и смещение:

Ostep глава 18. Paging: Introduction - перевод

Таким образом, виртуальный адрес “21” находится на 5-м (“0101” - м) байте виртуальной страницы “01” (или 1). С помощью нашего виртуального номера страницы мы теперь можем индексировать нашу таблицу страниц и найти, в каком физическом фрейме находится виртуальная страница 1. В таблице страниц выше физический номер кадра (PFN) (также иногда называемый физическим номером страницы или PPN) равен 7 (двоичный 111). Таким образом, мы можем перевести этот виртуальный адрес, заменив VPN на PFN, а затем произвести загрузку в физическую память (рис. 18.3).

Рисунок 18.3 - Процесс трансляции адресов
Рисунок 18.3 - Процесс трансляции адресов
Рисунок 18.3 - Процесс трансляции адресов
Рисунок 18.4 - Пример: Таблица страниц в физической памяти ядра
Рисунок 18.4 - Пример: Таблица страниц в физической памяти ядра
Рисунок 18.4 - Пример: Таблица страниц в физической памяти ядра

Обратите внимание, что смещение остается неизменным (т. е. оно не переводится), потому что смещение просто говорит нам, какой байт внутри страницы мы хотим. Наш конечный физический адрес-1110101 (117 в десятичной системе счисления), и именно оттуда мы хотим, чтобы наша система извлекала данные (рис. 18.2).

Имея в виду этот основной обзор, теперь мы можем задать (и, надеюсь, ответить на них) несколько основных вопросов, которые могут возникнуть у вас о пейджинге. Например, где хранятся эти таблицы страниц? Каково типичное содержание таблицы страниц и насколько велики эти таблицы? Делает ли пейджинг систему (слишком) медленной? Ответы на эти и другие заманчивые вопросы, по крайней мере частично, даны в нижеследующем тексте. Читайте дальше!

Где хранятся таблицы страниц?

Таблицы страниц могут быть ужасно большими, намного больше, чем таблица малых сегментов или пара base/bounds, которую мы обсуждали ранее. Например, представьте себе типичное 32-битное адресное пространство со страницами по 4 КБ каждая. Этот виртуальный адрес разделяется на 20-битный VPN и 12-битное смещение (напомним, что для размера страницы 1 КБ потребуется 10 бит, и просто добавьте еще два, чтобы получить 4 КБ).

20-битный VPN подразумевает, что существует 2^20 трансляций адресов, которыми ОС должна управлять для каждого процесса (это примерно миллион); предполагая, что нам нужно 4 байта на запись таблицы страниц (PTE) для хранения физического перевода плюс любые другие полезные вещи, мы получаем огромные 4 МБ памяти, необходимые для каждой таблицы страниц! Это довольно много. Теперь представьте, что запущено 100 процессов: это означает, что ОС потребуется 400 МБ памяти только для всех этих переводов адресов! Даже в современную эпоху, когда машины имеют гигабайты памяти, кажется немного сумасшедшим использовать большой кусок ее только для переводов, не так ли? И мы даже не будем думать о том, насколько велика такая таблица страниц для 64-битного адресного пространства; это было бы слишком ужасно и, возможно, полностью отпугнуло бы вас.

СТРУКТУРА ДАННЫХ — ТАБЛИЦА СТРАНИЦ
Одной из важнейших структур данных в подсистеме управления памятью современной ОС является таблица страниц. В общем случае таблица страниц хранит переводы виртуальных адресов в физические, позволяя системе знать, где на самом деле находится каждая страница адресного пространства в физической памяти. Поскольку каждое адресное пространство требует таких переводов, в системе обычно имеется одна таблица страниц для каждого процесса. Точная структура таблицы страниц либо определяется аппаратным обеспечением (старые системы), либо может более гибко управляться ОС (современные системы).

Поскольку таблицы страниц очень велики, мы не держим в MMU специального встроенного оборудования для хранения таблицы страниц текущего процесса. Вместо этого мы храним таблицу страниц для каждого процесса где-то в памяти. Предположим сейчас, что таблицы страниц находятся в физической памяти, которой управляет ОС; позже мы увидим, что большая часть самой памяти ОС может быть виртуализирована, и, следовательно, таблицы страниц могут храниться в виртуальной памяти ОС (и даже заменяться на диск), но сейчас это слишком запутанно, поэтому мы проигнорируем это. На рис. 18.4 изображена таблица страниц в памяти ОС; видите там крошечный набор переводов?

Что на самом деле находится в таблице страниц?

Давайте немного поговорим об организации таблицы страниц. Таблица страниц-это просто структура данных, которая используется для сопоставления виртуальных адресов (или, на самом деле, номеров виртуальных страниц) с физическими адресами (номерами физических фреймов). Таким образом, любая структура данных может работать. Простейшая форма называется линейной таблицей страниц, которая представляет собой просто массив. ОС индексирует массив по номеру виртуальной страницы (VPN) и ищет запись таблицы страниц (PTE) по этому индексу, чтобы найти нужный номер физического кадра (PFN). На данный момент мы примем эту простую линейную структуру; в последующих главах мы будем использовать более продвинутые структуры данных, чтобы помочь решить некоторые проблемы с подкачкой.

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

Рис. 18.5 - Запись в таблице страниц характерная для архитектуры x86 (PTE)
Рис. 18.5 - Запись в таблице страниц характерная для архитектуры x86 (PTE)
Рис. 18.5 - Запись в таблице страниц характерная для архитектуры x86 (PTE)

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

Есть еще пара важных моментов, но пока мы не будем о многом говорить. Присутствующий бит указывает, находится ли эта страница в физической памяти или на диске (т. е. была выгружена в файл подкачки (ориг. - it has been swapped out)). Мы поймем этот механизм дальше, когда изучим, как подкачивать части адресного пространства на диск для поддержки адресных пространств, которые больше физической памяти; подкачка позволяет ОС освобождать физическую память, перемещая редко используемые страницы на диск. Также часто встречается грязный бит, указывающий, была ли страница изменена с тех пор, как она была введена в память.

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

На рис. 18.5 показан пример записи таблицы страниц из архитектуры x86 [I09]. Он содержит текущий бит (P); бит чтения/записи (R/W), который определяет, разрешена ли запись на эту страницу; бит пользователя/супервизора (U/S), который определяет, могут ли процессы пользовательского режима получить доступ к странице; несколько битов (PWT, PCD, PAT и G), которые определяют, как работает аппаратное кэширование для этих страниц; бит доступа (A) и грязный бит (D); и, наконец, сам номер кадра страницы (PFN).

Подробнее о поддержке подкачки x86 читайте в Руководстве по архитектуре Intel [I09]. Однако имейте в виду, что чтение таких руководств, хотя и довольно информативно (и, безусловно, необходимо для тех, кто пишет код для использования таких таблиц страниц в ОС), поначалу может быть сложным. Требуется немного терпения и много желания.

В СТОРОНЕ: ПОЧЕМУ НЕТ БИТА ВАЛИДНОСТИ?
Вы можете заметить, что в примере Intel нет отдельных битов битов валидности и present бита, а есть только present бит (P). Если этот бит установлен (P=1), это означает, что страница одновременно присутствует и действительна. Если нет (P=0), это означает, что страница может отсутствовать в памяти (но является допустимой) или может быть недействительной. Доступ к странице с P=0 вызовет ловушку (trap) для ОС; затем ОС должна использовать дополнительные структуры, которые она хранит, чтобы определить, является ли страница действительной (и, следовательно, возможно, должна быть подкачена обратно) или нет (и, таким образом, программа пытается получить доступ к памяти незаконно). Такая рассудительность распространена в аппаратных средствах, которые часто просто предоставляют минимальный набор функций, на основе которых ОС может построить полный сервис.

Paging - всё также медленно

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

Ostep глава 18. Paging: Introduction - перевод

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

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

Ostep глава 18. Paging: Introduction - перевод

В нашем примере маска VPN будет установлена на 0x30 (hex 30 или binary 110000), которая выбирает биты VPN из полного виртуального адреса; SHIFT установлен на 4 (количество битов в смещении), так что мы перемещаем биты VPN вниз, чтобы сформировать правильный целочисленный номер виртуальной страницы. Например, с виртуальным адресом 21 (010101), наложение маски превращает это значение в 010000; сдвиг превращает его в 01 или виртуальную страницу 1, по желанию. Затем мы используем это значение в качестве индекса в массиве PTE, на который указывает базовый регистр таблицы страниц.

Как только этот физический адрес известен, аппаратное обеспечение может извлечь PTE из памяти, извлечь PFN и объединить его со смещением от виртуального адреса, чтобы сформировать желаемый физический адрес. В частности, вы можете думать о том, что PFN сдвигается влево сдвигом, а затем побитовым ИЛИ со смещением, чтобы сформировать окончательный адрес следующим образом:

Ostep глава 18. Paging: Introduction - перевод

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

Рис. 18.6 - Доступ к памяти с использованием пейджинга
Рис. 18.6 - Доступ к памяти с использованием пейджинга
Рис. 18.6 - Доступ к памяти с использованием пейджинга

Подводя итог, мы теперь опишем исходный протокол для того, что происходит на каждой ссылке памяти. На рис. 18.6 показан подход. Для каждой ссылки на память (будь то выборка инструкции или явная загрузка или хранение) подкачка требует, чтобы мы выполнили одну дополнительную ссылку на память, чтобы сначала получить перевод из таблицы страниц. Это большая работа! Дополнительные ссылки на память обходятся дорого, и в этом случае процесс, скорее всего, замедлится в два или более раз.

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

Трасировка памяти

Перед закрытием мы теперь проследим простой пример доступа к памяти, чтобы продемонстрировать все результирующие обращения к памяти, которые происходят при использовании подкачки. Фрагмент кода (на языке Си, в файле array.c), который нас интересует, выглядит следующим образом:

Ostep глава 18. Paging: Introduction - перевод

Мы компилируем array.c и запускаем его со следующими командами:

Ostep глава 18. Paging: Introduction - перевод

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

Ostep глава 18. Paging: Introduction - перевод

Код, если вы немного знаете x86, на самом деле довольно прост для понимания*. Первая команда перемещает нулевое значение (показанное как $0x0) в адрес виртуальной памяти расположения массива; этот адрес вычисляется путем взятия содержимого %edi и добавления к нему %eax, умноженного на четыре. Таким образом, %edi содержит базовый адрес массива, в то время как %eax содержит индекс массива (i); мы умножаем на четыре, потому что массив представляет собой массив целых чисел, каждый из которых имеет размер четыре байта.

*Мы немного обманываем здесь, предполагая, что каждая инструкция имеет размер четыре байта для простоты; на самом деле инструкции x86 имеют переменный размер

Вторая команда увеличивает индекс массива, хранящийся в %eax, а третья команда сравнивает содержимое этого регистра с шестнадцатеричным значением 0x03e8 или десятичным 1000. Если сравнение показывает, что два значения еще не равны (что и проверяет команда jne), четвертая команда возвращается к началу цикла.

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

Для этого примера мы предполагаем виртуальное адресное пространство размером 64 КБ (нереально маленькое). Мы также предполагаем, что размер страницы составляет 1 КБ.

Все, что нам сейчас нужно знать - это содержимое таблицы страниц и ее расположение в физической памяти. Предположим, что у нас есть линейная (массивная) таблица страниц и что она расположена по физическому адресу 1 КБ (1024).

Что касается его содержимого, то есть всего несколько виртуальных страниц, о которых нам нужно позаботиться, чтобы они были сопоставлены для этого примера. Во-первых, есть виртуальная страница, на которой живет код. Поскольку размер страницы составляет 1 КБ, виртуальный адрес 1024 находится на второй странице виртуального адресного пространства (VPN=1, так как VPN=0-это первая страница). Предположим, что эта виртуальная страница сопоставляется с физическим фреймом 4 (VPN 1 → PFN 4).

Далее идет сам массив. Его размер составляет 4000 байт (1000 целых чисел), и мы предполагаем, что он находится по виртуальным адресам от 40000 до 44000 (не включая последний байт). Виртуальные страницы для этого десятичного диапазона равны VPN=39 ... VPN=42. Таким образом, нам нужны сопоставления для этих страниц. Предположим, что эти виртуальные и физические сопоставления для примера: (VPN 39 → PFN 7), (VPN 40 → PFN 8), (VPN 41 → PFN 9), (VPN 42 → PFN 10).

Рис. 18.7 - Виртуальная и физическая трасировка памяти
Рис. 18.7 - Виртуальная и физическая трасировка памяти
Рис. 18.7 - Виртуальная и физическая трасировка памяти

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

Весь процесс для первых пяти итераций цикла показан на рис. 18.7. Самый нижний график показывает ссылки на память команд по оси y черным цветом (с виртуальными адресами слева и фактическими физическими адресами справа); средний график показывает доступ к массиву темно-серым цветом (опять же с виртуальным слева и физическим справа); наконец, самый верхний график показывает доступ к памяти таблицы страниц светло-серым цветом (только физический, так как таблица страниц в этом примере находится в физической памяти). Ось x для всей трассировки показывает доступ к памяти в течение первых пяти итераций цикла; существует 10 обращений к памяти на цикл, который включает в себя четыре выборки команд, одно явное обновление памяти и пять обращений к таблице страниц для перевода этих четырех выборок и одно явное обновление.

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

Это только что был самый простой пример (всего несколько строк кода на языке Си), и все же вы уже можете почувствовать сложность понимания реального поведения памяти реальных приложений. Не волнуйтесь: это определенно становится хуже, потому что механизмы, которые мы собираемся ввести, только усложняют эту и без того сложную машину. Извините*!

*На самом деле мы не сожалеем. Но мы сожалеем о том, что не сожалеем, если это имеет смысл

Выводы

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

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

[KE+62] “One-level Storage System” by T. Kilburn, D.B.G. Edwards, M.J. Lanigan, F.H. Sumner. IRE Trans. EC-11, 2, 1962. Reprinted in Bell and Newell, “Computer Structures: Readings and Examples”. McGraw-Hill, New York, 1971. The Atlas pioneered the idea of dividing memory into fixed-sized pages and in many senses was an early form of the memory-management ideas we see in modern computer systems.

[I09] “Intel 64 and IA-32 Architectures Software Developer’s Manuals” Intel, 2009. Available: http://www.intel.com/products/processor/manuals. In particular, pay attention to “Volume 3A: System Programming Guide Part 1” and “Volume 3B: System Programming Guide Part 2”.

[L78] “The Manchester Mark I and Atlas: A Historical Perspective” by S. H. Lavington. Communications of the ACM, Volume 21:1, January 1978. This paper is a great retrospective of some of the history of the development of some important computer systems. As we sometimes forget in the US, many of these new ideas came from overseas.