, инженер-программист, IBM 24.01.2006 Изучите основы структуры памяти и управления памятью, обрисованные в этом вводном руководстве по модели памяти Linux®. В нем исследуется модуль управления сегментами и модель управления страницами памяти, а также (подробно) - пояс плотской памяти. Понимание моделей памяти, используемых в Linux, - это первоначальный шаг к освоению на более патетическом уровне структуры операционной системы Linux и ее реализации. В этой статье модели памяти Linux и управление памятью рассматриваются на ознакомительном уровне. Операционная система Linux использует монолитный подход, при коем определяется комплект примитивов или системных вызовов для реализации служб операционной системы, таковских ровно управление процессами, параллельная труд и управление памятью, в нескольких модулях, ишачащих в режиме супервизора. И алкая Linux в мишенях совместимости поддерживает модель модуля управления сегментами (segment control unit) ровно символическое понятие, она использует эту модель на минимальном уровне. Управление виртуальной памятью - логический степень между запросами памяти в приложениях и плотской памятью. Управление плотской памятью. Управление виртуальной памятью на уровне ядра (модуль ядра, занимающийся распределением памяти - компонент, какой пробует удоволить требования к памяти). Запрос может быть выполнен из ядра или из программы пользователя. Управление виртуальным адресным пространством. Свопинг и кэширование. настоящая статья призвана подсобить вам в освоении внутреннего устройства Linux с точки зрения управления памятью операционной системы. Рассматриваются вытекающие темы: совместная модель модуля управления сегментами и ее особенности в Linux. совместная модель управления страницами памяти и ее особенности в Linux. плотские детали области памяти. В данной статье не рассматриваются детально спросы управления памятью в ядре Linux, однако информация по всеобщей модели памяти и по работе с ней должна дать вам основу для дальнейшего изучения. Внимание в статье уделяется архитектуре x86, однако вы можете использовать эти материалы и с иными аппаратными реализациями. Логический адрес - адрес благорасположения ячеи памяти, какой может быть (а может и нет) связан непосредственно с телесным благоволением. Логический адрес обыкновенно используется при запросе информации из контроллера. Линейный адрес (или линейное адресное пространство ) - это память, адресация коей начинается с 0. всякий вытекающий байт адресуется вытекающим последовательным номером (0, 1, 2, 3 и т.д.) до гроба памяти. таково адресуют память большинство CPU не Intel-архитектуры. В Intel®-архитектурах используется сегментированное адресное пространство, в коем память разделяется на сегменты размером 64KB, а сегментный регистр вечно указывает на базовый адрес адресуемого сегмента. 32-битный порядок в этой архитектуре рассматривается ровно линейное адресное пространство, однако в нем тоже используются сегменты. физиологический адрес - адрес, представленный битами плотской адресной шины. физиологический адрес может выдаваться от логического; в этом случае модуль управления памятью транслирует логический адрес в физиологический. CPU использует два модуля для переустройства логического адреса в его физиологический эквивалент. первоначальный называется модулем сегментации (segmented unit), а прочий - модулем разделения на страницы (paging unit). выбрасывайте исследуем модель модуля управления сегментами. суть модели сегментации заключается в том, что память управляется при помощи комплекта сегментов. натурально, всякий сегмент располагает раздельное адресное пространство. Сегмент заключается из двух компонентов: Базовый адрес смысл длины , указывающее длину сегмента Сегментированный адрес тоже заключается из двух компонентов - с електора сегмента и смещения в сегменте . Селектор сегмента указывает на используемый сегмент (то жрать, значения базового адреса и длины), а компонент смещения указывает смещение от базового адреса для реального доступа к памяти. физиологический адрес реального месторасположения памяти изображает суммой значений смещения и базового адреса. Если смещение превышает длину сегмента, система генерирует нарушение защиты. всякий сегмент - это 16-битное поле, называемое идентификатором сегмента или селектором сегмента . Процессоры семейства x86 включают несколько программируемых регистров, величаемых сегментными, кои хранят эти селекторы сегментов. таковскими регистрами являются (сегмент кода), (сегмент стека). всякий идентификатор сегмента указывает сегмент, какой представлен 64-битным (8-байтным) дескриптором сегмента . Эти дескрипторы сегментов хранятся в GDT (глобальная таблица дескрипторов) и может быть также сохранена в LDT (локальная таблица дескрипторов). всякий один-одинешенек, когда селектор сегмента загружается в сегментный регистр, отвечающий дескриптор сегмента загружается из памяти в отвечающий непрограммируемый регистр CPU. всякий дескриптор сегмента располагает длину 8 байт и рисует один-одинешенек сегмент в памяти. Они хранятся в таблицах LDT или GDT. Запись элемента дескриптора сегмента заключает и указатель на первоначальный байт в связанном сегменте, представленном полем Base, и 20-битное смысл (поле Limit), какое рисует размер сегмента в памяти. Несколько прочих пустотелее включают особые атрибуты, эдакие ровно степень прерогатив и фрукт сегмента ( ). фрукт сегмента представляется в четырехбитном поле Type. Поскольку используются непрограммируемый регистр, к GDT или LDT не производится обращений до тех пор, покамест не будет выполнена передача логического адреса в физиологический. Это убыстряет работу с памятью. 13-битный индекс, указывающий на отвечающий дескриптор сегмента, содержащийся в GDT или LDT. Флаг TI (Table Indicator), какой указывает, в какой таблице будет дескриптор сегмента. Если флаг равновелик 0, дескриптор будет в GDT; если 1 - в LDT. RPL (request privilege level) определяет текущий степень прерогатив CPU при загрузке отвечающего селектора сегмента в регистр сегмента. Поскольку дескриптор сегмента располагает длину 8 байт, его сравнительный адрес в GDT или LDT вычисляется путем умножения самых значимых 13 битов селектора сегмента на 8. так, если GDT хранится по адресу 0x00020000, и поле Index, показанное селектором сегмента, равновелико 2, тогда адрес отвечающего дескриптора сегмента равновелико (2*8) + 0x00020000. Максимальное численность дескрипторов сегмента, какое может быть сохранено в GDT, равновелико (2^13 - 1) или 8191. На рисунке 3 показано графическое понятие вычисления линейного адреса из логического. А нынче посмотрим, каковы отличия в Linux? В Linux эта модель располагает небольшие отличия. Я уже вещал о том, что Linux использует модель сегментирования односторонне (в основном для совместимости). В Linux все сегментные регистры указывают на один-одинешенек и тот же диапазон адресов сегментов - иными словами, всякий использует один-одинешенек и тот же комплект линейных адресов. Это позволяет Linux использовать узкое число дескрипторов сегментов, то жрать, все дескрипторы могут храниться в GDT. Двумя преимуществами этой модели являются: Управление памятью проще при использовании всеми процессами одинаких значений сегментных регистров (когда они совместно используют один комплект линейных адресов). Может быть завоевана совместимость с большинством архитектур. кое-какие RISC-процессоры тоже поддерживают подобный односторонний метод сегментирования. На рисунке 4 показаны эти отличия. Рассмотрим детально всякий из них. Дескриптор сегмента кода ядра G (флаг единицы сегментирования) = 1 для размера сегмента, сформулированного в страницах Type = 0xa для сегмента кода, какой можно проглотить и выполнить Линейный адрес для этого сегмента равновелик 4 GB. S =1 и type = 0xa отмечают сегмент кода. Селектор будет в регистре . Доступ к отвечающему селектору сегмента в Linux осуществляется чрез макрос . Дескриптор сегмента настоящих ядра располагает аналогичные значения за исключением поля Type, смысл какого учреждено в 2. Это указывает на то, что сегмент изображает сегментом настоящих и селектор хранится в регистре . Доступ к отвечающему селектору сегмента в Linux осуществляется чрез макрос . Сегмент кода пользователя используется совместно всеми процессами в пользовательском режиме. отвечающий дескриптор сегмента, хранящийся в GDT, располагает вытекающие значения: Type = 0xa для сегмента кода, какой можно проглотить и выполнить . В дескрипторе сегмента настоящих пользователя жрать всего-навсего одно изменение по сравнению с прошлым дескриптором - поле Type учреждено в 2 и определяет сегмент настоящих, кои можно проглотить и записать. Доступ к отвечающему селектору сегмента в Linux осуществляется чрез макрос . Кроме этих дескрипторов сегментов GDT заключает два прибавочных дескриптора сегментов для всякого созданного процесса - для сегментов TSS и LDT. всякий дескриптор сегмента TSS указывает на отдельный процесс. TSS хранит информацию об аппаратном контексте для всякого CPU, какой принимает участие в переключении контекста. так, при переключении режимов x86 CPU получает адрес стека порядка ядра из TSS. всякий процесс располагает собственный собственный TSS-дескриптор, хранящийся в GDT. Значения этого дескриптора таковы: Base = &tss (адрес поля TSS дескриптора отвечающего процесса; так, ), какой обусловлен в файле schedule.h ядра Linux DPL = 0. Пользовательский порядок не обращается к TSS. Флаг G очищен Все процессы совместно используют сегмент LDT по умолчанию . По умолчанию он заключает нулевой дескриптор сегмента. Этот дескриптор сегмента LDT по умолчанию хранится в GDT. Сгенерированный ядром Linux LDT занимает 24 байта. По умолчанию вечно присутствуют три записи: (переменной, определяющей численность одновр/еменных процессов, поддерживаемых в Linux. Ее смысл по умолчанию в отправном коде ядра равновелико 512, что позволяет обладать 256 одновр/еменных подключений к одному экземпляру) необходимо для вычисления максимального числа разрешенных записей в GDT. Из 8192 дескрипторов сегментов Linux использует 6 дескрипторов сегментов, еще 4 прибавочных для APM-функций (функции расширенного управления питанием), а 4 записи в GDT остаются не использованными. таковским образом, конечное число мыслимых записей в GDT равновелико 8192 - 14, или 8180. В любой момент времени мы не можем обладать более чем 8180 записей в GDT, следовательно: отчего ? Потому что для всякого созданного процесса загружается не всего-навсего TSS-дескриптор, используемый для обслуживания переключений контекста, однако и LDT-дескриптор. Это ограничение числа процессов в x86-архитектуре глядело к Linux 2.2, однако после ядра 2.4 эта проблема была разрешена частично путем отказа от переключения аппаратного контекста (которое сооружало необходимым использование TSS), а также путем замены его переключением процесса. нынче, выбрасывайте рассмотрим страничную модель . Модуль управления страницами перестроит линейные адреса в плотские (см. рисунок 1). комплект линейных адресов группируется вкупе, образуя страницы. Эти линейные адреса непрерывны по своей натуре - модуль управления страницами отображает эти комплекты непрерывной памяти в отвечающий комплект непрерывных плотских адресов, называемых страничными фреймами . Обратите внимание на то, что модуль управления страницами рисует RAM распределенным на страничные фреймы фиксированного размера. лева доступа, найденные для страницы, будут действительны для группы линейных адресов, формирующих страницу Структура настоящих, отображающая эти страницы на страничные фреймы, именуется, таблицей страниц . Эти таблицы страниц хранятся в основной памяти и инициализируются ядром перед позволением работы модуля управления страницами. На рисунке 5 показана таблица страниц. Обратите внимание на то, что комплект адресов, содержащихся в Page1, отвечает найденному компл