Модель разбиения на страницы в Linux
Разбиение на страницы в Linux аналогично обычному разбиению, но x86-архитектура имеет трехуровневый табличный механизм, состоящий из (рис.2):
Рис. 2. Три уровня разбиения на страницы
· Page Global Directory (pgd - глобальный каталог страниц), абстрактный верхний уровень многоуровневых таблиц страниц. Каждый уровень таблицы страниц имеет дело с различными размерами памяти - этот глобальный каталог может работать с областями размером 4 MB. Каждая запись будет являться указателем на более низкоуровневую таблицу каталогов меньшего размера, то есть pgd - это каталог таблиц страниц. Когда код проходит эту структуру (это делают некоторые драйверы), говорится, что выполняется "проход" по таблицам страниц.
· Page Middle Directory (pmd - промежуточный каталог страниц), промежуточный уровень таблиц страниц. На x86-архитектуре pmd не представлен аппаратно, но входит в pgd в коде ядра.
· Page Table Entry (pte - запись таблицы страниц), нижний уровень, работающий со страницами напрямую (ищите PAGE_SIZE), является значением, которое содержит физический адрес страницы вместе со связанными битами, указывающими, например, что запись корректна, и соответствующая страница присутствует в реальной памяти.
Эта трехуровневая схема разбиения на страницы также реализована в Linux для поддержки областей памяти больших размеров. Если поддержка больших областей памяти не требуется, вы можете вернуться к двухуровневой схеме, установив pmd в "1".
Эти уровни оптимизируются во время компиляции, разрешая второй и третий уровни (с использованием того же самого набора кода) простым разрешением или запретом промежуточного каталога. 32-битные процессоры используют pmd-разбиение, а 64-битые используют pgd-разбиение.
В 64-битных процессорах:
· 21 бит MSB не используются
· 13 бит LSB представлены смещением страницы
· Оставшиеся 30 бит разделены на
o 10 бит для Page Table
o 10 бит для Page Global Directory
o 10 бит для Page Middle Directory
Как видно из архитектуры, для адресации фактически используются 43 бита. То есть, на 64-битных процессорах реально доступно 2 в степени 43 ячеек виртуальной памяти.
Каждый процесс имеет свой собственный набор каталогов страниц и таблиц страниц. Для обращения к страничному фрейму с реальными пользовательскими данными операционная система начинает с загрузки (на x86-архитектурах) pgd в регистр cr3. Linux сохраняет в TSS-сегменте содержимое регистра cr3 и затем загружает другое значение из TSS-сегмента в регистр cr3 каждый раз во время выполнения нового процесса в CPU. В результате модуль разбиения на страницы ссылается на корректный набор таблиц страниц.
Каждая запись в таблице pgd указывает на страничный фрейм, содержащий массив записей pmd, которые, в свою очередь, указывают на страничный фрейм, содержащий pte, который, наконец, указывает на страничный фрейм, содержащий пользовательские данные. Если искомая страница находится в файле подкачки, в таблице pte сохраняется запись файла подкачки, которая используется (при ошибке отсутствия страницы) при поиске страничного фрейма для загрузки в память.
На рисунке 3 показано, что мы добавляем смещение на каждом уровне таблиц страниц для отображения на соответствующую запись страничного фрейма. Эти смещения мы получаем, разбив линейный адрес, принятый из модуля сегментации. Для разбиения линейного адреса, соответствующего каждому компоненту таблицы страниц, в ядре используются различные макросы. Не рассматривая детально эти макросы, давайте посмотрим на схему разбиения линейного адреса.
Рисунок 3. Линейные адреса имеют различные размеры
Резервные страничные фреймы
Linux резервирует несколько станичных фреймов для кода ядра и структур данных. Эти страницы никогда не сбрасываются в файл подкачки на диск. Линейные адреса с 0x0 до 0xc0000000 (PAGE_OFFSET) используются кодом пользователя и кодом ядра. Адреса с PAGE_OFFSET до 0xffffffff используются кодом ядра.
Это означает, что из 4 GB только 3 GB доступны для пользовательского приложения.
Как разрешается разбиение на страницы
Механизм разбиения на страницы, используемый процессами Linux, имеет две фазы:
· Во время начальной загрузки система устанавливает таблицу страниц для 8 MB физической памяти.
· Затем вторая фаза завершает отображение для оставшейся физической памяти.
В фазе первоначальной загрузки за инициализацию разбиения на страницы отвечает вызов startup_32(). Эта функция реализована в файле arch/i386/kernel/head.S. Отображение этих 8 MB происходит с адреса выше PAGE_OFFSET. Инициализация начинается со статически определенного во время компиляции массива, называемого swapper_pg_dir. Во время компиляции он размещается по конкретному адресу (0x00101000).
Определяются записи для двух таблиц, определенных в коде статически - pg0 и pg1. Размер этих страничных фреймов по умолчанию равен 4 KB, если не установлен бит расширения размера страниц (page size extension) (дополнительная информация по PSE находится в разделе "Расширенное разбиение на страницы"). Тогда размер каждой равен 4 MB. Адрес данных, указываемый глобальным массивом, сохраняется в регистре cr3, что, я полагаю, является первым этапом установки модуля разбиения на страницы для процессов Linux. Остальные страничные записи устанавливаются во второй фазе.
Вторая фаза реализована в методе paging_init().
Отображение RAM выполняется между PAGE_OFFSET и адресом, представляющим границу 4 GB (0xFFFFFFFF) в 32-битной x86-архитектуре. Это означает, что RAM размером примерно в 1 GB может быть отображена при загрузке Linux, и это происходит по умолчанию. Однако, если установить HIGHMEM_CONFIG, то физическая память размером более 1 GB может быть отображена для ядра - имейте в виду, что это временная мера. Это делается вызовом kmap().
Дата добавления: 2015-03-26; просмотров: 814;