Этой статьей я хочу описать часть своих знаний, приобретенных на моем пути в OSDev и системном программировании. Параллельно я буду писать также документацию по CS, которую в скором времени выложу на свой GitHub.
Важно подметить, что мой путь ещё далеко не окончен и я, можно сказать, нахожусь в самом его начале. Зачем тогда я это делаю? Чтобы закрепить уже имеющиеся знания, получить критику от более опытных специалистов, а также, возможно, помочь некоторым новичкам подробно разобраться в теме.
Принцип работы процессора.
Почти все подобные статьи начинаются с краткой, часто даже с переупрощенной схемы «Кнопка запуска ПК -> Прошивка (BIOS/UEFI) -> загрузчик -> ОС». Я же хочу начать немного с другого: Как вообще работает процессор на базовом уровне?
Вероятно, все погруженные в IT знают что такое бит, байт и т.д. Но как именно процессор ими оперирует? При каких условиях? Есть ли ограничения? Если есть, то какие? Вот на это мы сейчас и ответим.
Как процессор оперирует битами?
Процессор осуществляет свою работу путем выполнения команд (или инструкций). Изначально команды довольно примитивные: простые логические (AND, OR, XOR и т.д.), математические (ADD, SUB и т.д.) операции, пересылки и т.п. Из них складываются более сложные и комплексные команды (INVPLG, CMPS и т.д.).
Процессор также гарантирует сохранение порядка выполнения команд программиста. Т.е. программист, написавший код, всегда уверен в том, что он будет выполнен в нужном порядке (program order). Хотя, современные процессоры суперскалярны и способны к внеочередному исполнению нескольких команд за такт. Но к теме статьи это не относится.
При выполнении команд процессор оперирует машинными словами: двумя байтами информации (исторически сложившееся для x86 название).
Как биты доставляются до процессора?
Путем взаимодействия процессора с шиной данных и шиной адреса. В старых процессорах это были физические дорожки «от процессора к остальным», и так их легче воспринимать новичку. Хотя, в современных процессорах они представляют из себя скорее интерфейс взаимодействия контроллера памяти и RAM.
Как следует из названий, по шине данных идут данные, а по шине адреса — адреса. Их размер, в совокупности с частотой — это своего рода пропускная способность подсистемы памяти.
Размеры шин определяются конкретным чипом. В разных моделях у разных производителей они могут быть разными, но режимы работы используют конкретные размеры виртуальных адресов и регистров. Важно понимать, что на физическом уровне процессор читает память фиксированными кусками. Даже если программе нужен один или несколько байт, процессор запрашивает у памяти всю кэш-линию. Поэтому, получив целую кэш-линию, он загружает её в кэш, а программе отдает только нужную ей часть, остальное остается в кэше. Если же шина меньше, то обращения к памяти происходят в несколько циклов (Пример: 8-битный 8088, создающий 16-битные обращения за 2 цикла). Шина адреса при этом всегда задействована полностью.
Режимы работы процессора.
Режим процессора — набор связанных параметров, включающий в себя размер регистров общего назначения (GPR), размер линейного адреса и доступные механизмы трансляции адреса. Всего существует несколько режимов:
16-битные real и protected mode
16-битные регистры по умолчанию. Также доступны и 32-битные через 0x66-префикс, если их поддерживает кристалл
Для адресации используется 20 бит (для real mode) или 24 бита (для protected mode)
32-битный protected mode
32-битные регистры
32-битные виртуальные адреса (физические — 32 без PAE и 36 с PAE)
64-битный long mode
64-битные регистры
48- или 57-битные виртуальные адреса (физические — до 52 бит)
Unreal mode
Формально, это не отдельный режим, а метод совместимости 16-битных режимов с памятью выше 1МБ, вплоть до 4ГБ
V86 и SMM в статье рассматриваться не будут.
16-битный real mode.
Переходим к основной части статьи — режимы работы процессора. И начнем, конечно, с 16-битного real mode. Он является своего рода режимом совместимости с 8086 более новых процессоров.
Важной особенностью этого режима является то, что из него мы имеем косвенный доступ к BIOS, а точнее, можем обращаться к нему через специальные прерывания. Ни protected, ни long mode в силу архитектурных ограничений такой возможности не имеют. Речь именно о BIOS, а не о UEFI. Формально, некоторые системы предоставляют API для protected режимов, но такое есть далеко не на всех машинах, поэтому брать их за правило нельзя.
У многих вероятно уже возник вопрос: почему 16-битный режим использует 20-битные адреса, а главное — как? И тут не обойтись без исторической справки. 16-битная адресация обеспечивает доступ процессора всего лишь к 64КБ адресного пространства, что не давало прироста в сравнении с предыдущими 8-битными ПК. Перед инженерами Intel возникла задача — увеличить это число хоть как-нибудь. И они пришли к идее о сегментации, сделав в 8086 шину адреса 20-битной, что сразу расширило максимальный объем адресуемой памяти до 1МБ.
Сегментация.
Сегментация — модель адресного пространства, при котором память разделяется на сегменты. Сегмент — непрерывная область памяти, начало которой хранится в сегментном регистре. Нужные данные же хранятся внутри сегмента, а их позиция в нем задается смещением — числом, хранящемся в регистре общего назначения (GPR).
В этой модели памяти адрес задается не прямым “наименованием” байта, как в protected mode (flat model, хотя это выбор разработчика, а не условие), а формулой:
Почему умножаем на 16? Потому что . На деле получается:
Сегмент: 0bXXXX XXXX XXXX XXXX * 16 (он же << 4): 0bXXXX XXXX XXXX XXXX 0000 Сдвиг: 0b0000 YYYY YYYY YYYY YYYY Адрес: 0bXXXX XXXX XXXX XXXX 0000 + 0b0000 YYYY YYYY YYYY YYYY
А на конкретных цифрах:
Сегмент: 0b0010 1001 0110 1001 * 16: 0b0010 1001 0110 1001 0000 Сдвиг: 0b0000 1010 0011 1010 0001 Адрес: 0b0010 1001 0110 1001 0000 + 0b0000 1010 0011 1010 0001 = 0b0011 0011 1010 0011 0001
Т.е., как я думаю уже понятно, программе напрямую по конкретному адресу в нужное место не попасть:
bits 16 main: mov [0xB8000], 'A' ; На экран ничего не выведется, поскольку архитектура требует адрес вида сегмент:смещение. mov ax, 0xB800 mov es, ax mov di, 0 mov [es:di], 'A' ; На экран выведется A, поскольку 0xB800 * 16 + 0 = 0xB8000, что как раз адрес VGA text buffer.
A20.
Также одной из особенностей архитектуры x86 является наличие адресной линии А20 (с появлением 80286) на шине процессора, которая по умолчанию выключена, а без ее включения любые адреса выше 1МБ будут заворачиваться (wrap-around эффект). Если конкретнее — бит 20 (сама линия А20) и выше. Дальше все просто отбрасывается, и в итоге остаются только младшие 20 бит. Например:
Адрес: 0×10FFFF Этот же адрес с выключенной A20 процессор читает как: 0×0FFFF
Поэтому, если программе нужен будет доступ к памяти выше 1МБ, эту шину обязательно нужно включить. Самый простой способ: Fast A20 gate, проворачиваемый всего в 3 строчки на ассемблере:
in al, 0×92 ; Сохраняем значение порта 0×92 — порта, 1 бит (если считать от 0) которого как раз и отвечает за нужную нам линию, хотя и другой функционал тоже имеет. or al, 2 ; Выставляем тот же 1 бит в 1. out 0×92, al ; Записываем получившееся обратно в порт.
На самом деле, этот вариант хоть и прост, но достаточно ненадежен, поскольку подразумевает, что бит 1 у порта всегда 0, а также не учитывает возможные ошибки. Мы платим надежностью за простоту.
16-битный protected mode.
16-битный protected mode — это первый шаг на пути к нормальной многозадачной ОС. Основное его отличие с 16-битным real mode заключается в изменении роли сегментации.
Сегментация.
Здесь сегментация из «адресного» принимает защитный характер, отсюда и название режима. В чем заключается «защитная роль»:
Сегментные регистры (CS, DS, ES и т.д.) теперь хранят не адрес, а селектор — ссылку на дескриптор, где теперь записаны база сегмента, лимит, права доступа и т.д. - При каждой загрузке селектора процессор сам проверяет все это и выдает исключения при несовпадении с ожидаемым. Все без участия ОС.
Это обеспечивает изоляцию, когда никто не может выйти за границы своего сегмента (лимит) или без должных прав делать что-либо.
Дескрипторы — это своего рода «описатели». «Записи», в которых описаны некоторые «правила». В GDT (Global Descriptors Table) — «правила» работы с памятью и сегментами, в IDT (Interrupt Descriptors Table) — «правила» реагирования процессора на те или иные прерывания.
Особенности режима.
Для работы protected mode требует заранее настроенной GDT. Это архитектурное ограничение. Поэтому перед переходом в него нужно настроить хотя бы базовую глобальную таблицу дескрипторов. Каждый дескриптор имеет формат:
Байты 0-1. Limit, “Где заканчивается сегмент”
Байты 2-4. Base, “Где начинается сегмент”
Байт 5. Access byte, права
Байт 6. Флаги (старшие 4 бита) + Limit (младшие 4 бита)
Байт 7. Base Такая “некрасивая” структура обусловлена историей и желанием совместимости новых процессоров со старой архитектурой и программами.
Сама GDT обязана начинаться с нулевого дескриптора. А максимальный размер - 64КБ (65535 байта из-за 16-битного поля лимита), т.е. 8191 дескриптора. И, поскольку первый дескриптор нулевой, в итоге их может быть до 8190. Хотя, на деле редко используется больше нескольких десятков.
Unreal mode
Снова же, это не режим, а метод взаимодействия системы в 16-битном режиме с дескрипторным кэшем. Это дает доступ к 32-битным смещениям для доступа к памяти выше 1МБ, оставаясь в 16-битном режиме.
Как?
Механизм основан на том, что начиная с 80286 процессор хранит параметры сегмента (база, лимит, права) не только в видимом сегментном регистре, но и в скрытом дескрипторном кэше. В real mode этот кэш обычно содержит лимит 64 КБ, но его можно изменить: войти в protected mode, загрузить сегментный регистр данных (обычно — FS или GS) селектором дескриптора с лимитом 4 ГБ и базой 0, а затем вернуться в real mode. Кэш при этом не сбрасывается, и сегмент продолжает работать с лимитом 4 ГБ.
Классификация
Их существует всего 2:
Big unreal mode. Затрагивает только сегменты данных. Код остается в пределах 64КБ.
Huge unreal mode. Затрагивает также сегментный регистр CS, что позволяет выполнять код выше 64КБ. Сложнее, поскольку требует работы с прерываниями.
32-битный protected mode.
Появление 32-битного режима ознаменовало появление такого явления, как flat model т.е. нулевое смещение, а значит — прямое обращение к памяти. В свою очередь, его распространение в различных ОС привело к концу сегментации, как к адресной модели памяти, хотя она еще оставалась возможной.
Также стандартные изменения в виде:
Увеличения размера регистров до 32-бит
32-битный виртуальный адрес
Также в этом режиме появляется возможность включить paging — страничную модель адресации памяти. Это тема для отдельной большой статьи, но тут я кратко опишу суть.
Со включением paging (Бита PG в служебном регистре CR0) процессор уже оперирует не физическими, а виртуальными адресами. В системе еще до включения paging должны быть созданы таблицы страниц. Здесь они разделяются на 2-3 уровня:
PDPT (Page Directory Pointer Table). Уровень, который появляется при включении PAE. Без этого старшим остается PD.
PD (Page Directory)
PT (Page Table)
Записи в PT уже указывают не на следующий уровень таблиц, а на сами фреймы (4КБ) — физические области памяти, где фрейм по размеру равен странице — основной единице разбиения адресного пространства.
Каждая таблица содержит в себе 1024 (512 при включенном PAE) записей. Каждая запись указывает на следующую таблицу, каждая запись в которой — на страницу (на следующую таблицу при включенном PAE). Таким образом, целая PD таблица хранит записи, указывающие на отдельные таблицы PT, а она — записи, указывающие на страницы. Исключением является PDPT (при включении PAE) — в 32-битном режиме он имеет максимум всего 4 записи.
Т.е. это своего рода «пакет с пакетами», только в конце тебя ждет не то, что ты ищешь, а адрес того, что ты ищешь.
При этом «Активной» может быть только 1 таблица высшего уровня (PD для 32-битного режима без PAE; PDPT для него же, но с PAE; PML4 для 64-битного режима). Её адрес в памяти помещается в специальный регистр CR3. После этого каждый виртуальный адрес ищет записи именно в этой таблице
Таким образом адрес разбивается на блоки. Для выключенного PAE:
10 бит указывают номер таблицы PT (индекс в PD)
10 бит указывают номер страницы (индекс в PT)
12 бит — это offset внутри страницы
И с PAE:
2 бита указывают номер таблицы PD (индекс в PDPT)
9 бит указывают номер таблицы PT (индекс в PD)
9 бит указывают номер страницы (индекс в PT)
12 бит — это offset внутри страницы
Это очень упрощенная схема, упускающая кучу деталей, особенностей, оставляющая кучу вопросов, которая просто в общих чертах описывает логику работы. Но в рамках конкретно этой статьи подробностей не будет.
64-битный режим.
И последнее — 64-битный режим. Сегментация здесь уже практически не используется, а для работы обязателен уже включенный paging. И снова к подробностям.
Конечно, меняется:
размер регистров до 64 бит
размер виртуального адреса до 48 бит (также возможна и 57-битная адресация)
Сегментация.
Сегментация в Long mode уже фактически не используется. Практически все сегментные регистры, которые использовались ранее, игнорируются процессором. «Активными» остаются только 2: FS и GS. Они нужны для доступа к TLS (Thread-Local Storage) и в разных ОС могут использоваться по разному.
Адресация.
Здесь сохраняется механизм страничной адресации, но при этом он значительно расширяется. Появляется новый уровень таблицы — PML4, записи в которой указывают на PDPT, который теперь существует всегда ввиду того, что в long mode включенный PAE обязателен. Также размер регистра/поля адреса увеличивается до 64, но при этом в адресации участвует только младшие 48 бит — старшие 16 при этом являются копией 47-го (см. Canonical address ниже).
Как я упоминал выше, несмотря на то, что предел физ. адреса тут достигает 52 бит, зачастую для адресации используется только 48 бит. Тут следует подробнее это описать, потому что это очень важно для понимания работы этого режима. Виртуальный адрес выглядит теперь так:
0x0000124836C9BEDF = 0b0000 0000 0000 0000 0001 0010 0100 1000 0011 0110 1100 1001 1011 1110 1101 1111
Где старшие 16 бит должны быть копией 47 бита (canonical address). Младшие 12 - смещение внутри страницы/фрейма, а каждые 9 между ними - номер записи таблицы в вышестоящем уровне. Т.е.:
0b0000000000000000 000100100 100100000 110110110 010011011 111011011111 |CanonicalAddress| PML4 | PDPT | PD | PT | offset |
Где трансляция (определение нужного физического адреса из виртуального) выглядит так:
PML4[36] -> PDPT;
PDPT[288] -> PD;
PD[438] -> PT;
PT[155] -> Frame;
Frame + 3807 = нужный байт.
Canonical address — разделение адресных пространств. Т.е. «нижнее» пространство, где старшие 16 бит =0, принадлежит пользователю, а «верхнее» пространство, где старшие 16 бит =1, принадлежит ядру ОС (higher half). Каноническая дыра, создаваемая таким механизмом, просто огромна и съедает больше 99.99% всех возможных адресов, но даже так каноническая адресация дает 128ТБ на пользовательское пространство и столько же на пространство ядра, итого 256ТБ на всю систему, что уже запас памяти на десятилетия вперед для домашних ПК. Но уже существует 5-уровневая адресация с виртуальным адресом 57 бит, и обсуждается 6-уровневая.
Переход между режимами.
16-битный real -> 16-битный protected
Включение A20 line
Адрес валидной GDT в регистре GDTR
Выключенные прерывания
Установленный бит PE в регистре CR0
far jump
16-битный protected -> 32-битный protected
Наличие 32-битного код-сегмента в GDT
(Остальное уже выполнено ранее)
far jump
32-битный protected -> 64-битный long
Отключение paging (бит PG в CR0), если был включен
Настройка 4-уровневого paging
Установка бита PAE в CR4
Установка валидной PML4 в CR3
Установка бита LME в регистре EFER
Установить бит PG в CR0
far jump
Unreal mode
Включение А20 line
Настройка GDT, содержащей дескриптор данных (Лимит = 4ГБ, база = 0)
Переход в protected mode
Загрузить FS/GS селектором этого дескриптора
Сбросить бит PE в CR0 (вернуться в 16-битный режим)
Заключение.
Опыта в написании подобных статей у меня нет, поэтому, думаю, она довольно дырявая. В будущем, по мере наработки опыта, статьи будут становиться качественнее. Что касается меня:
Буду рад любого рода конструктивной критике и поддержке.
Всем спасибо за внимание!
Комментарии (18)

teecat
16.10.2026 07:11Забавно, что 32х битный автоматически приравнивается к протектед. Ушла эпоха, когда A20 считалась недокументированным хаком, а разработка защищенного режима прорывом, когда в первую очередь писали при кольца защиты

SIISII
16.10.2026 07:11Вообще, A20 -- вполне себе документированная вещь, официально опубликованная, например, в техническом описании на IBM PC/AT.

stackshortlist
16.10.2026 07:11Хороший подход начинать не со схемы загрузки, а с того, как процессор вообще оперирует битами, тогда переходы между реальным, защищённым и длинным режимом воспринимаются гораздо естественнее. Для новичков в OSDev я бы ещё отдельно подчеркнул, что в длинный режим нельзя попасть напрямую из реального без включения страничной адресации. Планируете ли разобрать в продолжении, как устроены GDT и переход через дальний прыжок?

ImDevyz Автор
16.10.2026 07:11Спасибо. Дальше, наверное, хотелось бы про регистры рассказать все. А после - то, что оставляет больше всего вопросов

SIISII
16.10.2026 07:11«Кнопка запуска ПК -> BIOS -> железо -> загрузчик -> ОС»
Хм... BIOS -> железо -- это как?
абсолютно все, даже не погруженные в IT люди, знают что такое бит, байт и т.д.
Далеко не все, и, что странное, даже среди типа ИТшников такие попадаются.
Нет одной инструкции «найти подстроку» . Вместо этого он использует довольно примитивные логические операции. AND, OR, XOR и т.д.
Вообще-то, у 8086 и всех потомков есть строковые операции, пригодные, в т.ч., и для поиска подстрок (CMPS). Им, конечно, требуется определённое "обрамление" (загрузить правильные адреса, установить счётчик символов, интерпретировать полученный результат), но, тем не менее.
Так вот, наборы таких логических операций называются инструкциями
Не логических, а просто любых действий, которые может выполнять процессор по приказу программиста. Кстати говоря, я предпочитаю переводить instruction как "команда": "инструкция" в русском языке зачастую является аналогом "manual", т.е. неким руководством к действию, но не приказом что-то сделать прямо сейчас -- в отличие от команды, которая именно приказывает (сравните: "Инструкция по эксплуатации" и "Рота, в ружьё!").
За такт ядро процессора может выполнить только 1 инструкцию (современные уже могут, но поскольку статья вводная, думаю, подобное допущение простимо), поэтому настоящего параллелизма на одном ядре не существует, вместо этого во всех современных ОС используется псевдопараллелизм.
Про последнее не понял, честно говоря. Что же касается параллелизма на уровне выполнения команд, то, во-первых, он существует (для архитектуры IA-32 -- начиная с Pentium, но если говорить шире, то впервые появился ещё в 1960-х годах, на CDC 6600 и затем IBM 360/91), а во-вторых, лучше уж ввести концептуальный порядок выполнения команд: строго одна за другой, и выполнение новой начинается лишь после завершения старой. Пока программа является однопоточной (физически выполняется на одном логическом процессоре), соблюдение концептуального порядка гарантируется аппаратурой, даже если физически выполнение команд совмещается или они вообще выполняются в другом порядке. Однако при параллельной работе на нескольких процессорах особенности выполнения команд уже надо учитывать (на IA-32/AMD64 это несложно, поскольку за очень многим следит аппаратура, на других архитектурах у программиста головной боли больше).
И, кстати, корректней говорить не о ядрах, а о логических процессорах: одно ядро может выглядеть для программиста несколькими процессорами (из-за гипертрединга).
В разных режимах работы это число также разное, а называется оно машинным словом
Неверно. Применительно ко всем процессорам x86 (начиная с 8086 и кончая любыми современными 64-разрядными) независимо от режима работы словом называются два байта (16 бит): так сложилось исторически. На других архитектурах словом может называться иная величина.
Своеобразной «дорожкой», по которой ток доходит до процессора, являются шины: шина данных и шина адреса. Как следует из названия, по первой процессору поступают данные, с которыми нужно работать, а по второй — адрес следующей инструкции. Шины бывают разных размерностей, и в силу того, что архитектура x86 довольно стара и сохранила совместимость с предками вплоть до 16-битных процессоров, даже современные 64-битные процессоры по умолчанию стартуют в 16-битном режиме и переключить его — задача загрузчика или ОС. Так вот, это все к тому, что битность шины адреса является одним из следствий режима работы процессора.
Абсолютно неверно. Во-первых, в столь явном виде, как это было в 1980-е, шин адреса и данных сейчас не существует (они, конечно, есть, но спрятаны на кристалле процессора, причём разведены очень хитро между процессорными ядрами, кэшами, контроллером памяти, мостами с внешними шинами и т.п.). Во-вторых, разрядность их определяется только и исключительно конкретной реализацией и абсолютно не зависит от режима работы процессора. Из ядра может торчать 8-разрядная шина данных (пример -- 8088, из-за чего некоторые клоуны называли его 8-разрядным микропроцессором), а может 256-разрядная -- и для программиста это совершенно незаметно. То же самое касается шины адреса: у 8086/88 она была 20-разрядной, у 80286 и 80386SX -- 24-разрядной, у 80386DX и 80486 -- 32-разрядной, ну и т.д.
Режимы работы процессора.
Неполный список. Полный выглядит так:
16-разрядный реальный режим (единственный для 8086/88 и 80186/188);
16-разрядный защищённый режим (введён в 80286);
32-разрядный защищённы режим (введён в 80386);
режим виртуального 8086 (V86) -- по сути, подрежим 32-разрядного защищённого, предназначенный для выполнения 16-разрядного кода реального режима (чтоб программы для ДОС можно было под Виндой запускать, грубо говоря). Полностью выпилен при переходе процессора в 64-разрядный режим;
"нереальный" (unreal) режим, неофициально появился в 80386 и сохраняется до сих пор, позволяя с помощью сегментных регистров FS и GS адресовать 4 Гбайта памяти, находясь в реальном режиме (вероятно, и в 16-разрядном защищённом тоже);
режим управления системой (SMM, введён в 80386SL и 80486SL);
64-разрядный защищённый режим (введён AMD, позже скопирован Intel, когда провал IA-64 aka Itanium стал очевиден).
из него мы имеем косвенный доступ к BIOS, а точнее, можем обращаться к нему через специальные прерывания. Таким не может похвастаться ни protected, ни long mode в силу архитектурных ограничений.
Неверно. У BIOS имеется ряд функций, доступных из 16- и 32-разрядных защищённых режимов, ну а UEFI, собственно, в защищённом режиме и работает (причём в просторечии его всё равно именуют BIOS, хотя это, строго говоря, неверно).
16-битная шина адреса обеспечивает доступ процессора всего лишь к 64КБ RAM, что почти не давало прироста в сравнении с предыдущими 8-битными ПК
Не RAM, т.е. оперативной памяти, а к 64 кбайтам адресного пространства памяти, а что в нём расположено -- не дело процессора (там могут быть и ОЗУ, и ПЗУ, и регистры устройств).
Не "почти не давало", а просто "не давало": основная масса 8-разрядных процессоров имела 16-разрядную шину адреса.
В этой модели памяти адрес задается не прямым “наименованием” байта, как в protected mode, а формулой:
В 16- и 32-разрядных защищённых режимах сегментация тоже используется, и физически без неё обойтись невозможно. Т.е. "прямого наименования байта" в этих режимах тоже нет.
Также одной из особенностей архитектуры x86 является наличие адресной линии А20 на шине процессора, которая по умолчанию выключена, а без ее включения любые адреса выше 1МБ будут заворачиваться (wrap-around эффект).
Вообще, изначально её не было: у 8086/88 шина адреса 20-разрядная, а её линии нумеруются A19:A0. Сворачивание происходило "автоматически": если при сложении сегмента и смещения получалось значение, превышающее по ширине 20 разрядов, т.е. превосходящее F'FFFF, старший бит терялся, поскольку линии A20 не существовало.
А вот когда делали 80286, у которого адрес был расширен до 24 разрядов (A23:A0), про это дело забыли и допустили ошибку, и в результате при сложении сегмента FFFF и смещения FFFF в реальном режиме получался адрес 10'FFEF. Это нарушало совместимость "снизу вверх" с предыдущими процессорами, поэтому разработчики IBM PC/AT добавили внешний по отношению к микропроцессору костыль -- вентиль A20 (gate A20), который блокировал передачу единицы по линии A20.
Protected mode - 32-битный режим процессора
Защищённые режимы -- и 16-, и 32-, и 64-разрядные в зависимости от того, что и как включено (ну и от модели процессора, естественно).
когда ОС может менять дескриптор и все селекторы будут автоматически указывать на новые
Не будут они "автоматически" указывать на новые: чтобы новый описатель (дескриптор) вступил в действие, после его изменения в памяти надо обязательно выполнить загрузку соответствующего сегментного регистра. Пока сегментный регистр не перезагружен, он обеспечивает обращение к сегменту, информация из которого была получена в момент его последней загрузки (т.е. содержимое описателя в памяти игнорируется).
В GDT (Global Descriptors Table) — «правила» работы с памятью и сегментами, в IDT (Interrupt Descriptors Table) — «правила» реагирования процессора на те или иные прерывания.
Помимо GDT, есть ещё и LDT.
И последнее — 64-битный режим. Сегментация здесь уже практически не используется
Она там не "практически не используется", а полностью выпилена. При работе в 64-разрядном режиме CS, DS, SS и ES вообще никак не используются, а FS и GS работают подобно базовым регистрам, а не "нормальным" сегментным (в 32-разрядной Винде они именно сегментными были, а доступ к TLS обеспечивался за счёт загрузки в них соответствующим образом сформированных описателей).

unreal_undead2
16.10.2026 07:11SMM - скорее часть другого измерения по привилегиям доступа (наряду с ring 0 - ring 3 и режимом гипервизора), эта тема вообще не затронута.

SIISII
16.10.2026 07:11Но, тем не менее, режим работы :) И да, про виртуализацию таки забыл -- но я в неё никогда и не вникал (применительно к IA-32/AMD64).

ImDevyz Автор
16.10.2026 07:11Про виртуализацию у меня уже лежит большая заметка, которую позже переделаю в статью. Но сейчас не вижу смысла про это писать - будет слишком большой скачок через темы

ImDevyz Автор
16.10.2026 07:11Тема не затронута, поскольку я с ней ещё не знаком. Проект у меня работает пока только в ring 0 и реализация ring 3, к сожалению, будет не скоро. Но как изучу, возможно, и про это напишу

ImDevyz Автор
16.10.2026 07:11Очень благодарен, что кто то настолько детально решился разобрать статью. Было очень информативно, учту все нюансы. Большое спасибо за критику

ImDevyz Автор
16.10.2026 07:11Отредактировал статью:
Постарался исправить все фактические ошибки и учесть поправки из комментариев
Немного поправил стилистику в некоторых местах
Подробнее описал переход между режимами и unreal mode
Всем большое спасибо, кто помог найти ошибки и улучшить статью
С этой закончил, теперь приступаю к статье про регистры

SIISII
16.10.2026 07:11Поэтому, если размер шины больше, чем нужно, то часть шины данных сверх нужного просто не используется (как в современных 64-битных ПК, когда те находятся в real mode на старте)
Вот это вот утверждение некорректно -- по всей видимости, Вы не разбираетесь в "электронной" стороне вопроса (это, если что, не наезд -- для программиста вполне обычным является не понимать, как в реальности работает железо, ведь это не его область деятельности; ну а как работа идёт на уровне транзисторов, зачастую и разработчики железа не очень хорошо понимают -- ибо на уровне транзисторов почти никогда цифровую электронику сейчас не проектируют). В реальности современные процессоры используют свои (не видимые никому, кроме разработчиков процессора) шины максимально полным образом. Скажем, если в программе написано: MOV AL,mem, т.е. выполняется загрузка всего одного байта, то, если требуемого байта нет в кэш-памяти, процессор считает из физического ОЗУ полную строку кэша, в которой будет, в т.ч., и этот байт -- а размер строки кэша обычно составляет 64 байта. Соответственно, если процессор соединён с памтяью 16-байтовой шиной, он для считывания одного-единственного байта физически выполнит четыре пересылки по 16 байтов. Ну а если требуемый байт уже лежит в кэше, он просто считает его из кэша вообще без обращения к памяти.
16-битные real и protected mode
16-битные регистры
20-битная (для real-mode) или 24-битная (для protected mode) шина адреса
Тоже не совсем корректно, как и последующий текст.
Регистры являются 16-разрядными только на 16-разрядных процессорах (80286 и младше). При работе на 80386 или более поздних в реальном или 16-разрядном защищённом режиме они всё равно будут 32-разрядными, и можно обращаться ко всем 32 разрядам. Скажем, в реальном режиме для 80386 можно написать ADD EAX,EBX -- и будут сложены два 32-разрядных регистра.
Ну а ширина шины адреса определяется типом процессора и воощбе никакого отношения к его режиму работы не имеет. Работающий в реальном режиме 80386DX использует все 32 бита адреса, просто старшие биты обычно будут равны нулю (но в нереальном режиме возможно обращение ко всем 4 Гбайтам, хотя нереальный режим -- это обычный реальный, просто с нестандартным заполнением теневых регистров FS/GS).
Вот в 64-разр процах, работающих в 16- или 32-разрядном режиме, доступа к старшим половинам регистров, кажется, нет, и они выглядят как 32-разрядные, хотя физически остаются 64-разрядными -- но утверждать не берусь, надо смотреть документацию, а мне лениво :)
Unreal mode
4.Загрузить DS/ES селектором этого дескриптора
Этот пункт, строго говоря, неверен: для нереального режима загружают FS и/или GS. Дело в том, что DS и ES постоянно перезагружаются при работе программы, а любая его загрузка в реальном режиме сносит предел и прочую скрытую информацию, т.е. лишает возможности обращения ко всей памяти. FS/GS, с другой стороны, программами реального режима не используются вообще (у 80286 и более ранних процессоров их просто не было), а программа, специально написанная для нереального режима, загрузив их в защищённом режиме, после возврата в реальный режим не будет их перезагружать, а будет лишь использовать в тех случаях, когда надо обратиться к памяти за пределами 1 Мбайта (вроде MOV AX, FS:адрес).

ImDevyz Автор
16.10.2026 07:11Большое спасибо за уточнения! Поправил статью и постарался учесть
Кстати, насчёт старших половин 64-битных регистров вы правы. Я перепроверил:
Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 1, раздел 3.4.1.1.:
the upper 32 bits of 64-bit general-purpose registers are undefined in 32-bit modes
Ещё раз спасибо за критику!
unreal_undead2
Вроде 16битный protected mode не выпиливали.
ImDevyz Автор
Забыл про него. Не работал с ним толком, в голове ничего не отложилось