Таким образом, системы x86 на базе Linux или Windows используют только кольцо 0 для режима ядра и кольцо 3 для режима пользователя. Почему процессоры даже различают четыре разных кольца, если они все равно используют только два из них? И изменилось ли это на архитектуре AMD64?
1 ответ
Есть две основные причины.
Первая причина в том, что, хотя процессоры x86 предлагают четыре кольца защиты памяти, степень защиты, предлагаемая таким образом, существует только на уровне сегмента. Таким образом, каждый сегмент может быть установлен на определенное кольцо ("уровень привилегий") от 0 до 3, наряду с другими средствами защиты, такими как отключение записи. Но не так много доступных дескрипторов сегментов. Большинство операционных систем хотели бы иметь более тонкую степень детализации защиты памяти. Как ... для отдельных страниц.
Таким образом, введите защиту на основе записей таблицы страниц (PTE). Большинство, если не все современные операционные системы x86 более или менее игнорируют механизм сегментирования (во всяком случае, насколько это возможно) и полагаются на защиту на основе PTE. Это определяется битами флага, которые являются младшими 12 битами в каждом PTE - плюс бит 63 на процессорах, которые поддерживают no-execute. Существует один PTE для каждой страницы, который обычно составляет 4K.
Один из этих битов флага называется "привилегированным" битом. Этот бит определяет, должен ли процессор находиться на одном из "привилегированных" уровней для доступа к странице. "Привилегированные" уровни: PL 0, 1 и 2. Но это всего лишь один бит, поэтому на уровне постраничной защиты количество "режимов", доступных для защиты памяти, равно двум: страница может быть доступна из непривилегированного режима или нет. Отсюда всего два кольца.
Чтобы иметь четыре возможных звонка для каждой страницы, они должны иметь два защитных бита в каждой записи таблицы страниц, чтобы кодировать одно из четырех возможных номеров звонков (так же, как и дескрипторы сегментов). Они не
Вторая причина - цель переносимости ОС. Это не только x86; Unix научил нас, что ОС может быть относительно переносимой для многопроцессорных архитектур, и это хорошо. А некоторые процессоры поддерживают только два кольца. Не зависящие от нескольких колец в архитектуре, разработчики ОС сделали ОС более переносимыми.
Третья причина связана с разработкой Windows NT. Дизайнеры NT (Дэвид Катлер и его команда, нанятые Microsoft из DEC Western Region Labs) имели большой опыт работы с VMS; Фактически, Катлер и некоторые другие были среди оригинальных дизайнеров VMS. А процессор VAX, для которого была разработана VMS (и наоборот), имеет четыре кольца. VMS использует четыре кольца. (На самом деле VAX имеет четыре защитных бита в PTE, что позволяет использовать такие комбинации, как «только чтение из пользовательского режима, но возможность записи из кольца 2 и изнутри.» Но я отвлекся.)
Но компоненты, которые работали в кольцах VMS 1 и 2 (Службы управления записями и CLI, соответственно), были оставлены вне дизайна NT. Кольцо 2 в VMS на самом деле было связано не с безопасностью ОС, а с сохранением среды CLI пользователя от одной программы к другой, а в Windows NT такой концепции просто не было; CLI работает как обычный процесс. Что касается кольца 1 VMS, то код RMS в кольце 1 должен был вызываться в кольцо 0 довольно часто, и переходы вызова являются дорогостоящими. Оказалось, что гораздо эффективнее просто перейти к кольцу 0 и покончить с ним, а не иметь много переходов кольца 0 в коде кольца 1. (Опять же - не то чтобы в NT все равно было что-то вроде RMS.)
Но почему они там? Что касается того, почему в x86 реализовано четыре кольца, в то время как ОС их не использует - вы говорите об ОС гораздо более свежего дизайна, чем x86. Многие функции "системного программирования" в x86 были разработаны задолго до того, как на нем были внедрены NT или настоящие ядра Unix-ish, и они не знали, что будут использовать ОС. (Только когда мы получили пейджинг на x86 - который не появлялся до 80386 - мы могли реализовать настоящие ядра Unix или VMS-подобные без переосмысления управления памятью с нуля.)
Современные ОС x86 не только в значительной степени игнорируют сегментирование (они просто устанавливают сегменты C, D и S с базовым адресом 0 и размером 4 ГБ; сегменты F и G иногда используются для указания на ключевые структуры данных ОС), они также в значительной степени игнорировать такие вещи, как "сегменты состояния задачи". Механизм TSS был явно разработан для переключения контекста потока, но оказалось, что он имеет слишком много побочных эффектов, поэтому современные ОС x86 делают это "вручную". Например, единственное время, когда x86 NT меняет аппаратные задачи, - это действительно исключительные условия, такие как исключение двойного сбоя.
Что касается x64, многие из этих неиспользуемых функций были опущены. (К их чести, AMD фактически поговорила с командами ядра ОС и спросила, что им нужно от x86, что им не нужно или не нужно, и что они хотели бы добавить.) Сегменты на x64 существуют только в том, что можно назвать рудиментарной формой, переключение состояний задачи не существует и т.д. И ОС продолжают использовать только два кольца.