Архитектура x64 что это
Десять имён для одной архитектуры
На пятничном семинаре учебного проекта лаборатории МФТИ-Интел один из студентов задал мне примерно такой вопрос: а почему 64-битный вариант архитектуры процессоров Intel называется x64, а 32-битный — x86? Я начал объяснять, что не всё так просто. Захотелось нарисовать более полную картину. Ведь на самом деле это не x64, и даже не x86.
386-ые, Пентиумы и Коры
На самом деле названий для этого феномена, около сорока лет присутствующего на сцене процессорных технологий, было придумано несколько. Даже больше, чем хотелось бы. Они появились из разных источников и используются в разных контекстах, разными компаниями и разными сообществами. Конечно же, это вносит некоторую неразбериху.
Я постарался здесь собрать все известные мне названия. Не хочу пытаться доказать, что одна группа имён лучше другой, — меньше использовать их не станут.
8086 и семейство
В 1978 году был выпущен 16-битный процессор Intel, который имел «имя» 8086. За ним были 8088, 80186, 80286, 80386 (плюс вариации), 80486 (плюс вариации). Легко заметить, что (почти) все эти числовые имена оканчиваются на две цифры 86, что дало название всей серии x86. Оно укрепилось, его продолжили использовать и после того, как процессорам перестали давать цифровые имена, а появились Intel Pentium, Celeron, Xeon, Core, Atom и т.д. Совместимые продукты других вендоров, таких как IBM, AMD, Cyrix, VIA и т.д., также описываются как x86.
По моим наблюдениям, x86 — самый популярный вариант для имени этой архитектуры в Интернете, статьях и прочей литературе, особенно, когда не стоит задачи точно специфицировать разрядность архитектуры или речь явным образом идёт о 32-битном варианте.
Используются также вариации этого названия для 32-битных вариантов, позволяющие более точно указать минимальный набор поддерживаемых инструкций: i386, i486, i586, i686, — например, для различения вариантов сборок бинарных пакетов дистрибутивов Linux.
Пришествие 64 бит
Своими стараниями Intel расширила машинное слово в описанной ранее серии процессоров с 16 до 32 бит. Достигнуть этой архитектуре 64 бит помогла компания AMD, в 2003 году представившая процессор, поддерживающий новые инструкции и регистры и реализующий AMD64.
Для того, чтобы явно указать повышенную битность процессора/кода/пакета, имя x86 стало получать новый суффикс «64». Вот только через какой знак его приписывать не договорились, и иногда видишь x86_64, а порой x86-64. Например, вывод команды uname в Linux использует подчерк. Наконец, у пакетов можно увидеть и суффикс amd64 строчными буквами.
Intel же обозначала это расширение архитектуры сперва IA-32e, затем EM64T. В настоящее время можно встретить оба варианта в различных именах пакетов, документации и прочем. Тем не менее, есть и третье введённое Intel название…
Как это называет Intel
В официальной документации 32-битная архитектура имеет имя IA-32; её 64-битный вариант получил довольно странное с моей точки зрения имя Intel 64. Почему странное — оно создаёт потенциал для путаницы, как мы увидим ближе к концу статьи.
Компании-поставщики софта
Всё вместе
Ложные имена
Как известно, за свою сорокалетнюю историю Intel выпускала (и выпускает сейчас) не только процессоры IA-32. Были и до сих пор присутствуют продукты других архитектур. Они тоже имеют свои имена, иногда несколько созвучные. При этом происходит путаница, от которой хотелось бы предостеречь.
Intel IA-64. Является полным синонимом термина «Intel Itanium». Используется для обозначения 64-битной архитектуры, несовместимой ни с IA-32, ни с 64-битным её вариантом ни по набору команд, ни по принципам работы. Да, существуют аппаратные и программные прослойки для запуска IA-32 приложений на Итаниуме, но это — тема для отдельного и интересного рассказа. Кстати, и для Itanium есть ещё одно обозначение — IPF, используемое изредка как суффикс.
К сожалению, линейки таких семейств процессоров Intel, как i432, i860, i960 или не дожили до наших дней, или же имеют крайне узкую нишу применения. А то глядишь — пришлось бы для каждого из них запоминать ещё по десятку имён.
Урок 1. Что такое 64-битные системы
На момент написания курса, наиболее известными являются две 64-битные архитектуры микропроцессоров: IA64 и Intel 64.
Архитектура Intel 64
Необходимость 64-битной архитектуры определяется приложениями, которым необходимо большое адресное пространство. В первую очередь это высокопроизводительные серверы, системы управления базами данных, САПР и, конечно, игры. Такие приложения получат существенные преимущества от 64-битного адресного пространства и увеличения количества регистров. Малое количество регистров, доступное в устаревшей x86 архитектуре, ограничивает производительность в вычислительных задачах. Увеличенное количество регистров обеспечивает достаточную производительность для многих приложений.
Подчеркнем основные достоинства архитектуры x86-64:
64-битные операционные системы
Практически все современные операционные системы сейчас имеют версии для архитектуры Intel 64. Например, Microsoft предоставляет Windows XP x64. Крупнейшие разработчики UNIX систем также поставляют 64-битные версии, как, например, Linux Debian 3.5 x86-64. Однако это не означает, что весь код такой системы является полностью 64-битным. Часть кода ОС и многие приложения вполне могут оставаться 32-битными, так как Intel 64 обеспечивает обратную совместимость. Так, 64-битная версия Windows использует специальный режим WoW64 (Windows-on-Windows 64), который транслирует вызовы 32-битных приложений к ресурсам 64-битной операционной системы.
Адресное пространство
Хотя 64-битный процессор теоретически может адресовать 16 экзабайт памяти (2^64), Win64 в настоящий момент поддерживает 16 терабайт (2^44). Этому есть несколько причин. Текущие процессоры могут обеспечивать доступ лишь к 1 терабайту (2^40) физической памяти. Архитектура (но не аппаратная часть) может расширить это пространство до 4 петабайт (2^52). Однако в этом случае необходимо огромное количество памяти для страничных таблиц, отображающих память.
Помимо перечисленных ограничений, объем памяти, который доступен в той или иной версии 64-битной операционной системе Windows, зависит также от коммерческих соображений компании Microsoft. Различные версии Windows имеют различные ограничения, представленные в таблице.
Программная модель Win64
Также как и в Win32, размер страниц в Win64 составляет 4Кб. Первые 64Кб адресного пространства никогда не отображаются, то есть наименьший правильный адрес- это 0x10000. В отличие от Win32, системные DLL загружаются выше 4Гб.
Особенность компиляторов для Intel 64 в том, что они могут наиболее эффективно использовать регистры для передачи параметров в функции вместо использования стека. Это позволило разработчикам Win64 архитектуры избавиться от такого понятия, как соглашение о вызовах (calling convention). В Win32 можно использовать разные соглашения: __stdcall, __cdecl, __fastcall и так далее. В Win64 есть только одно соглашение о вызовах. Рассмотрим пример, как передаются в регистрах четыре аргумента типа integer:
Аргументы после первых четырех integer передаются на стеке. Для float аргументов используются XMM0-XMM3 регистры, а также стек.
Разница в соглашениях о вызове приводит к тому, что в одной программе нельзя использовать и 64-битный, и 32-битный код. Другими словами, если приложение скомпилировано для 64-битного режима, то все используемые библиотеки (DLL) также должны быть 64-битными.
Передача параметров через регистры является одним из новшеств, делающих 64-битные программы более производительными, чем 32-битные. Дополнительный выигрыш в производительности можно получить, используя 64-битные типы данных, о чем будет рассказано в соответствующем уроке.
Разбираемся в моделях кода архитектуры x64
«Какой моделью кода мне воспользоваться?» — часто возникающий, но нечасто разбираемый вопрос при написании кода для архитектуры х64. Тем не менее, это довольно интересная проблема, и для понимания генерируемого компиляторами машинного кода х64 полезно иметь представление о моделях кода. Кроме того, для тех, кто беспокоится о производительности вплоть до мельчайших команд, выбор модели кода влияет и на оптимизацию.
Важное замечание: эта статья не является обучающим материалом для начинающих. Перед ознакомлением рекомендуется уверенное владение C и ассемблером, а так же базовое знакомство с архитектурой х64.
Также смотрите нашу предыдущую публикацию на схожую тему: Как x86_x64 адресует память
Модели кода. Мотивационная часть
В архитектуре х64 отсылки и на код, и на данные ведутся через командно-относительные (или, используя жаргон х64, RIP-относительные) модели адресации. В этих командах сдвиг от RIP ограничен 32 битами, однако могут возникнуть случаи, когда команде при попытке адресовать часть памяти или данных попросту не хватит сдвига в 32 бит, например при работе с программами больше двух гигабайт.
Один из способов решения этой проблемы — полный отказ от RIP-относительного режима адресации в пользу полного 64-битного сдвига для всех ссылок на данные и код. Однако, этот шаг обойдется очень дорого: для покрытия (довольно редкого) случая невероятно больших программ и библиотек, даже простейшие операции в рамках вообще всего кода потребуют большего чем обычно числа команд.
Таким образом, компромиссом становятся модели кода. [1] Модель кода это формальное соглашение между программистом и компилятором, в котором программист указывает свои намерения относительно размера ожидаемой программы (или программ), в которую попадет компилируемый в данный момент объектный модуль. [2] Модели кода нужны для того чтобы программист мог сказать компилятору: «не волнуйся, этот объектный модуль пойдет только в небольшие программы, так что можно пользоваться быстрыми RIP-относительными режимами адресации». С другой стороны, он может сказать компилятору следующее: «мы собираемся компоновать этот модуль в большие программы, так что пожалуйста используй неторопливые и безопасные абсолютные режимы адресации с полным 64-битным сдвигом».
О чем расскажет эта статья
Мы поговорим о двух описанных выше сценариях, малой модели кода и большой модели кода: первая модель говорит компилятору, что 32-битного относительного сдвига должно хватить для всех ссылок на код и данные в объектном модуле; вторая настаивает на использовании компилятором абсолютных 64-битных режимов адресации. Кроме того, существует еще и промежуточный вариант, так называемая средняя модель кода.
Каждая из этих моделей кода представлена в независящих друг от друга PIC и не-PIC вариациях, и мы поговорим про каждую из шести.
Исходный пример на С
Для демонстрации обсуждаемых в этой статье концептов я воспользуюсь представленной ниже программой на С и скомпилирую ее с различными моделями кода. Как можно видеть, функция main получает доступ к четырем разным глобальным массивам и одной глобальной функции. Массивы отличаются двумя параметрами: размером и видимостью. Размер важен для объяснения средней модели кода и не понадобится в работе с малой и большой моделями. Видимость важна для работы PIC-моделей кода и бывает либо статичной (видимость только в исходном файле), либо глобальной (видимость всем скомпонованным в программу объектам).
Пример компиляции в объектный модуль через большую модель кода с использованием PIC:
Малая модель кода
Перевод цитаты из man gcc на тему малой модели кода:
-mcmodel=small
Генерация кода для малой модели: программа и ее символы должны быть скомпонованы в нижних двух гигабайтах адресного пространства. Размер указателей — 64 бит. Программы могут быть скомпонованы и статически, и динамически. Это основная модель кода.
Другими словами, компилятор может спокойно считать, что код и данные доступны через 32-битный RIP-относительный сдвиг из любой команды в коде. Давайте взглянем на дизассемблированный пример программы на С, которую мы скомпилировали через не-PIC малую модель кода:
Как можно видеть, доступ ко всем массивам организован одинаково — с использованием RIP-относительного сдвига. Однако в коде сдвиг равен 0, поскольку компилятор не знает, где будет размещен сегмент данных, поэтому для каждого такого доступа он создает релокацию:
Наконец, давайте взглянем на отсылку к global_func :
Поскольку операнд callq тоже RIP-относителен, релокация R_X86_64_PC32 работает здесь аналогично размещению фактического относительного сдвига к global_func в операнд.
В заключение отметим, что благодаря малой модели кода компилятор воспринимает все данные и код будущей программы как доступные через 32-битный сдвиг, и тем самым для доступа к всевозможным объектам создает простой и эффективный код.
Большая модель кода
Перевод цитаты из man gcc на тему большой модели кода:
-mcmodel=large
Генерация кода для большой модели: Эта модель не делает предположений относительно адресов и размеров секций.
И вновь полезно взглянуть на релокации:
Поскольку нет необходимости делать предположения относительно размеров секций кода и данных, большая модель кода довольно унифицирована и одинаковым образом определяет доступ ко всем данным. Давайте вновь взглянем на global_arr :
А что насчет функции вызова?
Как можно видеть, в большой модели кода нет каких-либо предположений о размере секций кода и данных, равно как и о финальном расположении символов, она попросту ссылается на символы через абсолютные 64-битные шаги, своего рода «безопасную дорожку». Однако обратите внимание как, по сравнению с малой моделью кода, большая модель вынуждена использовать дополнительную команду при обращении к каждому символу. Таковая цена безопасности.
Итак, мы с вами познакомились с двумя полностью противоположными моделями: в то время как малая модель кода предполагает, что все вмещается в нижние два гигабайта памяти, большая модель предполагает, что нет ничего невозможного и любой символ может находиться где угодно во всей полноте 64-битного адресного пространства. Компромиссом между этими двумя моделями становится средняя модель кода.
Средняя модель кода
Как и ранее, давайте взглянем на перевод цитаты из man gcc :
Аналогично малой модели кода, средняя модель предполагает что весь код скомпонован в двух нижних гигабайтах. Тем не менее, данные разделены на предполагаемо скомпонованные в нижних двух гигабайтах «малые данные» и неограниченные по размещению в памяти «большие данные». Данные попадают в категорию больших при превышении ими предела, по определению равного 64 килобайтам.
Теперь становится понятно, почему в примере появились те массивы _big : они нужны средней модели для интерпретации «больших данных», которыми они, при размере в 200 килобайт каждый, и являются. Ниже можно ознакомиться с результатом дизассемблирования:
Обратите внимание на то, как ведется доступ к массивам: обращение к массивам _big идет через методы большой модели кода, тогда как обращение к остальным массивам идет через методы малой модели. Обращение к функции тоже ведется по методу малой модели кода, а релокации настолько аналогичны предыдущим примерам, что я даже не буду их демонстрировать.
Средняя модель кода это умелый компромисс между большой и малой моделями. Навряд ли код программы окажется слишком велик [4], так что подвинуть его сверх лимита в два гигабайта могут разве что статически скомпонованные в него большие куски данных, возможно как часть некоего объемного табличного поиска. Так как средняя модель кода отсеивает такие объемные куски данных и особым образом их обрабатывает, то вызовы кодом функций и малых символов будут так же эффективны, как и в малой модели кода. Только обращения к большим символам, по аналогии с большой моделью, потребуют от кода воспользоваться полным 64-битным методом большой модели.
Малая PIC-модель кода
Теперь давайте посмотрим на PIC варианты моделей кода, и как и раньше мы начнем с малой модели. [5] Ниже можно видеть пример кода, скомпилированного через малую PIC-модель:
Поскольку в малой модели кода различия между большими и малыми данными не играют никакой роли, мы сфокусируемся на важных при генерации кода через PIC моментах: различиях между локальными (статичными) и глобальными символами.
Как можно заметить, нет никакой разницы между сгенерированным для статичных массивов кодом и кодом в не-PIC случае. Это один из плюсов архитектуры x64: благодаря IP-относительному доступу к данным, мы бонусом получаем PIC, по крайней мере до тех пор пока не требуется внешний доступ к символам. Все команды и релокации остаются теми же, так что обрабатывать их лишний раз не нужно.
Интересно обратить внимание на глобальные массивы: стоит напомнить, что в PIC глобальные данные должны проходить через GOT, поскольку в какой-то момент их могут хранить, или пользоваться ими, общие библиотеки [6]. Ниже можно видеть код для доступа к global_arr :
Теперь давайте взглянем на вызов функции:
Обратите внимание, какие неявные предположения совершает компилятор: что к GOT и PLT можно получить доступ через RIP-относительную адресацию. Это будет важно при сравнении этой модели с другими PIC-вариантами моделей кода.
Большая PIC-модель кода
Ниже можно прочесть перевод связанной с этим цитаты из ABI:
В малой модели кода ко всем адресам (включая GOT) можно получить доступ через предоставленную архитектурой AMD64 IP-относительную адресацию. Именно поэтому нет нужды в явном указателе GOT и таким образом нет нужды в устанавливающем его прологе в функции. В большой и средней моделях кода необходимо определить регистр для хранения адреса GOT в независимых от расположения объектах, поскольку AMD64 ISA не поддерживает мгновенное перемещение размером больше чем 32 бит.
Зачем же мучиться с вычислением адреса GOT? Во-первых, как отмечено в цитате, в большой модели кода мы не можем предполагать, что 32-битного RIP-относительного сдвига будет достаточно для адресации GOT, из-за чего нам и требуется полный 64-битный адрес. Во-вторых, мы все еще хотим работать с PIC-вариацией, так что мы не можем попросту поместить абсолютный адрес в регистр. Скорее сам адрес должен быть вычислен относительно RIP. Для этого и нужен пролог: он совершает 64-битное RIP-относительное вычисление.
В любом случае, раз у нас в rbx теперь есть адрес GOT, давайте посмотрим на то как получить доступ к static_arr :
Обратите внимание что вновь GOT используется как привязка, на этот раз для обеспечения независящей от адреса отсылки к сдвигу PLT входа.
Средняя PIC-модель кода
Наконец, мы разберем сгенерированный для средней PIC-модели код:
Вызывает интерес тот факт, что код для доступа к global_arr_big похож на такой же код в малой PIC-модели. Это происходит по той же причине, почему пролог средней модели короче пролога большой модели: мы полагаем доступность GOT в рамках 32-битной RIP-относительной адресации. Действительно, к самому global_arr_big нельзя получить такой доступ, но этот случай все равно покрывает GOT, так как фактически global_arr_big в нем и находится, причем в виде полного 64-битного адреса.
Ситуация, тем не менее, отличается для static_arr_big :
Этот случай похож на большую PIC-модель кода, поскольку тут мы все же получаем абсолютный адрес символа, которого нет в самом GOT. Так как это большой символ, о котором нельзя предположить его нахождение в нижних двух гигабайтах, нам, как и в большой модели, требуется 64-битный PIC сдвиг.
Примечания:
[1] Не стоит путать модели кода с 64-битными моделями данных и моделями памяти Intel, все это разные темы.
[2] Важно помнить: собственно команды создает компилятор, и режимы адресации фиксируются именно на этом шаге. Компилятор не может знать в какие программы или общие библиотеки попадет объектный модуль, одни могут оказаться малыми, а другие большими. Компоновщику известен размер итоговой программы, но уже слишком поздно: компоновщик может лишь патчить сдвиг команд релокацией, а не менять сами команды. Таким образом, «соглашение» модели кода должно быть «подписано» программистом на этапе компиляции.
[3] Если что-то осталось непонятным, ознакомьтесь со следующей статьей.
[4] Тем не менее, объемы постепенно растут. Когда я в последний раз проверял Debug+Asserts билд Clang, он почти достигал одного гигабайта, за что во многом спасибо автогенерируемому коду.
[5] Если вы еще не знаете как работает PIC (как в целом, так и в частности для архитектуры x64), самое время ознакомиться со следующими статьями по теме: раз и два.
[6] Таким образом, компоновщик не может самостоятельно разрешить ссылки, и вынужден переложить обработку GOT на динамический загрузчик.
64 бита
Статья раскрывает смысл термина «64 бита». В статье кратко рассмотрена история развития 64-битных систем, описаны наиболее распространенные на данный момент 64-битные процессоры архитектуры Intel 64 и 64-битная операционная система Windows.
Введение
В рамках архитектуры вычислительной техники под термином «64-битный» понимают 64-битные целые и другие типы данных, имеющих размер 64 бита. Под «64-битными» системами могут пониматься 64-битные архитектуры микропроцессоров (например, EM64T, IA-64) или 64-битные операционные системы (например, Windows XP Professional x64 Edition). Можно говорить о компиляторах, генерирующих 64-битный программный код.
В данной статье будут рассмотрены различные моменты, связанные с 64-битными технологиями. Статья предназначена для программистов, желающих начать разрабатывать 64-битные программы, и ориентирована на Windows-разработчиков, поскольку для них вопрос знакомства с 64-битными системами наиболее актуален.
История 64-битных систем
64-битность только недавно вошла в жизнь большинства пользователей и прикладных программистов. Однако работа с 64-битными данными имеет уже длинную историю.
1961: IBM выпускает суперкомпьютер IBM 7030 Stretch, в котором используются 64-битные слова данных, 32-битные или 64-битные машинные инструкции.
1974: Control Data Corporation запускает векторный суперкомпьютер CDC Star-100, в котором используется архитектура 64-битных слов (предыдущие системы CDC имели 60-битную архитектуру).
1976: Cray Research выпускает первый суперкомпьютер Cray-1, в котором реализована архитектура 64-битных слов и который послужит основой для всех последующих векторных суперкомпьютеров Cray.
1985: Cray выпускает UNICOS — первую 64-битную реализацию операционной системы Unix.
1991: MIPS Technologies производит первый 64-битный процессор, R4000, в котором реализована третья модификация разработанной в их компании архитектуры MIPS. Этот процессор используется в графических рабочих станциях SGI начиная с модели IRIS Crimson. Kendall Square Research выпускает свой первый суперкомпьютер KSR1, построенный на основе их собственной запатентованной 64-битной архитектуры RISC под операционной системой OSF/1.
1992: Digital Equipment Corporation (DEC) представляет полностью 64-битную архитектуру Alpha — детище проекта PRISM.
1993: DEC выпускает 64-битную Unix-подобную операционную систему DEC OSF/1 AXP (позже переименованную в Tru64 UNIX) для своих систем, построенных на архитектуре Alpha.
1994: Intel объявляет о своих планах по разработке 64-битной архитектуры IA-64 (совместно с компанией Hewlett-Packard) — преемника их 32-битных процессоров IA-32. Дата выпуска назначена на 1998-1999 годы. SGI выпускает IRIX 6.0 с 64-битной поддержкой чипсета R8000.
1995: Sun запускает 64-битный процессор семейства SPARC UltraSPARC. HAL Computer Systems, подчиненная Fujitsu, запускает рабочие станции, созданные на основе 64-битного процессора SPARC64 первого поколения, независимо разработанного компанией HAL. IBM выпускает микропроцессоры A10 и A30, а также 64-битные процессоры PowerPC AS. IBM также выпускает 64-битное обновление для системы AS/400, способное преобразовывать операционную систему, базы данных и приложения.
1996: Nintendo представляет игровую консоль Nintendo 64, созданную на основе более дешевого варианта MIPS R4000. HP выпускает реализацию 64-битной 2.0 версии собственной архитектуры PA-RISC PA-8000.
1997: IBM запускает линейку RS64 64-битных процессоров PowerPC/PowerPC AS.
1998: Sun выпускает Solaris 7 с полной 64-битной поддержкой UltraSPARC.
1999: Intel выпускает набор команд для архитектуры IA-64. AMD публично объявляет о своем наборе 64-битных расширений для IA-32, который был назван x86-64 (позже переименован в AMD64).
2000: IBM выпускает свой первый 64-битный мэйнфрейм zSeries z900, совместимый с ESA/390, а также новую операционную систему z/OS.
2001: Intel наконец запускает линейку 64-битных процессоров, которые теперь получают название Itanium и рассчитаны на высокопроизводительные серверы. Проект не соответствует ожиданиям из-за многочисленных задержек при выпуске IA-64 на рынок. NetBSD становится первой операционной системой, которая запускается на процессоре Intel Itanium после его выхода. Кроме того, Microsoft также выпускает Windows XP 64-Bit Edition для архитектуры IA-64 семейства Itanium, хотя в ней сохраняется возможность запускать 32-битные приложения при помощи прослойки WoW64.
2003: AMD представляет линейки процессоров Opteron и Athlon 64, созданные на основе архитектуры AMD64, которая является первой 64-битной процессорной архитектурой, основанной на архитектуре x86. Apple начинает использовать 64-битный процессор «G5» PowerPC 970 производства IBM. Intel утверждает, что процессорные чипы семейства Itanium останутся единственными 64-битными процессорами, разработанными в их компании.
2004: В ответ на коммерческий успех AMD, Intel признается, что они разрабатывали клон расширений AMD64, которому дали название IA-32e (позже переименован в EM64T, и затем еще раз в Intel 64). Intel также выпускает обновленные версии семейств процессоров Xeon и Pentium 4 с поддержкой новых команд.
2004: VIA Technologies представляет свой 64-битный процессор Isaiah.
2005: 31 января Sun выпускает Solaris 10 с поддержкой процессоров AMD64 / Intel 64. 30 апреля Microsoft выпускает Windows XP Professional x64 Edition для процессоров AMD64 / Intel 64.
2006: Sony, IBM и Toshiba начинают выпуск 64-битного процессора Cell для PlayStation 3, серверов, рабочих станций и других устройств. Microsoft выпускает Windows Vista с включенной 64-битной версией для процессоров AMD64 / Intel 64, которая поддерживает 32-битную совместимость. Все Windows-приложения и компоненты являются 64-битными, однако многие из них имеют 32-битные версии, включенные в систему в виде плагинов в целях совместимости.
2009: Как и Windows Vista, Windows 7 компании Microsoft включает полную 64-битную версию для процессоров AMD64 / Intel 64, и на большинство новых компьютеров по умолчанию устанавливается 64-битная версия. Выходит операционная система компании Apple Mac OS X 10.6, «Snow Leopard» которая имеет 64-битное ядро и предназначена для процессоров AMD64 / Intel 64, однако по умолчанию эта система устанавливается только на некоторые из последних моделей компьютеров компании Apple. Большинство приложений, поставляемых с Mac OS X 10.6, теперь также являются 64-битными.
Более подробно с историей развития 64-битных систем можно познакомиться в статье Джона Машей «Долгая дорога к 64 битам» [1] и в энциклопедической статье в Wikipedia «64-bit» [2].
Прикладное программирование и 64-битные системы
Архитектура Intel 64 (AMD64)
Рассматриваемая архитектура Intel 64 простое, но в то же время мощное обратно совместимое расширение устаревшей промышленной архитектуры x86. Она добавляет 64-битное адресное пространство и расширяет регистровые ресурсы для поддержки большей производительности перекомпилированных 64-битных программ. Архитектура обеспечивает поддержку устаревшего 16-битного и 32-битного кода приложений и операционных систем без их модификации или перекомпиляции.
Отличительной особенностью Intel 64 является поддержка шестнадцати 64-битных регистров общего назначения (в x86-32 имелось восемь 32-битных регистров). Поддерживаются 64-битные арифметические и логические операции над целыми числами. Поддерживаются 64-битные виртуальные адреса. Для адресации новых регистров для команд введены «префиксы расширения регистра», для которых был выбран диапазон кодов 40h-4Fh, использующихся для команд INC и DEC в 32- и 16-битных режимах. Команды INC и DEC в 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.
Необходимость 64-битной архитектуры определяется приложениями, которым необходимо большое адресное пространство. В первую очередь это высокопроизводительные серверы, системы управления базами данных, САПР и, конечно, игры. Такие приложения получат существенные преимущества от 64-битного адресного пространства и увеличения количества регистров. Малое количество регистров, доступное в устаревшей x86 архитектуре, ограничивает производительность в вычислительных задачах. Увеличенное количество регистров обеспечивает достаточную производительность для многих приложений.
64-битные операционные системы
Практически все современные операционные системы сейчас имеют версии для архитектуры Intel 64. Например, Microsoft предоставляет Windows XP x64. Крупнейшие разработчики UNIX систем также поставляют 64-битные версии, как например Linux Debian 3.5 x86-64. Однако это не означает, что весь код такой системы является полностью 64-битным. Часть кода ОС и многие приложения вполне могут оставаться 32-битными, так как Intel 64 обеспечивает обратную совместимость с 32-битными приложениями. Например, 64-битная версия Windows использует специальный режим WoW64 (Windows-on-Windows 64), который транслирует вызовы 32-битных приложений к ресурсам 64-битной операционной системы.
Далее в статье мы будем рассматривать только 64-битные операционные системы семейства Windows.
WoW64
Windows-on-Windows 64-bit (WoW64) — подсистема операционной системы Windows, позволяющая запускать 32-битные приложения на всех 64-битных версиях Windows.
WoW64 на архитектуре Intel 64 (AMD64 / x64) не требует эмуляции инструкций. Здесь подсистема WoW64 эмулирует только 32-битное окружение, за счет дополнительной прослойки между 32-битным приложением и 64-битным Windows API. Где-то эта прослойка тонкая, где-то не очень. Для средней программы потери в производительности из-за наличия такой прослойки составят около 2%. Для некоторых программ это значение может быть больше. Два процента это немного, но следует учитывать, что 32-битные приложения работают немного медленнее под управлением 64-битной операционной системы Windows, чем в 32-битной среде.
Компиляция 64-битного кода не только исключает необходимость в WoW64, но и дает дополнительный прирост производительности. Это связано с архитектурными изменениями в микропроцессоре, такими как увеличение количества регистров общего назначения. Для средней программы можно ожидать в пределах 5-15% прироста производительности от простой перекомпиляции.
Из-за наличия прослойки WoW64 32-битные программы работают менее эффективно в 64-битной среде, чем в 32-битной. Но все-таки, простые 32-битные приложения могут получить одно преимущество от их запуска в 64-битной среде. Вы, наверное, знаете, что программа, собранная с ключом /LARGEADDRESSAWARE:YES может выделять до 3-х гигабайт памяти, если 32-битная операционная система Windows запущена с ключом /3gb. Так вот, эта же 32-битная программа, запущенная на 64-битной системе, может выделить почти 4 GB памяти (на практике около 3.5 GB).
Подсистема WoW64 изолирует 32-разрядные программы от 64-разрядных путем перенаправления обращений к файлам и реестру. Это предотвращает случайный доступ 32-битных программ к данным 64-битных приложений. Например, 32-битное приложение, которое запускает файл DLL из каталога %systemroot%\System32, может случайно обратиться к 64-разрядному файлу DLL, который несовместим с 32-битной программой. Во избежание этого подсистема WoW64 перенаправляет доступ из папки %systemroot%\System32 в папку %systemroot%\SysWOW64. Это перенаправление позволяет предотвратить ошибки совместимости, поскольку при этом требуется файл DLL, созданный специально для работы с 32-разрядными приложениями.
Подробнее с механизмами перенаправления файловой системы и реестра можно познакомиться в разделе MSDN «Running 32-bit Applications».
Программная модель Win64
Также как и в Win32 размер страниц в Win64 составляет 4Кб. Первые 64Кб адресного пространства никогда не отображаются, то есть наименьший правильный адрес это 0x10000. В отличие от Win32, системные DLL загружаются выше 4Гб.
Разница в соглашениях о вызове приводит к тому, что в одной программе нельзя использовать и 64-битный, и 32-битный код. Другими словами, если приложение скомпилировано для 64-битного режима, то все используемые библиотеки (DLL) также должны быть 64-битными.
Передача параметров через регистры является одним из новшеств, делающих 64-битные программы более производительными, чем 32-битные. Дополнительный выигрыш в производительности можно получить, используя 64-битные типы данных.
Адресное пространство
Хотя 64-битный процессор теоретически может адресовать 16 экзабайт памяти (2^64), Win64 в настоящий момент поддерживает 16 терабайт (2^44). Этому есть несколько причин. Текущие процессоры могут обеспечивать доступ лишь к 1 терабайту (2^40) физической памяти. Архитектура (но не аппаратная часть) может расширить это пространство до 4 петабайт (2^52). Однако в этом случае необходимо огромное количество памяти для страничных таблиц, отображающих память.
Помимо перечисленных ограничений, объем памяти, который доступен в той или иной версии 64-битной операционной системе Windows зависит также от коммерческих соображений компании Microsoft. Ниже приведена информация по объему памяти, поддерживаемой различными версиями 64-биными версиями Windows:
Windows XP Professional — 128 Gbyte;
Windows Server 2003, Standard — 32 Gbyte;
Windows Server 2003, Enterprise — 1 Tbyte;
Windows Server 2003, Datacenter — 1 Tbyte;
Windows Server 2008, Datacenter — 2 Tbyte;
Windows Server 2008, Enterprise — 2 Tbyte;
Windows Server 2008, Standard — 32 Gbyte;
Windows Server 2008, Web Server — 32 Gbyte;
Vista Home Basic — 8 Gbyte;
Vista Home Premium — 16 Gbyte;
Vista Business — 128 Gbyte;
Vista Enterprise — 128 Gbyte;
Vista Ultimate — 128 Gbyte;
Windows 7 Home Basic — 8 Gbyte;
Windows 7 Home Premium — 16 Gbyte;
Windows 7 Professional — 192 Gbyte;
Windows 7 Enterprise — 192 Gbyte;
Windows 7 Ultimate — 192 Gbyte;
Разработка 64-битных приложений
Наиболее полно вопросы разработки 64-битных приложений рассмотрены в курсе «Уроки разработки 64-битных приложений на языке Си/Си++». Содержание:
Также рекомендую раздел с обзорами статей по тематике связанной с 64-битнми технологиями.