Что такое x86-64 и откуда это взялось?
Программные продукты часто снабжаются интересными аббревиатурами, значение которых знают далеко не все. Например, напротив той же 1С: Бухгалтерии в прайсе можно обнаружить приписку x86-64. И вроде как не сложно догадаться, что речь идёт об использовании программного обеспечения, адаптированного для многоядерных процессоров. Но почему оно так странно записано? А как тогда должны быть обозначены программы для одноядерных систем (если такие, конечно же, сейчас вообще используются).
Логично было бы обозначать одноядерные процессоры и системы значком х32, как это делалось когда-то на уже устаревшем железе, ну а многоядерные системы отмечать как x64, поскольку сначала всё начиналось с двух ядер в системе. Но нет, на программном продукте мы видим именно x86-64! Такое обозначение встречается и, например, на дистрибутивах операционных систем. Если скачивать Линукс, то обязательно увидите множество самых разных версий, в том числе и обозначенную выше.
Всё начинается с разрядности. Для того, чтобы понимать о чём вообще идёт речь, нужно в первую очередь ориентироваться на то, чем является разрядность операционной системы.
В информатике разрядность – это количество битов, которые могут быть одновременно обработаны данным устройством (в нашем случае ОС). На сегодняшний день существуют только две разрядности операционной системы. Логично предположить, что это х32 и х64. Тогда откуда берется странная запись х86…Оно и на 32 не делится, и на 64 тоже.
x86 — это не разрядность, а архитектура процессора. Под архитектурой понимается способность аппаратного выполнения набора команд. Ведь мы помним, что любой процессор по сути дела – это набор цепей из полупроводниковых транзисторов. Цепь – это некоторая логическая последовательность, которая на выходе даст определенный результат.
Само собой, что аппаратная специфика конструкции процессора, то есть его архитектура, влияют на методику программирования и выбор подхода. x86 может работать как с 32-битной версией, так и с 64-битной версией. Но чаще всего указание на х86 ассоциируется именно с 32-разрядной системой. Правда корректно было бы указывать ещё и разрядность.
Сама маркировка x86 пошла от названия первого процессора от компании Intel i8086 и более новых моделей.
Правильный вариант обозначения через архитектуру выглядел бы примерно так:
- для 32 разрядной операционной системы x86-32bit
- для 64 битной x86-64bit
Ничего не напоминает последняя запись? Это та самая запись, которую мы обозначили в заголовке заметки и со смыслом которой пытались разобраться. x86-64 – это 64-разрядная версия набора инструкций x86. Это значит, что это версия для многоядерных процессоров, которая работает с процессорами типа х86 и при этом имеет 64-разрядную версию. Говоря ещё более простым языком – это версия для “двухъядерного процессора” и если у вас установлена операционка Windows x64, то выбирать следует именно такую версию.
x86-64
x86-64 (x64/AMD64/Intel64/EM64T) — аппаратная платформа. Автором технологии является компания AMD. Платформа создана для выполнения 64-разрядных приложений.
Представляет собой расширение x86-архитектуры, обладает практически полной обратной совместимостью.
Компании Microsoft и Oracle применяют для индикации данного набора инструкций термин «x64», хотя каталог с файлами в дистрибутивах Linux имеет название «amd64» (в случае с архитектурой x86, именуется как «i386»).
На сегодняшний день, платформу x86-64 поддерживают следующие компании:
- AMD: процессоры Z-серии, C-серии, G-серии, E-серии, E1, E2, A4, A6, A8, A10, FX, Athlon 64, Athlon 64 FX, Athlon 64 X2, Athlon II, Phenom, Phenom II, Turion 64, Turion 64 X2, Turion II, Opteron, последние модели Sempron;
- Intel: реализует данную платформу, используя название «Intel 64» («EM64T» и «IA-32e»). Поздние модели процессоров Pentium 4, серии Pentium D, Pentium Extreme Edition, Celeron D, Celeron G-серии, Celeron B-серии, Pentium Dual-Core, Pentium T-серии, Pentium P-серии, Pentium G-серии, Pentium B-серии, Core 2 Duo, Core 2 Quad, Core 2 Extreme, Core i3, Core i5, Core i7, Atom и Xeon;
- VIA: Nano, Eden, QuadCore.
Название
Есть несколько вариаций названия данной технологии. Порой, противоречия в именовании платформы в каждой отдельной компании создают общую путаницу. Попробуем разобраться в терминологии:
- x86-64 — изначальный вариант названия, под которым компания AMD опубликовала первую предварительную спецификацию.
- x64 — название версий ОС Windows и Solaris. Используется в качестве названия для архитектуры компаний Microsoft и Sun Microsystems.
- AA-64(AMD Architecture 64) — название архитектуры, автором которого стал известный неофициальный справочник sandpile.org, по аналогии с IA-64.
- Hammer Architecture— название основывается на первых ядрах процессоров, поддерживавших данную архитектуру — Clawhammer и Sledgehammer.
- AMD64 — название технологии, данное фирмой-разработчиком — компанией AMD. Появилось после выпуска первых Clawhammer и Sledgehammer процессоров. На данный момент, является официальным названием для реализации от AMD.
- Yamhill Tehnology— первичное название аналогичной технологии от Intel. Порой также встречалось название CT (Clackamas Technology).
- EM64T(Extended Memory 64 Technology) — первое официальное название реализации Intel.
- IA-32e — название порой употреблялось вместе с EM64T. В основном, использовалось для обозначения «длинного» режима (в документации Intel именуется «режимом IA-32e»).
- Intel 64 — новое официальное название архитектуры Intel, использующееся в настоящее время. Intel потихоньку отходит от названий IA-32, IA-32e и EM64T, используя это. Теперь оно является единственным официальным названием данной архитектуры со стороны компании Intel.
В настоящее время, самыми распространенными названиями выступают: «x64», «x86-64» и «AMD64». Порой это путает AMD-пользователей — вплоть до отказа использования родных дистрибутивов ОС: пользователь не уверен, будет ли работать выбранная версия на AMD процессоре. На самом же деле, распространители программного обеспечения применяют название amd64 лишь для того, чтобы подчеркнуть, что именно компания AMD является новатором в разработке данной технологии.
Зачастую, пользователи сами путают архитектуру Intel 64 с IA-64. Важно помнить, что Intel 64 и IA-64 — совершенно разные, несовместимые между собой платформы:
- Представители Intel 64: последние версии Pentium 4, некоторые модели Celeron D, линейка Core 2, Core i3, Core i5, Core i7, некоторые модели Intel Atom;
- Представители IA-64: семейства Itanium и Itanium 2.
Режимы
Имеется два режима работы данной архитектуры:
«Long Mode»
«Длинный» режим, будучи родным для AMD64, позволяет воспользоваться всеми дополнительными преимуществами архитектуры. Для активации данного режима требуется 64-битная ОС. К примеру, Windows 7 x64 или 64-битные версии UNIX-подобных систем (GNU/Linux, FreeBSD, OpenBSD, NetBSD, Solaris, Mac OS X — от версии 10.4.7 и выше).
Данный режим дает возможность работать 64-битным программам, кроме того, доступна и обратная совместимость: имеется поддержка 32-битного кода для работы 32-битных приложений. Но 32-битные программы не имеют возможности читать 64-битные системные библиотеки, а также — наоборот. Для того, чтобы побороть эти ограничения, большая часть 64-разрядных операционных систем предоставляет два набора необходимых системных файлов (для родных 64-битных приложений и для их 32-битных версий). Такая же методика применялась в ранних 32-битных системах (Windows 95) для запуска 16-битных программ.
«Длинный» режим не содержит в себе некоторые «рудименты» x86-архитектуры. Например, режим виртуального 8086, сегментированную модель памяти, аппаратную мультизадачность, и прочее.
«Длинный» режим имеет одну яркую особенность: его активация производится посредством установки флага CR0.PG. Он применяется для включения страничного MMU (если это переключение разрешено (EFER.LME=1). Если переключение недопустимо, включение MMU происходит в «наследственном» режиме.
Так, исполнение 64-битного кода с запрещенным страничным преобразованием невозможно, что приводит к определенным сложностям в программировании, ведь при переключении из «длинного» в «наследственный» режим (и обратно) необходим двойной сброс MMU. Для этого код переключения должен быть в тождественно отображенной странице.
«Legacy Mode»
Этот режим зовется «наследственным» по той причине, что дает возможность процессору AMD64 работать с инструкциями, которые рассчитаны для x86-процессоров. Кроме того, в рамках активации данного режима, осуществляется полная совместимость с 32-битным кодом и соответствующими ОС.
Процессор в «наследственном» режиме работает точно так же, как стандартный x86-процессор. Дополнительные функции, доступные под архитектурой AMD64, становятся неактивны.
При активации данного режима, 64-битные программы и соответствующие операционные системы функционировать не будут.
Особенности
Набор инструкций x86-64 (позже — AMD64), выпущенный компанией AMD — это расширение Intel IA-32 (x86-32). Ключевая отличительная черта AMD64 кроется в поддержке 64-битных регистров общего назначения, а также 64-битных арифметических/логических операций над целыми числами и 64-битных виртуальных адресов.
Чтобы осуществить адресацию регистров, для команд введены специальные «префиксы расширения регистра». Специально для них был выбран диапазон кодов 40h-4Fh, использующихся для команд INC и DEC в 32-битных режимах. Команды INC и DEC в 64-битном режиме кодируются в общей двухбайтовой форме.
Платформа x86-64 включает в себя:
- Шестнадцать целочисленных 64-битных регистра общего назначения (RAX, RBX, RCX, RDX, RBP, RSI, RDI, RSP, R8 — R15);
- Восемь 80-битных регистров с плавающей точкой (ST0 — ST7);
- Восемь 64-битных регистров Multimedia Extensions (MM0 — MM7, общее пространство с регистрами ST0 — ST7);
- Шестнадцать 128-битных регистров SSE (XMM0 — XMM15);
- 64-битный указатель RIP и 64-битный регистр флагов RFLAGS.
Сегментирование
В ходе создания архитектуры x86-64, специалисты компании AMD пришли к решению окончательно покончить с главным «рудиментом» архитектуры x86 — сегментной моделью памяти. Она поддерживалась еще во времена 8086/80286.
При создании новой версии продукта виртуализации, программисты VMware обнаружили ряд непреодолимых трудностей. Загвоздки возникали в процессе реализации виртуальной машины для 64-битных систем. Чтобы отделить код монитора от кода «гостя», программа применяла механизм сегментации, но данная задача так и не была реализована на практике.
После ряда неудачных экспериментов, компания AMD возобновила ограниченный вариант сегментной организации памяти (начиная с ревизии D архитектуры AMD64). Это дало возможность запускать 64-битные операционные системы в виртуальных машинах. Компания Intel не стала следовать такому примеру. Чтобы осуществить проверку процессора на предмет возможности запуска 64-битных гостевых операционных систем, VMware снабжает продукты специальной утилитой.
Команды LAHF и SAHF, вырезанные изначально, были возвращены в систему команд из-за их активного использования в программном обеспечении виртуализации.
По мере развития средств аппаратной виртуализации (Intel VT, AMD-V), необходимость в сегментации постепенно утрачивается.
ООО «Альтербит», 197183, Санкт-Петербург, Комендантский проспект, 2 схема проезда
Телефон: (812) 309-2602 ← Звони если хочешь купить сервер, схд, компьютер
X86-64
x86-64 (также x64/AMD64/Intel64/EM64T) — 64-битная аппаратная платформа: архитектура микропроцессора и соответствующий набор инструкций и чипсет, разработанные компанией AMD. Это расширение архитектуры x86 с полной обратной совместимостью. Набор инструкций x86-64 в настоящее время поддерживается процессорами AMD Athlon 64, Athlon 64 FX, Athlon 64 X2,Phenom, Phenom II, Turion 64, Opteron, последними моделями Sempron. Этот набор инструкций поддержан компанией Intel (с незначительными дополнениями) под названием Intel 64 (ранее известные как EM64T и IA-32e) в поздних моделях процессоров Pentium 4, а также в Pentium D, Pentium Extreme Edition, Celeron D, Core 2 Duo, Core 2 Quad, Core i3, Core i5, Core i7 и Xeon. Корпорации Microsoft и Sun Microsystems используют для обозначения этого набора инструкций термин «x64», однако каталог с файлами для архитектуры в дистрибутивах Microsoft называется «amd64» (ср. «i386» для архитектуры x86).
Как правильно называть
Существует несколько вариантов названий этой технологии, которые, порой, приводят к путанице и могут ввести пользователя в заблуждение.
- x86-64. Первоначальный вариант. Именно под этим названием фирмой AMD была опубликована первая предварительная спецификация.
- x64 Официальное название версий операционных систем Windows и Solaris, также используемое как название архитектуры фирмами Microsoft и Sun Microsystems.
- AA-64. Так архитектуру окрестил популярный неофициальный справочник sandpile.org (внеся информацию практически сразу после публикаций первой предварительной спецификации) по аналогии с IA-64, и по-прежнему так её называющий, как AMD Architecture 64.
- Hammer Architecture. Иногда встречалось название по первым разрабатываемым ядрам процессоров, получившим названия Clawhammer (гвоздодёр) и Sledgehammer (кувалда) иногда называемых просто Hammer (молоток).
- AMD64. После выпуска первых Hammer’ов в названии архитектуры появилось название фирмы-разработчика Advanced Micro Devices. Сейчас является официальным для реализации AMD.
- Yamhill Tehnology. Первое название Intel’овской реализации технологии. Иногда упоминалось название CT (Clackamas Technology).
- EM64T. Первое официальное название реализации Intel. Расшифровывалось как Extended Memory 64 Technology.
- IA-32e. Иногда встречалось совместно с EM64T, чаще для обозначения длинного режима, который в документации Intel называется «режимом IA-32e».
- Intel 64. Текущее официальное название архитектуры Intel. Постепенно Intel отказывается от наименований IA-32, IA-32e и EM64T в пользу этого названия, которое теперь является единственным официальным для этой архитектуры.
На сегодняшний день наиболее распространёнными являются x64, x86-64 и AMD64. Порой упоминание AMD вводит пользователей в заблуждение, вплоть до того, что они отказываются скачивать дистрибутивы родных версий ОС, мотивируя это тем, что на их Intel-процессоре версия для AMD не пойдёт. На самом деле распространители ПО используют название amd64 лишь потому, что именно AMD была пионером в разработке этой технологии. Бывает, что пользователи путают архитектуру Intel 64 с IA-64, ошибочно скачивая ПО для этой архитектуры, и с удивлением обнаруживают, что программа не запускается. Во избежание подобных ошибок, всегда следует помнить, что Intel 64 и IA-64 это совершенно разные, несовместимые друг с другом, микропроцессорные архитектуры. Представители Intel 64 — это Pentium 4 (последние модели), ряд моделей Celeron D, семейство Core 2 и некоторые модели Intel Atom. Представители IA-64 — это семейства Itanium и Itanium 2.
Режимы работы
Процессоры архитектуры поддерживают два режима работы: Long mode («длинный» режим) и Legacy mode («наследственный», режим совместимости с x86).
Long Mode
«Длинный» режим — «родной» для процессоров AMD64. Этот режим позволяет воспользоваться всеми дополнительными возможностями, предоставляемыми архитектурой AMD64. Для использования этого режима необходима 64-битная операционная система, например, Windows XP Professional x64 Edition, Windows Vista x64, Windows 7 x64 или 64-битные варианты UNIX-подобных систем GNU/Linux, FreeBSD, OpenBSD, NetBSD (чистые 64-битные сборки, однако, есть возможность запуска 32-битных приложений), Solaris (смешанная 32/64 сборка с разными ядрами для 32- и 64-битных процессоров), Mac OS X (смешанная 32/64 сборка с 32-битным ядром, начиная с версии 10.4.7). Этот режим позволяет выполнять 64-битные программы; также (для обратной совместимости) предоставляется поддержка выполнения 32-битного кода, например, 32-битных приложений, хотя 32-битные программы не смогут использовать 64-битные системные библиотеки, и наоборот. Чтобы справиться с этой проблемой, большинство 64-разрядных операционных систем предоставляют два набора необходимых системных файлов: один — для родных 64-битных приложений, и другой — для 32-битных программ. (Этой же методикой пользовались ранние 32-битные системы — например, Windows 95 — для выполнения 16-битных программ). В «длинном» режиме упразднен ряд «рудиментов» архитектуры x86, таких, как режим виртуального 8086, сегментированная модель памяти (однако, осталась возможность использования сегментов FS и GS, что полезно для быстрого нахождения важных данных потока при переключении задач), аппаратная мультизадачность, а также ряд команд, как реализующих упраздненные возможности, так и работающие с BCD-числами, которые в новых программах практически не использовались. Среди особенностей «длинного» режима, следует отметить тот факт, что он активируется установкой флага CR0.PG, который используется для включения страничного MMU (при условии что такое переключение разрешено (EFER.LME=1), в противном случае просто произойдет включение MMU в «наследственном» режиме). Таким образом, невозможно исполнение 64-битного кода с запрещенным страничным преобразованием. Это создает определенные трудности в программировании, поскольку при переключении из «длинного» в «наследственный» режим и обратно (например, для вызова функций BIOS или DOS, монитором виртуальной машины, и т. д.) требуется двойной сброс MMU, для чего код переключения должен находиться в тождественно отображённой странице.
Legacy Mode
Данный «наследственный» режим позволяет процессору AMD64 выполнять инструкции, рассчитанные для процессоров x86, и предоставляет полную совместимость с 32/16-битным кодом и операционными системами. В этом режиме процессор ведёт себя точно так же, как x86-процессор, например Pentium 4, и дополнительные функции, предоставляемые архитектурой AMD64 (например, дополнительные регистры) недоступны. В этом режиме 64-битные программы и операционные системы работать не будут.
Особенности архитектуры
Разработанный компанией AMD набор инструкций x86-64 (позднее переименованный в AMD64) — расширение архитектуры Intel IA-32 (x86-32). Основной отличительной особенностью AMD64 является поддержка 16-ти 64-битных регистров общего назначения (против 8-и 32-битных в x86-32), 64-битных арифметических и логических операций над целыми числами и 64-битных виртуальных адресов. Для адресации новых регистров для команд введены так называемые «префиксы расширения регистра», для которых был выбран диапазон кодов 40h-4Fh, использующихся для команд INC и DEC в 32- и 16-битных режимах. Команды INC и DEC в 64-битном режиме должны кодироваться в более общей, двухбайтовой форме.
Архитектура x86_64 имеет:
- 16 целочисленных 64-битных регистра общего назначения (RAX, RBX, RCX, RDX, RBP, RSI, RDI, RSP, R8 — R15),
- 8 80-битных регистров с плавающей точкой (ST0 — ST7),
- 8 64-битных регистров Multimedia Extensions (MM0 — MM7, имеют общее пространство с регистрами ST0 — ST7),
- 16 128-битных регистров SSE (XMM0 — XMM15),
- 64-битный указатель RIP и 64-битный регистр флагов RFLAGS.
Смерть и возрождение сегментной модели организации памяти
Разрабатывая архитектуру x86-64, инженеры корпорации AMD решили навсегда покончить с главным «рудиментом» архитектуры x86 — сегментной моделью памяти, которая передавалась по наследству ещё со времён 8086/80286. Однако, как потом оказалось, они очень погорячились. Архитектура стала абсолютно невиртуализируемой. При разработке новой версии своего продукта для виртуализации программисты компании VMWare столкнулись с непреодолимыми трудностями при реализации 64-битной виртуальной машины. Поскольку, для отделения кода монитора от кода «гостя» программой использовался механизм сегментации, эта задача стала практически неразрешимой. Осознав свою ошибку, AMD вернула ограниченный вариант сегментной организации памяти начиная с ревизии D архитектуры AMD64, что позволило запускать 64-битные ОС в виртуальных машинах. Intel, однако, этому примеру не последовала, и поэтому ни на одном её процессоре, не поддерживающем средства аппаратной виртуализации, запустить 64-битную виртуальную машину нельзя. С целью проверки того, возможен ли на данном процессоре запуск 64-битных гостевых ОС или нет, VMWare предоставляет вместе со своими продуктами специальную утилиту. Также следует отметить, что первоначально попавшие «под нож» команды LAHF и SAHF, которые также активно используются ПО виртуализации, затем также были возвращены в систему команд. С распространением средств аппаратной виртуализации (Intel VT, AMD-V) потребность в сегментации вновь постепенно отпадет.
См. также
Ссылки
- Архитектура AMD64 (EM64T)
- Крис КасперскиАрхитектура x86-64 под скальпелем ассемблерщика, Журнал «Хакер»
- The world’s leading source for pure technical x86 processor information.
- EM64T на сайте Intel
- Форум «Разработка 64-битных приложений» на сайте Intel
- Архитектура Intel64
- Документация на сайте AMD, а именно:
- AMD64 Architecture Programmer’s Manual Volume 3: General-Purpose and System Instructions Rev 3.14
- AMD64 Architecture Programmer’s Manual Volume 4: 128-Bit Media Instructions Rev 3.10 (SSE)
- AMD64 Architecture Programmer’s Manual Volume 5: 64-Bit Media and x87 Floating-Point Instructions Rev 3.09 (FPU, MMX)
- и другие
Wikimedia Foundation . 2010 .
Unix2019b/Организация памяти на x86-64
Процессоры архитектуры x86-64 поддерживают два основных режима работы: Long mode («длинный» режим) и Legacy mode («унаследованный», режим совместимости с 32-битным x86).
Long mode
«Длинный» режим — «родной» для процессоров x86-64. Этот режим даёт возможность воспользоваться всеми дополнительными преимуществами, предоставляемыми архитектурой. Для использования этого режима необходима 64-битная операционная система.
Этот режим позволяет выполнять 64-битные программы; также (для обратной совместимости) предоставляется поддержка выполнения 32-битного кода, например, 32-битных приложений, хотя 32-битные программы не смогут использовать 64-битные системные библиотеки, и наоборот. Чтобы справиться с этой проблемой, большинство 64-разрядных операционных систем предоставляют два набора необходимых системных файлов: один — для родных 64-битных приложений, и другой — для 32-битных программ.
Когда вы используете 64-битную операционную систему (Windows, Linux или какую-либо другую), ваш процессор работает в длинном режиме. 32-битные ОС теряют популярность и используются всё реже, так как не позволяют использовать весь потенциал современного железа. Так, дистрибутив Ubuntu Linux уже начиная с версии 17.10 не выпускается в 32-битном исполнении. Windows Server 2008 стала последней серверной ОС от Microsoft, которая имела 32-битную версию, и Server 2012 существует только 64-битная.
Legacy mode
Данный «унаследованный» режим позволяет процессору выполнять инструкции, рассчитанные для процессоров x86, и предоставляет полную совместимость с 32-битным кодом и операционными системами. В этом режиме процессор ведёт себя точно так же, как x86-процессор, например Athlon или Pentium III, и дополнительные функции, предоставляемые архитектурой x86-64 (например, дополнительные регистры), недоступны. В этом режиме 64-битные программы и операционные системы работать не будут.
Этот режим включает в себя подрежимы:
- Реальный режим (real mode)
- Защищённый режим (protected mode)
- Режим виртуального 8086 (virtual 8086 mode)
Реальный режим использовался в MS-DOS, в реальном режиме выполнялся код BIOS при загрузке компьютера.
Защищённый режим используется в 32-битных версиях современных многозадачных операционных систем (например, обычная 32-битная Windows XP работает в защищённом режиме, как и 32-битная версия Ubuntu 16.04).
Режим виртуального 8086 — подрежим защищённого, предназначался главным образом для создания т. н. «виртуальных DOS-машин». Если из 32-битной версии Windows вы запускаете 16-битное DOS-приложение, то работает эмулятор NTVDM (NT Virtual DOS Machine), который использует этот режим процессора. Другой эмулятор, DOSBox, не использует этот режим V86, а выполняет полную эмуляцию. Заметим, что в 64-битных версиях Windows эмулятор NTVDM был исключён, поэтому напрямую запустить на выполнение 16-битный com- или exe-файл стало невозможно (тем не менее, можно использовать тот же DOSBox или другой гипервизор для полной эмуляции реального режима).
Переход между режимами
Из длинного режима нельзя перейти в реальный или режим виртуального 8086 без перезагрузки. Поэтому, как уже отмечено, в 64-битных версиях Windows не работает NTVDM и нельзя запускать 16-битные программы.
Самый современный процессор x86-64 полностью поддерживает реальный режим. Если загрузка выполняется через BIOS, то код загрузчика (из сектора #0) исполняется в реальном режиме. Однако если вместо BIOS используется UEFI, то переход в Long mode происходит ещё раньше, и никакого кода в реальном режиме уже не выполняется. Можно считать, что современный компьютер сразу начинает работать в 64-битном длинном режиме.
Поэтому далее нас будет интересовать только длинный режим.
Трансляция адресов в памяти
Упрощённо говоря, процессор обращается к памяти через шину. Адресами памяти, которыми обмениваются в шине, являются физические адреса, то есть сырые числа от нуля до верхней границы доступной физической памяти (например, до 2 33 , если у вас установлено 8 ГБ оперативки). Ранее между процессором и микросхемами памяти располагался северный мост — отдельный чип, но в реализации Intel начиная с микроархитектуры Sandy Bridge он интегрирован на кристалл процессора.
Физические адреса являются конкретными и окончательными — без трансляции, без подкачки, без проверки привилегий. Вы выставляете их на шину и всё: выполняется чтение или запись.
Однако в современной операционной системе программы используют абстрацкию — виртуальное адресное пространство. Каждая программа пишется в такой модели, что она выполняется одна, всё пространство принадлежит ей, код использует адреса логической памяти, которые должны быть оттранслированы в физические адреса до того, как будет выполнен доступ к памяти. Концептуально трансляция выглядит следующим образом:
Это не физическая схема, а только описание процесса преобразования адресов. Такая трансляция осуществляется всякий раз, когда CPU выполняет инструкцию, которая ссылается на адрес памяти.
Логический адрес на x86 состоит из двух частей: селектора сегмента и смещения внутри сегмента. Процесс трансляции включает два шага:
- учёт сегментного селектора и переход от смещения внутри сегмента к некоторому линейному адресу;
- перевод линейного адреса в физический.
Спрашивается, зачем нужен первый шаг и зачем нужны эти сегменты, почему бы напрямую не использовать линейные адреса в программе? Это результат эволюции. Чтобы действительно понять смысл сегментации x86, нам нужно вернуться в 1978 год.
Сегментация
Реальный режим
16-битный процессор 8086 использовал 16-битные регистры и мог напрямую адресовать только 2 16 байт памяти. Инженеры придумывали, как же можно заставить его работать с большим объёмом памяти, не расширяя разрядность регистров.
Были придуманы сегментные регистры, которые должны были задавать, к какому именно 64-килобайтному куску памяти относится данный 16-битный адрес.
Решение выглядит логичным: сначала вы устанавливаете сегментный регистр, по сути говоря “так, я хочу работать с куском памяти начиная с адреса X”; затем 16-битный адрес уже используется как смещение в рамках этого куска.
Всего предусматривалось сначала четыре 16-битных сегментных регистра, потом добавили ещё два:
- CS = Code Segment
- DS = Data Segment
- ES = Extra (или Destination) Segment
- SS = Stack Segment
- FS
- GS
Названия этих регистров связаны с назначением. При выполнении инструкций они загружаются из сегмента кода. При обращении к стеку (инструкции push/pop) неявно используется сегмент стека (при работе с регистрами SP и BP). Некоторые инструкции (так называемые «строковые») используют фиксированные сегменты, например инструкция movs копирует из DS:(E)SI в ES:(E)DI.
Для вычисления линейного адреса ячейки памяти процессор вычисляет физический адрес начала сегмента — умножает сегментную часть виртуального адреса на число 16 (или, что то же самое, сдвигает её влево на 4 бита), а затем складывает полученное число со смещением от начала сегмента. Таким образом, сегменты частично перекрывались, и всего можно было адресовать около 1 МБ физической памяти. Спрашивается, почему не умножать значение сегментного регистра сразу на 65536, ведь тогда можно было бы адресовать 4 ГБ памяти. Тогда это было не нужно и только растило стоимость чипа.
В реальном режиме отсутствует защита памяти и разграничение прав доступа.
Программы были маленькие, поэтому их стек и код полностью помещались в 64 КБ, не было проблем. В языке C тех древних времён обычный указатель был 16-битный и указывал относительно сегмента по умолчанию, однако существовали также far-указатели, которые включали в себя значение сегментного регистра. Призраки этих far-указателей преследуют нас в названиях типов в WinAPI (например LPVOID — long (far) pointer to void).
#include int main(){ char far *p =(char far *)0x55550005; char far *q =(char far *)0x53332225; *p = 80; (*p)++; printf("%d",*q); return 0; }Тут оба указателя указывают на один и тот же физический адрес 0x55555.
Защищённый режим
В 32-битном защищенном режиме также используется сегментированная модель памяти, однако уже организованная по другому принципу: расположение сегментов описывается специальными структурами (таблицами дескрипторов), расположенными в оперативной памяти.
Сегменты памяти также выбираются все теми же сегментными регистрами. Значение сегментного регистра (сегментный селектор) больше не является сырым адресом, но вместо этого представляет собой структуру такого вида:
Существует два типа дескрипторных таблиц: глобальная (GDT) и локальная (LDT). Глобальная таблица описывает сегменты операционной системы и разделяемых структур данных, у каждого ядра своя. Локальная таблица может быть определена для каждой конкретной задачи (процесса). Бит TI равен 0 для GDT и 1 для LDT. Индекс задаёт номер дескриптора в таблице дескрипторов сегмента. Поле RPL расшифровывается как Requested Privilege Level.
Сама таблица представляет собой просто массив, содержащий 8-байтные записи (дескрипторы сегмента), где каждая запись описывает один сегмент и выглядит так:
Помимо базового адреса сегмента дескрипторы содержат размер сегмента (точнее, максимально доступное смещение) и различные атрибуты сегментов, использующиеся для защиты памяти и определения прав доступа к сегменту для различных программных модулей. Базовый адрес представляет собой 32-битный линейный адрес, указывающий на начало сегмента, а лимит определяет, насколько большой сегмент. Добавление базового адреса к адресу логической памяти дает линейный адрес (никакого умножения на 16 уже нет). DPL (Descriptor Privilege Level) — уровень привилегий дескриптора; это число от 0 (наиболее привилегированный, режим ядра) до 3 (наименее привилегированный, пользовательский режим), которое контролирует доступ к сегменту.
Когда CPU находится в 32-битных режимах, регистры и инструкции могут в любом случае адресовать всё линейное адресное пространство. Итак, почему бы не установить базовый адрес в ноль и позволить логическим адресам совпадать с линейными адресами? Intel называет это «плоской моделью», и это именно то, что делают современные ядра операционных систем под x86. Это эквивалентно отключению сегментации.
Понятно, что раз таблицы GDT и LDT лежат в памяти, каждый раз ходить в них за базовым адресом долго. Поэтому сегментные дескрипторы кешируются в специальных регистрах в момент загрузки (в тот момент, когда происходит запись в сегментный селектор).
Местоположение GDT в памяти указывается процессору посредством инструкции lgdt.
Длинный режим
На архитектуре x86-64 в длинном (64-битном) режиме сегментация не используется. Для четырёх сегментных регистров (CS, SS, DS и ES) базовый адрес принудительно выставляются в 0. Сегментные регистры FS и GS по-прежнему могут иметь ненулевой базовый адрес (но он стал 64-битным и может быть установлен через отдельные моделезависимые регистры (MSR)). Это позволяет ОС использовать их для служебных целей.
Например, Microsoft Windows на x86-64 использует GS для указания на Thread Environment Block, маленькую структурку для каждого потока, которая содержит информацию об обработке исключений, thread-local-переменных и прочих per-thread-сведений. Аналогично, ядро Linux использует GS-сегмент для хранения данных per-CPU.
Посмотрим на таблицы сегментных дескрипторов. Таблица LDT на самом деле вышла из употребления и сейчас не используется. В таблице GDT в современных системах есть как минимум пять записей:
- Null — первая ячейка не используется (сделано, чтобы нулевое значение селектора было зарезервированным [1]);
- Kernel Code;
- Kernel Data;
- User Code;
- User Data.
Практика: просмотр регистров
(gdb) info registers rax 0x40052d 4195629 rbx 0x0 0 rcx 0x0 0 rdx 0x7fffffffde78 140737488346744 rsi 0x7fffffffde68 140737488346728 rdi 0x1 1 rbp 0x7fffffffdd80 0x7fffffffdd80 rsp 0x7fffffffdd80 0x7fffffffdd80 r8 0x7ffff7dd4e80 140737351863936 r9 0x7ffff7dea700 140737351952128 r10 0x7fffffffdc10 140737488346128 r11 0x7ffff7a32e50 140737348054608 r12 0x400440 4195392 r13 0x7fffffffde60 140737488346720 r14 0x0 0 r15 0x0 0 rip 0x400531 0x400531 eflags 0x246 [ PF ZF IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0
Мифы о сегментных регистрах
Сегментные регистры остались в прошлом, в 64-битном длинном режиме они окончательно выпилены. Это неправда. Регистры существуют, даже GDB их печатает.
Раньше сегментные регистры были 16-битные, а сейчас они уже 64-битные. Это неправда, регистры CS, SS, DS, ES и пр. всегда были 16-битными и остаются такими, у них нет расширенных E- или R-версий.
Сегодня в этих регистрах всегда записан ноль. Это тоже неправда, там записан индекс сегментного дескриптора в таблице, а уже в этом дескрипторе базовый адрес равен тождественно нулю. Из приведённого вывода GDB очевидно, что CS и SS не равны нулю. К тому же биты RPL играют свою роль.
Раз сегментные регистры не нужны, я могу в своей программе делать с ними разные арифметические операции. Это неправда, регистры не относятся к регистрам общего назначения и их не получится использовать в качестве операндов в любых инструкциях.
Ладно, я смогу использовать их для хранения произвольных данных, а именно 16-битных чисел. Так тоже не выйдет, потому что при любом обращении к памяти процессор всё равно обращается к сегментному селектору и проверяет сегментный дескриптор.
Кольца защиты
Вы, вероятно, знаете интуитивно, что приложения имеют ограниченные полномочия на компьютерах Intel x86 и что только код операционной системы может выполнять определенные задачи. Как это работает? Уровни привилегий x86 — механизм, с помощью которого ОС и ЦП ограничивают возможности программ пользовательского режима.
Существует четыре уровня привилегий: от 0 (наиболее привилегированных) до 3 (наименее привилегированных). В любой момент времени процессор x86 работает на определенном уровне привилегий, который определяет, что код может и не может сделать. Эти уровни привилегий часто описываются как защитные кольца, причем самое внутреннее кольцо соответствует самым высоким привилегиям. [2]
Большинство современных ОС на x86 используют только Ring 0 и Ring 3.
Кольца управляют доступом к памяти. Код с уровнем привилегий i может смотреть только данные уровня i и выше (менее привилегированных).
На кольце 0 можно делать всё. На кольце 3, например, нельзя:
- изменить текущее кольцо защиты (иначе весь механизм был бы бесполезен);
- изменить таблицу страниц;
- зарегистрировать обработчик прерываний;
- выполнить ввод-вывод инструкциями in и out;
- .
Текущий уровень привилегий (CPL) определяется сегментным дескриптором кода. Если сейчас исполняется сегмент кода с уровнем привилегий 3, значит, исполняется пользовательский код. Если исполняется код с уровнем привилегий 0 — исполняется код ядра.
При обращении к памяти проверяется неравенство
max(CPL, RPL) ≤ DPL,
- CPL — текущий уровень привилегий,
- RPL — записан в сегментном регистре (селекторе),
- DPL — записан в сегментном дескрипторе.
Еси неравенство ложно, генерируется ошибка general protection fault (GPF).
Как мы ранее выяснили, системные вызовы позволяют менять текущий уровень привилегий, поэтому эта операция достаточно тяжёлая.
Страничная организация памяти
Страничная память — способ организации виртуальной памяти, при котором виртуальные адреса отображаются на физические постранично.
В семействе x86 поддержка появилась с поколения 386, оно же первое 32-битное поколение.
Если сегментация сейчас практически не используется, то таблицы страниц, наоборот, используются вовсю во всех современных операционных системах. Его важно понимать, так как с особенностями страничной организации можно прямо или косвенно столкнуться при решении прикладных задач.
Страницы
Виртуальная память делится на страницы. Размер размера страницы задается процессором и обычно на x86-64 составляет 4 KiB. Это означает, что управление памятью в ядре выполняется с точностью до страницы. Когда вам понадобится новая память, ядро предоставит вам одну или несколько страниц. При освобождении памяти вы вернёте одну или несколько страниц. Каждый более гранулярный API (например malloc) реализуется в пространстве пользователя.
Физическая память также поделена на страницы.
Виртуальное адресное пространство
Хотя виртуальные адреса имеют разрядность в 64 бита, текущие реализации (и все чипы, которые находятся на стадии проектирования) не позволяют использовать всё виртуальное адресное пространство из 2 64 байт (16 экзабайт). Это будет примерно в четыре миллиарда раз больше виртуального адресного пространства на 32-битных машинах. В обозримом будущем большинству операционных систем и приложений не потребуется такое большое адресное пространство, поэтому внедрение таких широких виртуальных адресов просто увеличит сложность и расходы на трансляцию адреса без реальной выгоды. Поэтому AMD решила, что в первых реализациях архитектуры фактически при трансляции адресов будут использоваться только младшие 48 бит виртуального адреса.
Кроме того, спецификация AMD требует, что старшие 16 бит любого виртуального адреса, биты с 48-го по 63-й, должны быть копиями бита 47 (по принципу sign extension). Если это требование не выполняется, процессор будет вызывать исключение. Адреса, соответствующие этому правилу, называются «канонической формой». Канонические адреса в общей сложности составляют 256 терабайт полезного виртуального адресного пространства. Это по-прежнему в 65536 раз больше, чем 4 ГБ виртуального адресного пространства 32-битных машин.
Это соглашение допускает при необходимости масштабируемость до истинной 64-разрядной адресации. Многие операционные системы (включая семейство Windows NT и GNU/Linux) берут себе старшую половину адресного пространства (пространство ядра) и оставляют младшую половину (пользовательское пространство) для кода приложения, стека пользовательского режима, кучи и других областей данных. Конструкция «канонического адреса» гарантирует, что каждая совместимая с AMD64 реализация имеет, по сути, две половины памяти: нижняя половина «растет вверх» по мере того, как становится доступнее больше виртуальных битов адреса, а верхняя половина — наоборот, вверху адресного пространства и растет вниз.
Первые версии Windows для x64 даже не использовали все 256 ТБ; они были ограничены только 8 ТБ пользовательского пространства и 8 ТБ пространства ядра. Всё 48-битное адресное пространство стало поддерживаться в Windows 8.1, которая была выпущена в октябре 2013 года.
Структура таблицы страниц
Ставится задача транслировать 48-битный виртуальный адрес в физический. Она решается аппаратным обеспечением — блоком управления памятью (memory management unit, MMU). Этот блок является частью процессора. Чтобы транслировать адреса, он использует структуры данных в оперативной памяти, называемые таблицами страниц.
Вместо двухуровневой системы таблиц страниц, используемой системами с 32-битной архитектурой x86, системы, работающие в длинном режиме, используют четыре уровня таблицы страниц.
Возможные размеры страниц:
- 4 КБ (2 12 байт) — наиболее часто используется (как и в x86)
- 2 МБ (2 21 байт)
- 1 ГБ (2 30 байт)
Пусть для определённости размер страницы равен 4 КБ. Значит, младшие 12 битов адреса кодируют смещение внутри страницы и не изменяются, а старшие биты используются для определения адреса начала страницы.
CR3 — это специальный регистр процессора. В записях каждой таблицы лежит физический адрес начала таблицы следующего уровня.
Полная иерархия сопоставления страниц размером 4 КБ для всего 48-битного пространства займет немногим больше 512 ГБ ОЗУ (около 0.195% от виртуального пространства 256 ТБ).
Кеширование
Таблицы страниц хранятся в оперативной памяти. Если при каждом обращении по виртуальному адресу выполнять полностью трансляцию адресов, это будет работать очень медленно. Поэтому в процессоре реализуется специальный кеш под названием «буфер ассоциативной трансляции» (Translation lookaside buffer, TLB).
На практике вероятность промаха TLB невысока и составляет в среднем от 0,01% до 1%.
Практика: как скоро оно упадёт?
Понятно, что данный код по стандарту некорректен, содержит Undefined Behavior, а раз так, то компилятор может сделать что угодно, например не упасть вообще. Но тем не менее, если запускать на x86-64, то падает оно в определённый момент.
#include #include char buf[1]; #define PAGE_SIZE 4096 int main() { char* ptr = buf; for (;;) { int offset = (intptr_t)ptr % PAGE_SIZE; printf("%p: offset = %d\n", ptr, offset); *ptr = 'a'; // Segmentation fault expected! ++ptr; } return 0; }Литература