2 ноября 2010 г.

Введение в 64 бита на Windows

64-битные (или, что тоже самое: 64-разрядные) числа, адреса памяти или другие структуры данных - это данные, размер которых не более 64 бит. Соответственно, 64-разрядный процессор (CPU) - это процессор, который основан на/оперирует с данными (регистры, адреса) этого размера, позволяя производить прямые действия с числами этого размера (однако внешние шины данных и адресации могут иметь и иную размерность). 64-битные CPU существовали в суперкомпьютерах ещё в 70-х годах прошлого века. Но в сегменте персональных компьютеров (который до этого был 32-разрядным) они были представлены только в 2003-м году с появлением x86-64 (AMD64).

В большинстве процессоров для адресации данных в памяти могут использоваться только целочисленные (Integer) и/или специальные адресные регистры. А другие регистры для этого использовать нельзя. Поэтому размер регистров, используемых для адресации, обычно определяет размер памяти, которую можно адресовать напрямую - даже если в процессоре есть регистры большего размера (например, вещественный сопроцессор). Большинство процессоров спроектировано так, что любой один целочисленный регистр может хранить адрес (место) любых данных в виртуальной памяти (т.е. любой целочисленный регистр - это адресный регистр). Поэтому суммарное количество возможных адресов в виртуальной памяти (суммарный размер данных, которые компьютер может хранить в своём рабочем пространстве) определяется размером этих регистров. 32-битный адресный регистр означает что может быть адресовано 232 адресов или 4 Гб памяти. Когда зарождалась эта архитектура, 4 Гб памяти было намного больше типичных в то время размеров устанавливаемой оперативной памяти (4 Мб), поэтому считалось, что этого более чем достаточно для адресации. Ещё одной причиной, почему 4.29 миллиардов адресов рассматривались как подходящий размер для работы: 4.29 миллиардов чисел было достаточно, чтобы присвоить уникальный номер каждой записи в базах данных тех времён.

Большинство современных 64-разрядных процессоров имеют искусственное ограничение на размер памяти, который они могут адресовать - значительно меньший, чем вы можете ожидать от 64 битов. Например, архитектура AMD64 сейчас имеет ограничение в 52 бита на физическую память и поддерживает только 48-ми битное виртуальное адресное пространство. Это 4 Пб (петабайта) и 256 Тб (терабайт) соответственно. Это позволяет иметь достаточно места в обозримом будущем, не платя полную цену за обработку 64-битных адресов полностью.

Аналогичным образом, некоторые 64-разрядные операционные системы Windows имеют искусственные ограничения на размеры памяти, существенно ниже теоретически возможных. Это связано с тем, что разработчики ОС не хотят утверждать, что их продукт сможет работать на конфигурации, которую они не тестировали - поэтому максимальный размер памяти равен самому большому размеру памяти, который удалось протестировать к моменту выпуска ОС. Однако, реальное ограничение может быть ещё ниже из-за редакции ОС. К примеру, Windows 7 Starter поддерживает только 2 Гб оперативной памяти (да, это ограничение 64-разрядной версии). Вот табличка с параметрами систем. Я также рекомендую почитать этот цикл статей (по ссылке - последняя статья из серии, просто там в начале указано полное оглавление).

Помимо увеличенной возможности адресации 64-разрядные процессоры предлагают новые вычислительные возможности в виде новых регистров и команд. Также накладываются дополнительные ограничения и соглашения. К примеру, рассматривая x86-64: математический сопроцессор считается устаревшим. Его использование не поощряется, хотя и технически возможно. Вместо сопроцессора можно использовать 64-разрядные регистры или SSE. Имеются более строгие требования к выравниванию данных. Особенно на стеке. Модель вызова - только одна. Ключевые слова типа stdcall, register и т.п. игнорируются. По этим трём причинам (сопроцессор, выравнивание + передача вещественных чисел в SSE-регистрах), Extended (10 байт, родной для мат-сопроцессора) в мире x64 становится очень уж неудобным. Возможно, он будет как-то изменён в грядущей Delphi x64 (либо приравняют к Double или Single, как это делает Free Pascal, либо пометят как deprecated, либо оставят как есть, но работа с ним может быть не оптимальной). x86-64 даёт вам 16 64-разрядных регистров и 16 128-ми разрядных XMM-регистров (для вещественных чисел). Для сравнения: у x86-32 есть только 8 32-разрядных регистров (если я правильно сосчитал), остальное - это сопроцессор (8 80-битных регистра) и расширения. Т.е. x86-64 практически удваивает набор регистров, разве что регистры для вещественных чисел имеют уменьшенную до Double размерность. Напомню, сам сопроцессор никуда не уходит, но его использование не поощряется. Сопроцессор также не участвует в единственной для x64 модели вызова.

32 бит vs 64 бит

Переход от 32-битной к 64-битной архитектуре является фундаментальным изменением, потому что большинство операционных систем должны быть специально изменены для использования преимуществ новой архитектуры. Стороннее программное обеспечение также должно быть изменено, чтобы воспользоваться новыми возможностями; уже написанные программы поддерживаются либо режимом обратной совместимости в железе (когда один и тот же процессор может выполнять и 64-разрядные и 32-разрядные команды), либо реализацией 32-разрядного ядра процессора рядом с 64-разрядным, либо слоем программной эмуляции. В Windows для x86/x86-64 имеет место первый вариант. При этом самой большой проблемой являются драйвера. Если обычные приложения могут спокойно работать, используя подсистему WOW64, то драйвер обязан работать в родном для системе режиме. Т.е. 32-разрядный драйвер не сможет работать в 64-разрядной системе.

Когда нужно ставить 32-разрядную или 64-разрядную ОС? Простой ответ может выглядеть так: если у вас 4 Гб оперативной памяти и более - то ставить надо 64-разрядную. В противном случае - 32-разрядную. Подробный ответ: всё не совсем так просто. Во-первых, в 32-разрядной системе вам не удастся воспользоваться всеми 4 Гб памяти - реальная цифра будет около 3 Гб (плюс-минус). Происходит это по той причине, что часть пространства резервируется под различные нужды. С другой стороны, на новых процессорах есть Physical Address Extension (PAE), которое даёт вам поддержку 64 Гб памяти - даже в 32-разрядном режиме. Но воспользоваться им вы сможете только на серверных Windows при условии, что все установленные драйвера поддерживают этот режим. Более подробно про PAE написано в этом цикле статей, а также в уже упоминавшемся выше цикле статей от Марка Руссиновича. Кроме того, нужно учитывать и обратную совместимость. Если у вас в машине есть очень старая железка, под которую нет 64-разрядного драйвера - использовать её вы не сможете: придётся ставить 32-разрядную ОС. К счастью, это скорее исключение, чем правило.

Верите вы или нет, но большей части разработчиков не нужна 64-разрядность. Зачем? Их 32-разрядные приложения и так отлично работают. В конце концов, обратная совместимость - конёк Microsoft. Давайте посмотрим, кому реально нужен x64:
  • Логика программы требует ворочать огромными объёмами данных в памяти.
  • Совместимость кода:
    • Кто пишет плагины/расширения для Проводника и других программ.
    • Хуки.
    • Кто работает в команде и пишет DLL, которую грузит x64 exe, писанный в VS.
  • И... всё.
Вложенные три пункта - это ровно одна и та же причина: 64-разрядный процесс не может загрузить 32-разрядную DLL и наоборот. Поэтому, реально причины всего две: вам нужны бонусы огромного адресного пространства или вам нужна интеграция с другим 64-разрядным компонентом. Всё.

Казалось бы, совместимость с 64-разрядным кодом - некая "вещь в себе", но когда к тебе подходит коллега (пишущий на MS VS) и говорит: "Слушай, что за дела? У заказчика все машины 64-битные, весь наш софт 64-битный - но почему ты делаешь 32-разрядные DLL?" и вам приходится отвечать: "Ну, Delphi не умеет делать 64-разрядные DLL" - надо видеть его глаза.

Вторая большая причина - плагины. В том числе и расширения Проводника.

Для всех остальных переход на x64 - только лишняя головная боль, которая не принесёт им ничего, кроме чувства идеальности (да! теперь наш продукт работает в родном режиме на x64!). Более того, это лишь ухудшит их ситуацию: теперь им нужно поддерживать и распространять две версии продукта, вместо одной (которая, напомню, отлично работает и так).

Главным недостатком 64-битной архитектуры является то, что одни и те же данные (и код) будут занимать больше места в памяти и на диске (из-за увеличения размера указателей, других аналогичных типов и выравнивания). Это увеличивает размер необходимой процессу памяти, частично гася работу кэша. Кроме того, 64-разрядное приложение не будет работать в 32-разрядном окружении, поэтому, как я уже указал выше, вам придётся делать две версии своей программы - что означает удвоенное поле для тестирования и поддержки. Поскольку преимущества 64-разрядного режима (работа с очень большими объёмами данных) выгодны только очень ограниченному количеству приложений (типа серверов БД), то мы можем ожидать очень медленный переход на 64-разрядные системы. Конечно, когда-нибудь в будущем 90% всех домашних компьютеров будут иметь 64-разрядные возможности - и написание 32-разрядных программ станет не выгодно.

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

Просуммируем сказанное здесь в табличке (преимущество/недостаток рассматривается в сравнении с соседней моделью):

32-разрядные приложения или архитектура64-разрядные приложения или архитектура
ПреимуществаНедостаткиПреимуществаНедостатки
Приложения работают в любой системе.Приложения работают только в 64-разрядной системе. Вам придётся делать 32-разрядный вариант, чтобы ваше приложение работало в большинстве современных систем.
Приложения имеют меньший размер и требуют меньше ресурсов (памяти) для работы.Размер и требования приложений слегка увеличены за счёт удвоения размера адресов и выравнивания.
32-разрядная DLL может быть загружена в 32-разрядный процесс.32-разрядная DLL НЕ может быть загружена в 64-разрядный процесс.64-разрядная DLL может быть загружена в 64-разрядный процесс.64-разрядная DLL НЕ может быть загружена в 32-разрядный процесс.
Приложение не получает бонуса с выполнения на 64-разрядной системе.Приложение может воспользоваться новыми регистрами процессора.
Сложно работать с большими объёмами памяти. Вам нужно использовать какой-нибудь механизм частичной загрузки/выгрузки. Например, AWE, проецируемые частями файлы или подходящий алгоритм обработки по частям.У приложения есть доступ к 8 Тб адресного пространства.
Работа с 64-разрядными числами возможна, но по частям.Работа с 64-разрядными числами напрямую.
Родной поддержки для 128-разрядных чисел нет.Возможна работа с 128-разрядными числами, но по частям (не ясно, будет ли такая поддержка в компиляторе Delphi).

Историческая справка
Процесс одной размерности может загрузить только модуль той же размерности. Это применимо к 16-, 32- и 64-разрядным процессам и модулям. Вот почему в 64-разрядной системе вы часто видите два варианта данных: один вариант - для 64-разрядных процессов, другой вариант - для 32-разрядных процессов ("Program Files" vs "Program Files (x86)", "System32" vs "SysWOW64").

Однако, при переходе от Win16 к Win32 всё обстояло несколько иначе. Не было двух вариантов кода, а просто использовались переходники от 16-ти разрядным к 32-разрядным модулям. С помощью процесса, известного как generic thunks, 16-ти битная программа может загрузить 32-битную DLL и вызвать её. Windows 95 и слой эмуляции 16-ти битных Windows в Windows NT сильно зависят от generic thunks, так что им не нужно иметь две версии кода (как это имеет место быть для 32- и 64-разрядного кода). Вместо этого, 16-ти битная версия просто использует 32-битную версию через "переходник".

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

WOW (также известный как WOW32) предоставляет специальные функции (в обе стороны), которыми может воспользоваться переходник. Всё это приводит к видимости, что 16-ти разрядный код загружается и используется 32-разрядным. При переходе с 32 на 64 такого механизма не предусмотрено.

64-битные модели данных

Каждое приложение и каждая операционная система имеют абстрактную модель данных. Многие приложения не говорят явно об этой модели, но модель данных управляет тем, как написан код приложения. В 32-разрядной программной модели на Windows (известной как модель ILP32) Integer, LongInt и Pointer были размером в 32 бита. Большинство разработчиков использовало эту модель даже не осознавая этого. За всю историю Win32 это допущение было корректным (хотя и не безопасным).
Историческая справка
Классическая модель данных при переходе от 16- к 32-разрядной архитектуре выглядела так: в языке есть фундаментальные типы данных: ShortInt, SmallInt, LongInt, Int64 (и их беззнаковые эквиваленты), которые всегда имеют фиксированную размерность (8/16/32/64 бита соответственно) и не зависят от целевой платформы. И есть обобщённый тип данных: Integer (и его беззнаковый эквивалент Cardinal), который равен родному для целевой платформы типу. Т.е. Integer = SmallInt на Win16 и Integer = LongInt на Win32. Предполагалось, что Integer будет равен Int64 на Win64.
Но при переходе на Win64 все эти допущения оказываются не рабочими. Посмотрите на эту табличку существующих на сегодня моделей данных 64-разрядной архитектуры:

64-битные модели данных
Модель данных SmallInt/Word LongInt/LongWord Integer/Cardinal Int64/UInt64 Pointer Пример операционной системы с этой моделью
LLP64 16 32 32 64 64 Microsoft Windows (X64/IA-64)
LP64 16 64 32 64 64 Большинство Unix и Unix-like систем, типа Solaris, Linux, and Mac OS X
ILP64 16 64 64 64 64 HAL Computer Systems порт Solaris на SPARC64
SILP64 64 64 64 64 64 Unicos

(я знаю, что в этой табличке очень странно выглядят LongInt и LongWord на 64 бита, но компилятора Delphi под эти платформы нет, поэтому руководствоваться можно лишь соответствиями с типами C (LongInt <-> long int), которые (соответствия), впрочем, совершенно не обязаны выполняться. Вероятно, это была не самая лучшая идея - указывать типы Delphi в табличке моделей данных под C, но надо же как-то их сравнить)

Практика показала, что большинству программ даже сегодня не нужна работа с 64-разрядными числами - они прекрасно обойдутся и 32-разрядными. Кроме того, x86-64 эффективнее обрабатывает 32-разрядные числа. Как вы можете видеть, две наиболее популярные модели (LLP64 и LP64) используют 32-разрядный Integer. Так что если вы используете Integer как "просто целочисленный тип, удобный для процессора" - вы в порядке. Потому что таким типом для x86-64 является 32-разрядное, а не 64-разрядное число (как ни странно). Например, при расширении 32-разрядного числа до 64-разрядного - старшая часть регистра не обнуляется. Заметьте, что определение Integer как типа, зависящего от целевой платформы всё ещё не нарушено. Просто в x86-64 он оказался равен LongInt. Ну, бывает. Для равного по размеру Pointer-у есть другой тип (см. ниже).

Итак, увеличение размерности до 64 бит было бы пустой тратой места, потому что это не нужно большинству приложений. Но приложениям нужны указатели на 64-разрядные данные и им нужна способность обрабатывать эти 64-разрядные числа. Кроме того, есть и причина обратной совместимости: неисчислимое количество кода уже написано в предположении, что размер Integer и LongInt не изменится. Именно эти причины привели к выбору Windows модели данных LLP64. В модели данных LLP64 только указатели увеличиваются до 64 бит. Все остальные типы остаются без изменений. Иными словами, команда разработчиков Windows сделала переход на 64-разрядную архитектуру максимально безболезненным.

Изначально, приложения, которые работают на 64-битной Windows будут получаться портированием 32-разрядных приложений Windows. Цель состоит в том, что один и тот же исходник, будучи написан аккуратно, должен работать как в 32-х, так и в 64-разрядном окружении. Определение модели данных само по себе не делает эту задачу проще. Но гарантия, что модель данных влияет только на размер указателя, является только первым шагом. Второй шаг - определить набор новых типов данных, которые позволят разработчику автоматически менять размеры данных, связанных с указателями. Это позволит вам иметь целочисленный тип данных, который меняет свой размер вместе со сменой размера указателя от 32 до 64 бит. Базовые типы данных останутся размером 32 бита, поэтому вам не нужно будет производить никаких изменений уже написанного кода, работающим с файлами на диске, передачей по сети или IPC. Это значительно сузит объём работ, необходимых для портирования ваших приложений на 64-битные Windows.

Заметьте, что программная модель - это выбор на базе компилятора, так что в одной ОС могут сосуществовать разные модели данных. Хотя модель, выбранная самой ОС, обычно доминирует по понятным причинам.

Переход на 64-битную архитектуру (предварительная информация)

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

Наиболее вероятно, что будущая 64-разрядная версия Delphi возьмёт модель LLP64 - как взяла её Windows и как следует ей Free Pascal. Поэтому все Integer и LongInt останутся 32-разрядными.

Уже сейчас Delphi имеет новые типы данных, которые выглядят намного логичнее старых (ну-ка, не подглядывая в справку: сколько байт занимает SmallInt? А ShortInt?): Int8, Int16, Int32, Int64, NativeInt (и их беззнаковые аналоги: UInt8, UInt16, UInt32, Uint64 и NativeUInt). Типы с цифрами имеют (надеюсь) очевидную размерность, а NativeInt/NativeUInt равен размеру Pointer в байтах. Достаточно очевидно и прозрачно. Integer всё ещё является типом, размерность которого зависит от платформы, но никто не говорил, что его размерность равна размерности указателя. Integer - это просто общий целочисленный тип. В частности поэтому Integer равен 16/32/32 на Win16/Win32/Win64.

Давайте приведём табличку, суммирующую сказанное для целочисленных типов данных:

Целочисленные типы данных в Delphi
ТипЗнаковый?Размер в Win16Размер в Win32Размер в Win64ПсевдонимыЦель/назначение
PointerX163264Указатель
NativeInt+163264Знаковый целочисленный тип, совместимый с указателем
NativeUInt-163264Беззнаковый целочисленный тип, совместимый с указателем
Int+163232IntegerЗнаковый целочисленный тип общего назначения
UInt-163232CardinalБеззнаковый целочисленный тип общего назначения
Int8+888ShortIntЗнаковый целочисленный тип размером 8 бита
UInt8-888ByteБеззнаковый целочисленный тип размером 8 бит
Int16+161616SmallIntЗнаковый целочисленный тип размером 16 бит
UInt16-161616WordБеззнаковый целочисленный тип размером 16 бит
Int32+323232LongIntЗнаковый целочисленный тип размером 32 бита
UInt32-323232LongWordБеззнаковый целочисленный тип размером 32 бита
Int64+X6464Знаковый целочисленный тип размером 64 бита
UInt64-X6464Беззнаковый целочисленный тип размером 64 бита

Итак, что же со всем этим делать? Ну, вам нужно следовать нескольким простым правилам:
  • Если вам нужна целочисленная переменная для общих целей - берите Integer.
  • Если вам нужна целочисленная переменная строго фиксированного размера (например, для записи в файл, отправке по сети или другому приложению через IPC) - берите (U)IntX (предпочтительно) или любой фундаментальный тип - LongInt, SmallInt и т.п. (менее предпочтительно, но хотя бы привычнее). Не используйте для этого Integer или Cardinal.
  • Если вам нужен тип для "Pointer-like" данных - берите Native(U)Int.
  • Вам нужно внимательнее относится к типам данным. Например, тип Integer больше не взаимозаменяем с HWND или THandle в Win64.
Остаётся только согласовать код в местах прикосновения разных типов.

Вероятно, позднее Embarcadero опубликует какое-нибудь руководство по переходу на Win64, как она сделала это для Unicode.

Что делать, если вы писали свой код в расчёте на то, что Integer станет равен Int64, а LongInt останется 32-битным? Иными словами, вы думали, что переход на 64 будет совершаться так же, как и переход на 32. А действовать надо ровно так же: вы находите все места использования 64-разрядных чисел и меняете тип с Integer на NativeInt. С другой стороны, вы могли бы сделать простой Search&Replace по исходному коду: Integer -> NativeInt. Да, это будет работать, но надо понимать, что тогда ваша программа будет везде оперировать с 64-разрядными числами (вы ведь использовали Integer как "просто целочисленный тип, удобный для процессора", не так ли?), что, как мы узнали, является не самым эффективным действием на x86-64.

Ну и, напоследок, несколько фактов:
  • 64-разрядное приложение использует формат PE, так что размер exe-файла не может превышать 2 Гб.
  • SizeOf(THandle/HWND/HMODULE/Hxxxx) = SizeOf(Pointer) = 8. Поэтому вот так будет неправильно: Value := Integer(Handle).
  • Свойства вроде Tag (вероятнее всего) станут типа NativeInt.
  • Пожалуй, единственными 32-разрядными приложениями, которые получают какие-то бонусы с их выполнения на 64-разрядной системе, являются приложения, помеченные флагом LARGEADDRESSAWARE. На 32-разрядной системе таким приложениям дадут только (максимум) 3 Гб, а вот на 64-разрядной системе в их распоряжении будут все 4 Гб.
См. также:

26 комментариев :

  1. Зачетненько.
    Развеелись страхи о переходе на 64бита а так же раскрыта тема - А оно мне нужно? =)

    ОтветитьУдалить
  2. Отлично! Спасибо!

    ОтветитьУдалить
  3. Поддержку коллег - очень интересная статья.

    Я буду пользоваться 64 - у меня есть одно серверное приложение, для которого было бы неплохо иметь пару сотен гигабайт оперативной памяти.

    ОтветитьУдалить
  4. Отдельное спасибо за раздел "64-х битные модели данных" и табличку в нём. Очень удобно и наглядно всё собрано.

    ОтветитьУдалить
  5. >Модель вызова - только одна. Ключевые слова >типа stdcall, register и т.п. игнорируются.
    Если немного подумаете, то модель вызова и на x86 была одна :)
    CALL - он CALL и для stdcall и для register.
    Процессор x86 ничего не знает ни про stdcall, ни про register.
    Модель вызова - это абстракция на уровне языка программирования.
    Или я чего-то путаю? :)

    ОтветитьУдалить
  6. >а вот на 64-х разрядной системе в их распоряжении будут все 4 Гб

    Точнее - 3.5 Гб

    ОтветитьУдалить
  7. Насколько я помню stdcall - механизм вызова в виндовом апи, когда аргументы распложены в стеке справа налево.
    Это вроде никакого отношения к разрядности не имеет.

    ОтветитьУдалить
  8. > Точнее - 3.5 Гб

    Неверно. Точный предел - 4026 Мб.

    > модель вызова и на x86 была одна

    Call - это не модель вызова. Это инструкция процессора.

    Соглашение вызова или модель вызова — регламентирует технические особенности вызова подпрограммы (передачи параметров, возврата из подпрограммы и передачи результата вычислений в точку вызова). Как несложно сообразить, делается это в тесном сотрудничестве с процессором. Модель вызова зависит от процессора И от программной части. Модель вызова IA-64 вы при всём желании не перетащите на x86. Поэтому и говорят о модели вызова процессора. Странно, что это приходится разжёвывать.

    Рекомендую ещё этот пост и эту серию.

    > Процессор x86 ничего не знает ни про stdcall, ни про register.

    Угу, а ещё он про стек ничего не знает :)

    ОтветитьУдалить
  9. > Это вроде никакого отношения к разрядности не имеет.

    Модель вызова и архитектура процессора - тесно связанные понятия.

    ОтветитьУдалить
  10. >Модель вызова и архитектура процессора - тесно связанные понятия.
    Ой ли :)
    http://en.wikipedia.org/wiki/X86_calling_conventions

    - The cdecl calling convention is used by many C systems for the x86 architecture.
    - Syscall is the standard calling convention for 32 bit OS/2 API.
    - Optlink is used by the IBM VisualAge compilers
    - pascal
    - register(An alias for Borland fastcall)
    - stdcall
    - fastcall
    - Microsoft fastcall
    - Borland fastcall
    - Watcom register based calling convention
    - TopSpeed / Clarion / JP
    - safecall
    - thiscall
    for x64:
    - Microsoft x64 calling convention
    - AMD64 ABI convention (is followed on Linux and other non-Microsoft operating systems)

    Метод передачи параметров зависит не от разрядности процессора, т.к. у процессора вообще нет такой сущности как параметр. У него есть только регистры, память и стек. И как там будут размещены параметры - не процессору решать.
    Умеете признавать ошибки? :)

    ОтветитьУдалить
  11. > у процессора вообще нет такой сущности как параметр.

    Нету. Я и не спорил. И что это меняет? И, кстати, этого:

    > И как там будут размещены параметры - не процессору решать.

    я не говорил. Я сказал: "Модель вызова и архитектура процессора - тесно связанные понятия". Где тут про "процессору решать"?

    Ну-ка, если соглашение вызова от процессора не зависит - перетащите-ка мне с x86 на IA-64 соглашение register (которое, напомню, использует стек сопроцессора, которого у IA-64 нет, для передачи вещественных чисел). Или перетащите с IA-64 на x86 модель вызова IA-64 (которая, напомню, использует 96 регистров при вызове функции. Причём функция говорит, какие из этих регистров входные, а какие - выходные. Плюс там есть регистровый стек, плюс...).

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

    ОтветитьУдалить
  12. Даже для x86 приложений лучше не делать Integer(THandle). Т.к. следующим шагом программиста будет присвоение этого значения типу THandle (сам я передавал как параметр в WinAPI). В Win32 всё в порядке, а вот в Win64 грабли - значение THandle бывает больше 2^31. Так что в результате получаем ошибку переполнения значения.

    ОтветитьУдалить
  13. Отличная статья, спасибо. Особенно про разбор типов.

    Что до "поддержки двух версий программ" - это нормальная ситуация. Те, кто не хотят заморочек с разной архитектурой процов, пишут на .NET, и правильно делают.

    Что до выбора ОС "для себя" - я думаю, чем скорее отомрет x86 (32bit), тем будет лучше для всех.
    Он славно послужил нам, но - тем не менее.
    Поэтому "колеблющихся" надо агитировать за 64 :)

    Я, имея на борту домашнего компа ровно 4G памяти + PAE для 32-эмуляции, поставил 64-битную Винду. И не жалею.

    ОтветитьУдалить
  14. Я буду пользоваться 64 - у меня есть одно серверное приложение, для которого было бы неплохо иметь пару сотен гигабайт оперативной памяти.
    Поспешу вас разочаровать. Кроме поддержки процессором, нужна еще поддержка материнкой.
    Даже неплохие серверные платформы имеют куда меньший лимит (к примеру, такая - 144 Gb).

    ОтветитьУдалить
  15. Считаю что 64-бит надуманная проблема и вообще не нужно.
    Судите сами при 32-х разрядной архитектуре уже давно успешно используются винты более 4 гб. И файлы можно создавать более 4 гб. Почему тоже самое нельзя сделать с памятью. Тем более уже существуют устройства наподобие ACARD ANS-9010, где можно тупо подключить данное устройство вставить сколько нужно памяти и работать с данными как с файлом, но при этом файл размещается в памяти. Никакие костыли более просто не нужны.
    Только нужно дать напутсвенного пинка манагерам, чтобы они наконец зашевелили своей толстой задницей и организовали повсеместную продажу данного устройства.

    ОтветитьУдалить
  16. Когда последний раз писали код с File Mapping или AWE? Хочется вернуться во времена сегментированных Win16?

    Нет уж, спасибо. Вот уж действительно, чем скорее x86 вымрет, тем лучше для всех.

    ОтветитьУдалить
  17. Работал с файлами на winAPI строил бинарное дерево в файле (правда на ram-диске). Указываешь адрес в int64 и ходишь по всему файлу куда хочется. Все идет целиком в одном файле. Удобно проектировать, программировать и отлаживать.

    P.S. На форумах пишут что есть проблемы при выделении >2 гб общим массивом даже если система полностью 64-бит (железо, ОС, компилятор). Нужно выделять сегменты памяти в отдельных процессах. В общем 64-бит это тот те же AWE только в профиль.

    ОтветитьУдалить
  18. За девайс Acard ANS-9010 +500 в карму

    ОтветитьУдалить
  19. Уважаемый блогер. Будьте так добры. Прокоментируйте этот пост http://www.ixbt.com/soft/windows-4gb-2.shtml и напишите статью что нужно сделать чтобы выделить приложению >4 гб в 64 битных ОС на практике (желательно с примером). Иначе получается как писал товарищ выше, 64 бит действиетельно не нужно. Также как и нет смысла устанавливать >4гб на комп.

    ОтветитьУдалить
  20. Вы просите меня дать вам решение проблемы, которую вы не озвучили. В Win64 нет никаких проблем с выделением памяти > 4 Гб.

    // Всегда успешно в Win64 - вне зависимости от кол-ва ОЗУ
    // (Разумеется при стандартных условиях на наличие свободного места в адресном пространстве и файле подкачки)
    AllocMem(NativeInt(8) * 1024 * 1024 * 1024 { 8 Гб } );

    Вы путаете ОЗУ (RAM) и (виртуальную) память приложения. Рекомендую ознакомиться.

    ОтветитьУдалить
  21. Неплохой обзор. Вообще, с поддержкой 32-битных приложений на 64-разрядных есть ряд специфики. Например, wow64 хоть и позволяет запускать 32-бита, но не позволяет инъекции кода. Именно по этой причине большинство программ модифицирующих проводник Windows, не запускаются. Подробнее об этом и о других особенностях можно узнать в обзоре "32 битная программа на 64" по адресу http://ida-freewares.ru/support-32-bit-app-in-64-app-windows.ht

    ОтветитьУдалить
  22. >>> wow64 хоть и позволяет запускать 32-бита, но не позволяет инъекции кода

    Вообще-то, никто не запрещает 32-битному коду использовать WriteProcessMemory для внедрения 64-битного кода в 64-битный процесс.

    Другое дело, что в 64-битный процесс нельзя загрузить 32-битные DLL (и наоборот).

    ОтветитьУдалить
  23. А никто не подскажет есть ли аналог директивы __ptr32 в Delphi?
    Если я в х64 приложении хочу явно использовать х32 указатель (для работы с DLL например), как я могу это сделать?
    Можно конечно использовать Integer или Cardinal а самому знать, что там на самом деле указатель, но с подобной директивой все-таки проще.

    ОтветитьУдалить
    Ответы
    1. А можно пример зачем такое надо?

      Удалить
    2. Пример костыльный, в реальных условиях мне сложно представить где это может понадобиться.

      Есть сторонний сервис, очень древний, естественно х32. Для него есть API (DLL из которой экспортируются методы), тоже х32. Разработчики выкатили х64 версию DLL чтобы можно было создавать полноценные х64 проекты и использовать их API при этом.
      Но часть указателей пометили __ptr32
      Да, все заголовочные файлы на С++, Delphi не поддерживается. Вот хочется использовать их API в Delphi на х64 проектах. Непонятно как перевести директиву __ptr32
      Также я не очень понимаю как быть если например: есть метод отправки сообщения, текст сообщения передается через указатель. Указатель помечен как __ptr32 Как быть если мое приложение (путь даже на С++) уже занимает больше 2GB указатель на текст записанный в памяти после никак не поместится в 32 бита? Как это разрулит компилятор С++?

      Удалить
    3. Не очень понятно как они внутри это реализовали. __ptr32 означает обрезку с 8-ми до 4-х байт. Соответственно, при разыменовании он дополняется нулями до полного указателя. Итого, если указатели выходят за 4 Гб, то нарываемся на Access Violation.

      Мне кажется, __ptr32 предназначен в основном для отладчика - чтобы ворочать 32-битными адресами в 32-битном процессе. В остальных случаях он смысла не имеет. Адреса в 64-битном процессе должны быть 64-битными, в 32-битном - 32-битными. Иначе мы нарываемся на проблему потери данных в указателе.

      Я бы, во-первых, отпинал разработчиков DLL за такой идиотизм. Во-вторых, сделал бы функцию-переходник. Пусть она принимает Pointer, делает ему Assert < 4 Gb, передаёт в настоящую функцию как DWORD/Cardinal/UInt32. И обратно: принимает DWORD/Cardinal/UInt32 - конвертирует в Pointer.

      Удалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.