Архитектура памяти
Сегментное преобразование пары селектор:смещение дает 32-битный линейный адрес (лежащий в диапазоне 4 Гб линейного адресного пространства). При этом линейный адрес совпадает со значением смещения виртуального адреса. Фактически, при такой организации памяти виртуальный и линейный адреса совпадают.
Наличие поля тип, определяющего возможность чтения/записи/исполнения кода в соответствующем сегменте может навести на мысль, что именно на этом уровне производится защита памяти от нецелевого использования. Например, при работе прикладной программы в пользовательском режиме ее код находится в сегменте с селектором 1b. Для этого сегмента разрешены операции чтения и исполнения. Используя селектор 1b, программа не сможет модифицировать свой собственный код.
Однако, как уже было сказано, для всех сегментов производится трансляция в одни и те же физические адреса. Поэтому при обращении к данным или стеку (селектор 23) прикладная программа обнаружит свой код по тому же смещению, что и для селектора 1b, причем режим доступа к сегменту позволяет производить чтение/запись. (При этом важно помнить: одно и то же смещение в разных адресных пространствах указывает на разную физическую память.) Таким образом, способ использования сегментации в ОС NT не обеспечивает защиту кода от нецелевого использования.
Далее задействуется механизм страничной организации памяти и переключения контекста памяти.
Каждый контекст памяти (адресное пространство процесса) представляется собственной таблицей трансляции линейного адреса (совпадающего с виртуальным) в физический.
Каждый элемент таблицы страниц содержит бит, указывающий на возможность доступа к странице из пользовательского режима. При этом все страницы доступны из режима ядра.
Кроме того, каждый элемент таблицы страниц содержит бит, указывающий на возможность записи в соответствующую страницу памяти.
Эти два бита используются для управления доступом к страницам памяти и формируют следующий набор правил:
- страница всегда может быть прочитана из режима ядра;
- на страницу может быть произведена запись из режима ядра, только если установлен бит разрешения записи;
- страница может быть прочитана из пользовательского режима, только если установлен бит доступа к странице из пользовательского режима;
- на страницу может быть произведена запись из пользовательского режима, если установлены оба бита (разрешение записи и доступ из пользовательского режима);
- если страница может быть прочитана, она может быть исполнена.
Страницы памяти с исполняемым кодом не будут иметь разрешения на запись, если не предпринять никаких дополнительных действий. Поэтому при попытке использования селектора данных для модификации кода будет сгенерирована исключительная ситуация.
Для упрощения организации памяти, для всех контекстов памяти NT осуществляет одинаковую трансляцию для некоторого диапазона виртуальных адресов. Таким образом, при переключении контекста памяти, то есть переходе на новую таблицу трансляции виртуального адреса в физический, некоторая часть элементов этой таблицы останется неизменной.
Это нужно для того, чтобы ядро операционной системы (компоненты ОС и драйвера) всегда располагалось по фиксированным виртуальным адресам вне зависимости от текущего контекста памяти. Таким неизменяемым диапазоном адресов являются верхние 2 Гб памяти.
Для защиты кода ОС соответствующие элементы таблицы трансляции виртуального адреса в физический помечены как недоступные из пользовательского режима.
Соответственно, диапазон виртуальных адресов 2-4 Гб называют системным адресным пространством (system address space), а диапазон 0-2 Гб – пользовательским адресным пространством (user address space).
Во избежание путаницы между терминами адресное пространство процесса и пользовательское/системное адресное пространство, где это возможно, вместо термина адресное пространство процесса мы будем пользоваться термином контекст памяти.
При этом возможная путаница вполне допустима, так как с точки зрения прикладного программиста пользовательское адресное пространство – и есть текущий контекст памяти.