Skip to main content

Системные вещи. Ответы Часть 3.

Виртуальная память

Что такое виртуальная память?

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

Применение механизма виртуальной памяти позволяет:

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

Как работает paging?

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

Поддержка такого режима присутствует в большинстве 32битных и 64битных процессоров.

Решаемые задачи

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

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

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

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

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

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

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

Физический адрес директории или же супер-директории загружен в один из управляющих регистров процессора.

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

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

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

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

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

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

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

PAE, x64, IOMMU?

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

Physical Address Extension (PAE) — режим работы встроенного блока управления памятью x86-совместимых процессоров, в котором используются 64-битные элементы таблиц страниц (из которых для адресации используются только 36 бит), c помощью которых процессор может адресовать 64 ГБ физической памяти (вместо 4 ГБ, адресуемых при использовании 32-разрядных таблиц), хотя каждая задача (программа) всё равно может адресовать максимум до 4 ГБ виртуальной памяти. Также, в новых моделях процессоров в PAE-режиме старший бит элемента таблицы страниц отвечает за запрет исполнения кода в странице, что затрудняет атаку по методу переполнения буфера.

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

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

Недостатками использования IOMMU по сравнению с прямой физической адресацией памяти в DMA запросах являются:

  • Некоторое ухудшение производительности из-за необходимости транслирования адресов и расходов на управление, например, проход по иерархии таблицы страниц.
  • Дополнительное потребление памяти для хранения таблиц отображения. Может быть уменьшен при использовании основных таблиц трансляции адресов процессора.

IOMMU используется для прямой работы виртуализованных операционных систем с оборудованием основной системы. Наличие IOMMU для таких комбинаций позволяет повысить безопасность, производительность и упростить реализацию виртуальной машины.

Что такое прерывание? Какие они бывают?

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

В зависимости от источника возникновения сигнала, прерывания делятся на:

  • аппаратные — события, которые исходят от внешних источников  и могут произойти в любой произвольный момент. Факт возникновения в системе такого прерывания трактуется как запрос на прерывание (IRQ);
  • внутренние — события в самом процессоре как результат нарушения каких-то условий при исполнении машинного кода: деление на ноль или переполнение стека, обращение к недопустимым адресам памяти или недопустимый код операции;
  • программные (частный случай внутреннего прерывания) — инициируются исполнением специальной инструкции в коде программы. Программные прерывания, как правило, используются для обращения к функциям встроенного программного обеспечения (firmware), драйверов и операционной системы.

Аппаратные прерывания в зависимости от возможности запрета делятся на:

  • маскируемые — прерывания, которые можно запрещать установкой соответствующих битов в регистре маскирования прерываний;
  • немаскируемые (Non-maskable interrupt, NMI) — обрабатываются всегда, независимо от запретов на другие прерывания. К примеру, такое прерывание может быть вызвано сбоем в микросхеме памяти.

Как происходит их обработка? Что со стеками?

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

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

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

На этом на сегодня всё, читайте продолжение в следующем посте.

Комментарий “Системные вещи. Ответы Часть 3.

  1. Вечер добрый, возник такой забавный вопрос, что и подвигло меня посетить Ваш блог в том числе. Вы рассказываете естественные вещи, просто такое ощущение, что какую- то простую вещь упускаю.
    Дело в том, что защита разделялась на защиту по сегментам и по страницам. Сегментирование в x64 не работает как таковое. Как операционные системы защищают в таком случае адресное пространство процессов одного уровня друг от друга ? Может что- то недочитал ранее…

    Сам работаю сопровожденцем и интересуюсь лишь в меру любопытства.
    Гуглить умею, читать на english- тоже.
    Может просто не правильно вопросы задавал ?

Добавить комментарий