Часть 2. Реальный и Защищённый режимы работы процессора
На данный момент х86 процессоры могут работать в четырёх режимах. Мы рассмотрим только первые два из них, как более распространённые:
2.0. Память в реальном режиме
В Real-моде, программист полноправный хозяин системы и имеет дело только с физической памятью. Ему доступны буквально все инструкции процессора, прямой доступ к портам оборудования, и многое другое. Здесь не задают вопросов типа “Вы уверены? Да/Нет”, и за необдуманные действия ответственность несёте только вы. Бонусом идёт программирование вообще без системы, на одних только функциях биос (благо его и сейчас эмулируют все EFI). Основной задачей ОС является как-раз абстрагирование пользователя от реальных устройств, которые он может по неосторожности вывести из строя. Одним словом, зевать от скуки тут не приходится, и нужно быть всегда на чеку.
У первых процессоров 8086 шина-адреса(A) была 20-битная. Такая ширина позволяет адресовать всего
Посмотрим на рисунок, где изображены проводники системной шины, как они идут с процессора. Шина эта – мультиплексная, т.е. единственная и для адреса, и для данных. Сначала на шину выставляется адрес, после которого по ней-же отправляются и данные:
Как видим, увеличение разрядности шины увеличивает кол-во адресуемых ячеек памяти. Это хорошо.. Но регистры ЦП были в то время 16-битными (без приставки ‘E’), и ими можно было дотянуться максимум до 64 Кб физической памяти (см.рис). Как вместить в 16-битный регистр 20-битный адрес? Было решено разбить всю память на логические сегменты, и адресовать её через регистровую пару в формате сегмент+смещение. Формула и вид получился такой:
Здесь видно, что три средние тетрады перекрываются. Для сегментных регистров типа СS остаются только смещённые влево 4-бита, которыми можно выбрать один из 16-ти сегментов. Под смещение выделяется как и прежде 16-бит, а это 64 Кб внутри выбранного сегмента памяти. Таким образом, размер сегмента в реальном режиме равен 64К, а всего сегментов =16. В сумме они дают:
Однако это не табу, и ничто не мешает нам перевернуть ‘пластинку’ наоборот, ведь мы имеем дело с логическим адресом. Тогда получим 65.536 сегментов, по 16-байт в каждом. Эти 16-байт назвали ‘параграф’ и это мин.размер одного сегмента в R-моде. Такая картина позволяет операционной системе (например DOS) эффективно распределять память, которой и так с гулькин-нос. Сегменты по 64К бывают только на бумажке, а на практике - это просто макс предел, который выделяет ОС под один сегмент.
Во-первых, среди одного мегабайта, 64К непрерывной памяти у системы может и не быть, а во-вторых - если у программы в сегменте-данных всего три строчки текста, зачем выделять для них 64К памяти? Система просто выравнит эти данные до 16-байтного параграфа, и вручит их пользователю. Если (в процессе работы) программе понадобится ещё доп.памяти, то программист должен запросить её у системы, и она без проблем выделит из своего резерва кусок (если таковой остался).
На рисунке выше, я выделил красным одну из линий шины-адреса и вставил туда ключ. Эту линию назвали А20, поскольку она идёт следующей от 20-битного адреса 0-19. Программист обязательно должен включить её при переводе процессора из реального в защищённый режим работы, иначе не видать нам четырёх гигов памяти. Если линия А20 отключена (а она отключена по-умолчанию), то после 20-битного адреса
Асматикам старой школы издавна не давала покоя мысль, поиметь 4 гига в реальном режиме. Это открывало большие перспективы, поскольку в R-моде нет многозадачности, и все ресурсы процессора полностью принадлежат только одному приложению. На современных процессорах, оно будет летать как истребитель. И нужно сказать, что энтузиастам это удалось! 4Gb памяти в реальном режиме это не сказка, а недокументированный режим, который назвали UnReal-Mode (можете погуглить).
Если коротко, то выделяется один сегментный регистр (как правило ES), который будет играть роль торпеды в 4Gb пространство. Для него подготавливается соответствующий дескриптор с базой(0) и лимитом в 4Gb. Запихать этот дескриптор в ES находясь в реальном режиме процессор не позволит, поэтому нужно перевести его в защищённый режим, обновить ES и вернуться обратно в R-моду. Теперь через ES можно гулять по 4G пространству, как у себя дома. Позже, когда приём вылез из хакерских нор и получил огласку, его взяли на вооружение чуть-ли не все досовские игры. В практической части мы ещё вспомним про UnReal, а пока топаем дальше..
2.1. Память защищённого режима
Процессор переводится в Рrotected-моду всего одним\нулевым битом в регистре
Процессоры изначально заточены под сегментную организацию памяти, это наследие докатилось и до наших дней. Более того, они не имеют механизмов запрещения сегментации, тогда-как страничную трансляцию можно включать\выключать битом(31) регистра управления процессором
По большому счёту, возиться с дескрипторами приходится только тем, кто пишет свою ОС. В остальных случаях они уже настроены системой и трогать их не имеет смысла. Поэтому обратим внимание только на основные моменты.
Это так.. к сведению. Лучше посмотрим на схему ниже..
В аппаратной части подсистемы памяти фигурируют 3 типа адресов: логический, линейный и физический. Из этой троицы, ЦП оперирует только логическим адресом, который использует в своих программах программист. Для трансляции остальных адресов, процессор имеет блок управления памятью MMU - Memory Management Unit.
Логический адрес состоит из сегмента+смещения, например
Таким образом, память в защищённом режиме работы процессора – вся линейна. Сегментные регистры служат только для защиты доступа к памяти. В дескрипторах всех сегментных регистров, всегда устанавливается база(0) с максимальным лимитом в 4Gb, и никак иначе. Транслятор страниц постоянно включён, и разбивает большие сегменты на мелкие страницы памяти, размером по 4К-байт каждая. Любая из страниц имеет свои атрибуты защиты, не считая защиты верхнего уровня родительских сегментов. Это огромная корпорация, которая следит за всеми, и жёстко пресекает любое нарушение установленных прав. Одним словом – защищённый режим это руль! ..который позже согнём в баранку.
----------------------------------------
PS\\: Кстати в отладчике OllyDbg можно наглядно увидеть значения дескрипторов в шести сегментных регистрах. Как-видим, особняком стоит тут только
В глазах процессора, мир существует в виде 1-байтных ячеек памяти. Будь-то физическое устройство (типа клавиатура), или наша логическая программа – для процессора это группа из нескольких байт. Считаю, что изучение ассемблера нужно начинать именно с организации памяти, поскольку 90% времени работать придётся именно с нею. Совсем не обязательно зубрить наизусть названия регистров – это придёт как озорение само-собой. Лучше направить энергию на изучение почвы, по которой собираемся ходить.Начало здесь: Ассемблер для х86
На данный момент х86 процессоры могут работать в четырёх режимах. Мы рассмотрим только первые два из них, как более распространённые:
- Real-Mode - реальный, системная поддержка в виде BIOS;
- Protected-Mode - защищённый, поддержка Win и никсы;
- Virtual-Mode - для эмуляции реального режима, из защищённого;
- System-Management-Mode – реальный из защищённого, без эмуляции.
2.0. Память в реальном режиме
В Real-моде, программист полноправный хозяин системы и имеет дело только с физической памятью. Ему доступны буквально все инструкции процессора, прямой доступ к портам оборудования, и многое другое. Здесь не задают вопросов типа “Вы уверены? Да/Нет”, и за необдуманные действия ответственность несёте только вы. Бонусом идёт программирование вообще без системы, на одних только функциях биос (благо его и сейчас эмулируют все EFI). Основной задачей ОС является как-раз абстрагирование пользователя от реальных устройств, которые он может по неосторожности вывести из строя. Одним словом, зевать от скуки тут не приходится, и нужно быть всегда на чеку.
У первых процессоров 8086 шина-адреса(A) была 20-битная. Такая ширина позволяет адресовать всего
FFFFF
байт памяти. Запускаем виндовый калькулятор, переводим его и BIN, и введём 20 единиц. Именно такой вид имела шина(А) на физическом уровне. Посмотрим на рисунок, где изображены проводники системной шины, как они идут с процессора. Шина эта – мультиплексная, т.е. единственная и для адреса, и для данных. Сначала на шину выставляется адрес, после которого по ней-же отправляются и данные:
Как видим, увеличение разрядности шины увеличивает кол-во адресуемых ячеек памяти. Это хорошо.. Но регистры ЦП были в то время 16-битными (без приставки ‘E’), и ими можно было дотянуться максимум до 64 Кб физической памяти (см.рис). Как вместить в 16-битный регистр 20-битный адрес? Было решено разбить всю память на логические сегменты, и адресовать её через регистровую пару в формате сегмент+смещение. Формула и вид получился такой:
Код:
Линейный адрес = (Seg * 16) + Offset
---------------------------------------
1111 1111 1111 1111 ---------> CS
| 1111 1111 1111 1111 ----> IP
| |
|<------ 20-бит ------>|
1111 1111 1111 1111 1111 ----> адрес = DS:SI, ES:DI
CS:IP, SS:SP
64К х16=1М
памяти.Однако это не табу, и ничто не мешает нам перевернуть ‘пластинку’ наоборот, ведь мы имеем дело с логическим адресом. Тогда получим 65.536 сегментов, по 16-байт в каждом. Эти 16-байт назвали ‘параграф’ и это мин.размер одного сегмента в R-моде. Такая картина позволяет операционной системе (например DOS) эффективно распределять память, которой и так с гулькин-нос. Сегменты по 64К бывают только на бумажке, а на практике - это просто макс предел, который выделяет ОС под один сегмент.
Во-первых, среди одного мегабайта, 64К непрерывной памяти у системы может и не быть, а во-вторых - если у программы в сегменте-данных всего три строчки текста, зачем выделять для них 64К памяти? Система просто выравнит эти данные до 16-байтного параграфа, и вручит их пользователю. Если (в процессе работы) программе понадобится ещё доп.памяти, то программист должен запросить её у системы, и она без проблем выделит из своего резерва кусок (если таковой остался).
На рисунке выше, я выделил красным одну из линий шины-адреса и вставил туда ключ. Эту линию назвали А20, поскольку она идёт следующей от 20-битного адреса 0-19. Программист обязательно должен включить её при переводе процессора из реального в защищённый режим работы, иначе не видать нам четырёх гигов памяти. Если линия А20 отключена (а она отключена по-умолчанию), то после 20-битного адреса
0xfffff
счётчик адресов опять сбросится в нуль, не давая нам вылезти из первого мегабайта. На аппаратном уровне линией А20 управляет чипсет, через порт 92h системной логики.Асматикам старой школы издавна не давала покоя мысль, поиметь 4 гига в реальном режиме. Это открывало большие перспективы, поскольку в R-моде нет многозадачности, и все ресурсы процессора полностью принадлежат только одному приложению. На современных процессорах, оно будет летать как истребитель. И нужно сказать, что энтузиастам это удалось! 4Gb памяти в реальном режиме это не сказка, а недокументированный режим, который назвали UnReal-Mode (можете погуглить).
Если коротко, то выделяется один сегментный регистр (как правило ES), который будет играть роль торпеды в 4Gb пространство. Для него подготавливается соответствующий дескриптор с базой(0) и лимитом в 4Gb. Запихать этот дескриптор в ES находясь в реальном режиме процессор не позволит, поэтому нужно перевести его в защищённый режим, обновить ES и вернуться обратно в R-моду. Теперь через ES можно гулять по 4G пространству, как у себя дома. Позже, когда приём вылез из хакерских нор и получил огласку, его взяли на вооружение чуть-ли не все досовские игры. В практической части мы ещё вспомним про UnReal, а пока топаем дальше..
2.1. Память защищённого режима
Процессор переводится в Рrotected-моду всего одним\нулевым битом в регистре
CR0
. Сразу-же о сегментной памяти реального режима можно забыть. Хотя сегменты по-прежнему и присутствуют, они несут в себе уже другую нагрузку. Адрес теперь не состоит из двух составляющих Seg:Offs
, а вся память приобретает плоский FLAT вид. В игру вступают защитные механизмы, которыми природа щедро одарила этот режим.Процессоры изначально заточены под сегментную организацию памяти, это наследие докатилось и до наших дней. Более того, они не имеют механизмов запрещения сегментации, тогда-как страничную трансляцию можно включать\выключать битом(31) регистра управления процессором
CR0
. У процессора всегда было и будет шесть сегментных регистров, которые он использует в качестве указателей на определённый блок памяти в Win32 программах:- CS – Code Segment – сегмент кода;
- DS – Data Segment – сегмент данных;
- SS – Stack Segment – сегмент стека;
- ES – Extended Segment – дополнительный для данных;
- GS – General Segment – добавлен для РМ-моды
- FS – Безымянный (сл.по алфавиту) для РМ-моды.
По большому счёту, возиться с дескрипторами приходится только тем, кто пишет свою ОС. В остальных случаях они уже настроены системой и трогать их не имеет смысла. Поэтому обратим внимание только на основные моменты.
- Каждый сегмент памяти описывает его собственный дескриптор. Размер дескриптора 8-байт (64-бита). В нём хранится линейный адрес начала сегмента в памяти (Base=32-бит), размер сегмента (Limit=20-бит), и атрибуты защиты (Access=12-бит).
- При обращении к сегменту, системные механизмы защиты сначала проверяют значение смещения, которое не должно превышать лимит. То есть адрес не должен выходить за пределы сегмента, в противном случае ЦП сгенерит исключение ‘AccessViolation’ - ошибка доступа.
- Как выделенными для лимита 20-ю битами определить размер сегмента в 4Gb? Ответ - лимит указывается в единицах измерения! Среди атрибутов, в дескрипторе имеется т.н. бит гранулярности(G). Если он сброшен, то единицей измерения считается байт, если взведён - то 4К-байтная страница. Отсюда следует, что при G=0 размер\лимит сегмента ограничен значением 1Мб, а при G=1 включается множитель 4Кб, и получаем 4Gb.
Это так.. к сведению. Лучше посмотрим на схему ниже..
В аппаратной части подсистемы памяти фигурируют 3 типа адресов: логический, линейный и физический. Из этой троицы, ЦП оперирует только логическим адресом, который использует в своих программах программист. Для трансляции остальных адресов, процессор имеет блок управления памятью MMU - Memory Management Unit.
Логический адрес состоит из сегмента+смещения, например
CS:EIP, DS:ESI, ES:EDI
и т.д.. MMU собирает эти составляющие, преобразуя лог.адрес в линейный, в диапазоне 0..N. Если в регистре CR0
включён страничный режим, то в дело вступает транслятор страниц в MMU. Если-же страничное преобразование выключено, то линейный адрес совпадает с физическим (здесь он выключен). На рисунке ниже, в сегментных регистрах лежит 2-байтный селектор сегмента, адрес процессора – это смещение, сегментацией и трансляцией занимается блок MMU процессора, РА – это PhysicalAddress:Таким образом, память в защищённом режиме работы процессора – вся линейна. Сегментные регистры служат только для защиты доступа к памяти. В дескрипторах всех сегментных регистров, всегда устанавливается база(0) с максимальным лимитом в 4Gb, и никак иначе. Транслятор страниц постоянно включён, и разбивает большие сегменты на мелкие страницы памяти, размером по 4К-байт каждая. Любая из страниц имеет свои атрибуты защиты, не считая защиты верхнего уровня родительских сегментов. Это огромная корпорация, которая следит за всеми, и жёстко пресекает любое нарушение установленных прав. Одним словом – защищённый режим это руль! ..который позже согнём в баранку.
----------------------------------------
PS\\: Кстати в отладчике OllyDbg можно наглядно увидеть значения дескрипторов в шести сегментных регистрах. Как-видим, особняком стоит тут только
FS
, который система всегда держит при себе, и программисту трогать его не советует. Это указатель на системную таблицу РЕВ - Process Environment Block, куда система сбрасывает информацию о текущем процессе – т.н. окружение процесса.