Skip to content

Лекция 5

Inspirate789 edited this page May 15, 2022 · 3 revisions

32-разрядные процессоры (386+).

Производство x86: 1985 - ~2010 годы.

32-разрядными в них являются:

  • Регистры, кроме сегментных (они всё так же 16-битные)
  • Шина данных
  • Шина адреса (2^32 = 4Гб ОЗУ), то есть теперь можно использовать не 2 регистра для хранения адреса, а всего 1

Режимы работы.

8086 (1978 г.) -> 80186 (1982 г.) -> 80286 (1982 г.) добавлен защищённый режим -> 80386 (1985 г.) архитектура стала 32-разрядной -> 80486 (1989 г.) -> Pentium -> … -> (современные процессоры)

"Реальный" режим (режим совместимости с 8086):

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

~10 лет назад (а может быть и сейчас тоже) при включении компьютера 32-разрядные процессоры включались именно в реальном режиме работы, то есть им был доступен всего 1 Мб памяти, они могли загружать DOS.

А если ОС была более современной, чем DOS, то для её загрузки нужен специальный загрузчик, который перевёл бы процессор из реального режима работы в защищённый с помощью специальных команд и использовал бы уже все доступные возможности процессора. То есть, теперь основной режим работы процессора - защищённый, в который ОС переключает процессор сразу в начале загрузки. В таком случае процессору доступно много памяти (не 1 Мб, а вся оперативная память).

"Защищённый" режим.

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

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

Режим V86

  • Режим виртуального 86-го процессора
  • 32-разрядный процессор может временно переключаться в него из защищённого режима в рамках какой-то задачи, чтобы выполнять старые DOS'овские программы. То есть 32-разрядный процессор может нативно поддерживать старые 16-битные приложения без перезапуска этих приложений, а просто переключая режим работы процессора. Современные 64-разрядные процессоры в основном режиме работы уже не поддерживают такую совместимость (вроде как), поэтому для запуска 16-битных приложений нам приходится использовать эмулятор (DOSBox).

Есть ещё и другие режимы...

Регистры x86.

pic_01

  • В плане архитектуры с точки зрения прикладного (не системного) программиста или обычного пользователя принципиально ничего не поменялось.
  • Регистры доступны в том же самом наборе (регистры общего назначения, регистр флагов, индексные и указательные регистры, и т.д.). Только теперь они расширились до 32 бит и у их названий появилась приставка E (Extending). При этом всё также доступны их младшие половинки (AX, BX, CX и т.д., а также их половинки - AH, AL, BH, BL, CH, CL и т.д.).
  • Сегментные регистры больше не доступны программисту. Теперь ими управляет только операционная система.
  • Добавлено несколько новых групп регистров (их больше, чем исходное количество регистров).
  • В процессоре может быть до нескольких десятков машинно-специфичных регистров. Их состав зависит от конкретной модели процессора.

Система команд.

  • Аналогична системе команд 16-разрядных процессоров
  • Доступны как прежние команды обработки 8- и 16-разрядных аргументов, так и 32-разрядных регистров и переменных
  • Пример:
mov eax, 12345678h
xor ebx, ebx
mov bx, 1
add eax, ebx ; eax=12345679h

Модели памяти.

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

  • Плоская (самая примитивная) - код и данные используют одно и то же пространство.
  • Сегментная (была и раньше) - сложение сегмента и смещения
  • Страничная (основной вид в современных ОС) - виртуальные адреса отображаются на физические адреса постранично. Страница (!= сегмент) - некоторый блок памяти (более мелкий, чем сегмент), который может иметь разные размеры.
    • Виртуальная память — метод управления оперативной (внутренней) памятью компьютера, позволяющий выполнять программы, требующие больше оперативной памяти, чем имеется в компьютере, путём автоматического перемещения частей программы между основной памятью и вторичным хранилищем (файл на диске, или раздел подкачки - swap)
    • Основной режим для большинства современных ОС
    • В x86 минимальный размер страницы - 4 Кб (может принимать и другие значения)
    • Основывается на таблице страниц (аналогия с таблицей векторов прерываний в 1-ом килобайте оперативной памяти) - структуре данных (произвольного размера), используемой системой виртуальной памяти в операционной системе компьютера для хранения сопоставления между виртуальным адресом и физическим адресом (для того чтобы процессор мог по виртуальному адресу получить физический и обратиться к нужной ячейке памяти). Виртуальные адреса используются выполняющимся процессом, в то время как физические адреса используются аппаратным обеспечением (процессором). Таблица страниц является ключевым компонентом преобразования виртуальных адресов, который необходим для доступа к данным в памяти.
OFFTOP:
В Linux можно создавать и файл подкачки, и раздел подкачки. Чаще делается второе (раздел */swap*).
В Windows в корне диска C лежит большой скрытый файл с расширением .sys (очень неточная информация)
и называется вроде как swap... Короче просто загуглите...

Что подразумевается под словом "подкачка":

Если программа требует больше оперативной памяти, чем есть в распоряжении компьютера, то ОС автоматически находит страницы программ, к которым давно не обращались, выгружает их из оперативной памяти, помечает их как выгруженные в специальном каталоге (чтобы к ним нельзя было обратиться, потому что их уже нет в памяти) и выгружает на жёсткий диск. Освобождённая оперативная память отдаётся другой программе, которая её запросила. В дальнейшем, если выгруженная страница потребуется (произойдёт обращение к виртуальному адресу, который попадёт в страницу, которая помечена как выгруженная), ОС найдёт какую-то другую страницу, которая не используется, перепишет её в файл подкачки, а из подкачки достанет ту страницу, к которой обратились.

Страничная организация памяти.

pic_02

  • Виртуальные адреса (в верхней части картинки) тоже 32-разрядные и состоят из 3 частей (10, 10 и 12 бит соответственно).
  • Конвертацией виртуальных адресов в физические занимается процессор.

Главное достижение архитектуры x86 (за период примерно с 1978 по 1985) - механизм разделения прав доступа на аппаратном уровне, разделение программ между собой и разделение операционной системы и прикладных программ. Сюда же включается конвертация виртуальных адресов в физические при каждом обращении в память.

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

Казалось бы, это должно было замедлить компьютер в 3 раза, но этого не произошло, потому что в процессорах реализован специальный механизм хеширования каталогов таблиц страниц и самих таблиц страниц с помощью специального буфера. Но углубляться дальше в аппаратное обеспечение мы будем уже на операционных системах с Рязановой...

Управление памятью в x86.

  • В сегментных регистрах - селекторы. Сегментные регистры теперь хранят не номера параграфов (памяти стало гораздо больше, и номер параграфа нам теперь не нужен), а селекторы, включающие в себя:
    • 13-разрядный номер дескриптора
    • 14-й бит: признак того, какую таблицу страниц использовать - глобальную (общесистемную) или локальную (текущей прикладной программы - текущего процесса)
    • 15-й и 16-й биты: уровень привилегий запроса (0-3), который требуется для работы с текущим сегментом.

      Эти числа от 0 до 3 используются для определения привилегий в защищённом режиме работы процессора и называются кольцами защиты или уровнями защиты. На 0 уровне находится операционная система, её ядро (в зависимости от того, является оно монолитным или модульным, то есть либо вся ОС работает на этом уровне привилегий, либо в некоторых случаях её драйвера выносятся на 1 и 2 уровень). На 3 уровне (минимальном) находятся все прикладные программы, то есть они не могут обращаться к памяти, требующей более высокий уровень привилегий.

  • По селектору (а точнее, по номеру дескриптора) определяется запись в одной из таблиц дескрипторов сегментов
  • При включённом страничном режиме - по таблице страниц определяется физический адрес страницы либо выявляется, что она выгружена из памяти, срабатывает исключение (специальный вид прерываний) и операционная система подгружает затребованную страницу из файла/раздела "подкачки" (swap)

Поддержка многозадачности.

32-разрядные процессоры в защищённом режиме отличаются от предшественников ещё и поддержкой многозадачности.

До этого мы знали 3 вида сегментов: кода, данных и стека. Теперь добавляется 4-й.

TSS (Task State Segment — сегмент состояния задачи) — специальная структура в архитектуре x86, содержащая информацию о задаче (процессе).

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

Как реализуется многозадачность на аппаратном уровне:

  • Если в процессоре есть несколько ядер, то напрашивается решение "в лоб": на каждом ядре выполнять свою задачу. Но многозадачность появилась до многоядерности, да и процессов в ОС намного больше, чем ядер в процессоре, поэтому этот вариант отпадает.
  • Многозадачность реализуется с помощью планировщика задач (элемент ОС), который по прерыванию таймера (с очень высокой частотой) переключает программы по некоторому алгоритму (по кругу или в зависимости от приоритетов: какие-то более важные включать чаще, а остальные реже). Таким образом создаётся иллюзия того, что все программы работают одновременно, но на самом деле они работают по очереди с очень большой частотой. Сегмент TSS как раз используется для вытеснения задач с сохранением их текущего состояния (значения регистров и т.д.).

Исключения - новый вид прерываний.

Исключения (Exceptions) подразделяются на отказы, ловушки и аварийные завершения.

Отказ (fault) — это исключение, которое обнаруживается и обслуживается до выполнения инструкции, вызывающей ошибку.

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

Ловушка (trap) — это исключение, которое обнаруживается и обслуживается после выполнения инструкции, его вызывающей.

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

Аварийное завершение (abort) — это исключение, которое не позволяет точно установить инструкцию, его вызвавшую.

  • Оно используется для сообщения о серьезной ошибке, такой как аппаратная ошибка или повреждение системных таблиц.
  • Требует какой-то критической реакции вплоть до остановки операционной системы.

Регистр EFLAGS.

FLAGS + 5 специфических флагов (системные флаги для работы в защищённом режиме).

То есть этот регистр тоже стал 32-разрядным, но не все из 16 новых флагов используются.

Регистры управления памятью.

  • GDTR (Global Descriptor Table Register - регистр глобальной таблицы дескрипторов): 6-байтный регистр, содержит 32-битный линейный адрес начала таблицы глобальных дескрипторов (GDT) и 16-битный размер этой таблицы (лимит, уменьшенный на 1)
  • IDTR (Interrupt Descriptor Table Register - регистр таблицы дескрипторов прерываний): 6-байтный регистр, содержит 32-битный линейный адрес начала таблицы глобальных дескрипторов обработчиков прерываний (IDT) и 16-битный размер (лимит, уменьшенный на 1). Теперь таблица прерываний может располагаться где угодно (не обязательно по нулевому адресу) и описывается специальным регистром IDTR
  • LDTR (Local Descriptor Table Register - регистр локальной таблицы дескрипторов): 10-байтный регистр, содержит 16-битный селектор для GDT и весь 8-байтный дескриптор из GDT, описывающий текущую таблицу локальных дескрипторов
  • TR (Task Register): 10-байтный регистр, содержит 16-битный селектор для GDT и весь 8-байтный дескриптор из GDT, описывающий то, где искать TSS текущей задачи

Все эти наборы регистров содержатся в каждом аппаратном ядре (ядре процессора) и управляются независимо друг от друга.

Регистры управления процессором.

  • CR0 - флаги управления системой
    • PG - включение режима страничной адресации
    • Несколько флагов для управления отдельными параметрами кеша
    • WP - запрет записи в страницы "только для чтения"
    • NE - ошибки FPU вызывают исключение, а не IRQ13
    • TS - устанавливается процессором после переключения задачи
    • PE - включение защищённого режима (если его не выставить, то по умолчанию 32-разрядный процессор при включении будет работать в реальном режиме)
  • CR1 - зарезервирован (либо мы не можем обращаться к нему, либо он предназначен для хранения служебных данных на аппаратном уровне, к которым мы не имеем доступ и которые нам не нужны)
  • CR2 - регистр адреса ошибки страницы - содержит линейный адрес страницы, при обращении к которой произошло исключение #PF (эта ошибка может возникнуть при обращении к выгруженной из памяти страницы и вызвать исключение, при котором процессор поместит в CR2 адрес страницы, которой нам сейчас не хватает и которую надо загрузить)
  • CR3 - регистр основной таблицы страниц (содержит адрес основной таблицы страниц и режим работы этих адресов, который будет говорить о том, какого размера будут страницы - 4 Кб или больше)
    • 20 старших бит физического адреса начала каталога таблиц либо 27 старших бит физического адреса начала таблицы указателей на каталоги страниц, в зависимости от бита PAE в CR4
    • Управление кешированием и сквозной записью страниц
  • CR4 - регистр управления новыми возможностями процессоров (начиная с процессора Pentium, т.е. с 90-х годов)

Отладочные регистры.

  • DR0..DR3 - 32-битные линейные адреса четырёх возможных точек останова по доступу к памяти
  • DR4, DR5 - зарезервированы
  • DR6 (DSR) - регистр состояния отладки. Содержит причину останова
  • DR7 (DCR) - регистр управления отладкой. Управляет четырьмя точками останова

Отладочные регистры нужны для отладчиков. Но мы отладчики писать не будем, поэтому подробно рассматривать эти регистры нет смысла.

Машинно-специфичные регистры.

Появлялись по мере развития процессоров от 386 до современных.

Эта специфика обеспечивает возможность использования актуальных параметров процессора.

Категории:

  • Регистры управления кешем
  • Регистры дополнительного управления страничной адресацией
  • Регистры расширений процессора: MMX, SSE и т.д.

Системные и привилегированные команды.

Дополнительные команды, позволяющие управлять системными регистрами, доступными в процессоре.

  • Выполнение ограничено, в основном, нулевым кольцом защиты
  • Выполнять их может только ядро операционной системы, прикладным программам они недоступны

Команды:

  • LGDT, SGDT - загрузка/выгрузка глобальной таблицы дескрипторов
  • LLDT, SLDT - загрузка/выгрузка локальной таблицы дескрипторов
  • LTR, STR - загрузка/выгрузка регистра задачи (Load Task Register / Store Task Register)
  • LIDT, SIDT - загрузка/выгрузка таблицы дескрипторов прерываний
  • MOV CR0..CR4 или DR0..DR7, <источник> - запись данных в регистры управления процессором (только на нулевом кольце защиты), перенастройка страничной адресации и т.д.
  • ...

Страничная адресация - преобразование линейного адреса в физический (пример).

  • Линейный адрес состоит из 3 частей:
    • биты 31-22 - номер таблицы страниц в каталоге
    • биты 21-12 - номер страницы в выбранной таблице
    • биты 11-0 - смещение от физического адреса начала страницы в памяти
  • Каждое обращение к памяти требует двух дополнительных обращений!
  • Необходим специальный кеш страниц - TLB
  • Каталог таблиц/таблица страниц:
    • биты 31-12 - биты 31-12 физического адреса таблицы страниц либо самой страницы
    • атрибуты управления страницей

Механизм защиты.

Механизм защиты - ограничение доступа к сегментам или страницам в зависимости от уровня привилегий. Это ограничение реализовано на аппаратном уровне.

К типам сегментов реального режима (код, стек, данные) добавляется TSS - сегмент состояния задачи. В нём сохраняется вся информация о задаче на время приостановки её выполнения, то есть когда планировщик задач передаёт управление другому процессу. Размер - 68h байт.

Структура TSS:

  • Селектор предыдущей задачи
  • Регистры стека (пары ESS:ESP) для 0, 1, 2 уровней привилегий
  • Все регистры нашей прикладной программы (включая регистр стека 3-го уровня защиты, где непосредственно работает задача)EIP, EFLAGS, EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI, CS, DS, ES, FS, HS, SS, LDTR
  • флаги задачи
  • битовая карта ввода-вывода (контроль доступа программы к устройствам) - структура, определяющая полномочия текущей задачи (программы) по её возможности её обращения к внешним устройствам. Там отдельными битами настраивается то, к каким устройствам / группам устройств (к какому диапазону портов ввода-вывода) наша программа может обратиться. С помощью битовой карты ОС может разрешать/запрещать программам взаимодействовать с устройствами напрямую.

64-разрядные процессоры (x86-64) AMD - с 2001, Intel - с 2003.

x86-64 === AMD64

OFFTOP:
Почему AMD обогнал Intel? 
Примерно в это же время Intel разрабатывал ещё и свою собственную архитектуру - IA64
(не имеет ничего общего с x86-64). Она предусматривала новую систему команд, практически
полностью сломали совместимость с более старыми программами, сделали более современные
процессоры, на которых почти 20 лет выпускались серверные системы. Но они не очень широко
использовались из-за проблем с совместимостью, что вынуждало переписывать (или
оптимизировать/перекомпилировать) все программы под новое железо. А это очень трудоёмкая
задача. В итоге нужное количество софта под IA64 так и не было написано, и эта архитектура
окончательно прекратила своё существование несколько лет назад. А вот расширения архитектуры
8086 (1978 года) до сих пор выпускаются. 64-разрядная архитектура AMD победила, и чуть позже
в Intel сделали такие же процессоры (можно сказать, по стандартам AMD).
  • Основные режимы работы:
    • Legacy mode - совместимость с 32-разрядными процессорами (32-разрядный режим работы, в который вкладываются режимы V86 и т.д.)
    • Long mode – 64-разрядный режим с частичной поддержкой 32-разрядных программ (полная поддержка была бы очень накладной). Рудименты V86 и сегментной модели памяти упразднены
  • Регистры:
    • целочисленные 64-битных регистры общего назначения - RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP (их 32- и 16-разрядные части также доступны);
    • новые целочисленные 64-битных регистры общего назначения R8 — R15
    • 64-битный указатель RIP и 64-битный регистр флагов RFLAGS.

Соглашения о вызовах.

Соглашение о вызове — формализация правил вызова подпрограмм, которая должна включать:

  • способ передачи параметров;
  • способ возврата результата из функции;
  • способ возврата управления.

Соглашения о вызовах определяются в рамках отдельных языков высокого уровня, а также - различных программных API, в т. ч. API операционных систем.

В Си есть cdecl, причём как 32-, так и 64-разрядный (cdecl32, cdecl64), которые различаются не только размером, но и способом передачи параметров: в cdecl32 - через стек, а в cdecl64 - через часть регистров общего назначения (неточная информация). При чём через регистры общего назначения возвращаются только небольшие значения, большие значения возвращаются через указатели. Также есть и другие соглашения о вызовах (syscall, stdcall и др.), которые, помимо прочего, могут различаться порядком передачи параметров в подпрограммы (слева направо или справа налево).

За более точной информацией лучше обратиться к лекциям 3 семестра по Си...

Пример ассемблерной вставки в программе на Си.

Ассемблерная вставка - способ написания фрагмента кода на ассемблере в программе на языке высокого уровня.

#include <stdio.h>
#include <stdlib.h>

int main()
{
  printf("Hello World!\n");
  int src = 1;
  int dst;

  asm ("mov %1, %0\n\t" // записали src в dst (%0 и %1 - нулевой и первый параметры)
      "add $2, %0"      // прибавили 2 к dst ($2 - число 2, константа)
      : "=r" (dst)
      : "r" (src));
  
  printf("%d\n", dst);
  return 0;
}

Пожалуйста, не бейте автора за кодстайл! Это писал Дмитрий Александрович.

$ gcc -Wall -Werror -Wextra -Wpedantic -c example.c
$ gcc example.o -o example.exe
$ ./example.exe
Hello World!
3

Если писать под Visual Studio, то у оператора asm надо ставить фигурные скобки, а не круглые. И там можно внутри писать ассемблерные команды в такой же нотации, как у MASM'а. А если писать под GCC (под оболочкой MinGW), как в примере, то придётся использовать другую нотацию (другой синтаксис): в частности, обратный порядок аргументов.

Clone this wiki locally