Главная Случайная страница


Полезное:

Как сделать разговор полезным и приятным Как сделать объемную звезду своими руками Как сделать то, что делать не хочется? Как сделать погремушку Как сделать так чтобы женщины сами знакомились с вами Как сделать идею коммерческой Как сделать хорошую растяжку ног? Как сделать наш разум здоровым? Как сделать, чтобы люди обманывали меньше Вопрос 4. Как сделать так, чтобы вас уважали и ценили? Как сделать лучше себе и другим людям Как сделать свидание интересным?


Категории:

АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника






Операционная система x64





В любом обзоре архитектуры Windows лучше начинать с рассмотрения адресации и адресного пространства. Хотя 64-разрядный процессор теоретически мог бы адресоваться к 16 экзабайтам памяти (264), в настоящее время Win64 поддерживает 16 Тб - значение, которое представлено 44 разрядами. Почему же нельзя задействовать все 64 разряда, чтобы адресоваться к 16 экзабайтам памяти? По целому ряду причин.

Начнем с того, что нынешние процессоры x64 обычно позволяют обращаться лишь к 40-разрядному представлению физической памяти (1 Тб). Сама архитектура (но не современное оборудование) допускает расширение до 52 разрядов (4 петабайтов). Даже если бы это ограничение было снято, размеры таблиц страниц, необходимых для проецирования такого громадного объема памяти, оказались бы просто гигантскими.

Как и в Win32, адресуемая память делится на области пользовательского режима и режима ядра. Каждому процессу выделяется собственное уникальное пространство размером 8 Тб в нижней части памяти, а код режима ядра размещается в верхних 8 Тб и разделяется всеми процессами. У разных версий 64-разрядной Windows разные ограничения на объемы физической памяти (табл. 1 и 2).

Табл. 1. Общие ограничения на память

  32-разрядные модели 64-разрядные модели
Виртуальное адресное пространство (одного процесса) 4 Гб 16 Тб
Виртуальное адресное пространство для каждого 32-разрядного процесса 2 Гб (3 Гб при запуске системы с ключом /3GB) 4 Гб при компиляции с параметром /LARGEADDRESSAWARE (иначе 2 Гб)
Виртуальное адресное пространство для каждого 64-разрядного процесса Неприменимо 8 Тб
Пул подкачиваемой памяти режима ядра 470 Мб 128 Гб
Пул не подкачиваемой памяти режима ядра 256 Мб 128 Гб
Элемент системной таблицы страниц (Page Table Entry, PTE) 660-900 Мб 128 Гб

Табл. 2. Ограничения на физическую память в зависимости от процессоров

Операционная система 32-разрядные модели 64-разрядные модели
Windows XP Professional 4 Гб (1-2 процессора) 128 Гб (1-2 процессора)
Windows Server 2003, Standard Edition 4 Гб (1-4 процессора) 32 Гб (1-4 процессора)
Windows Server 2003, Enterprise Edition 64 Гб (1-8 процессоров) 1 Тб (1-8 процессоров)
Windows Server 2003, Datacenter Edition 64 Гб (8-32 процессора) 1 Тб (8-64 процессора)

 

Так же, как и в Win32, размер страницы на платформе x64 равен 4 Кб. Первые 64 Кб адресного пространства никогда не проецируются на физическую память, поэтому младший допустимый адрес - 0x10000. В отличие от Win32 системные DLL по умолчанию не загружаются по адресу в верхней части адресного пространства пользовательского режима. Вместо этого они загружаются после 4 Гб, обычно по адресам, близким к 0x7FF00000000.

Приятная особенность процессоров x64 - поддержка битового флага No Execute, который в Windows используется для реализации аппаратной защиты от выполнения данных как кода (Data Execution Protection, DEP). Существование многих вирусов и "багов" на платформе x86 как раз и обусловлено тем, что процессор может выполнять данные так, будто это байты кода. Переполнение буфера (намеренное или случайное) может привести к тому, что процессор будет выполнять содержимое области памяти, где должны храниться данные. Благодаря DEP операционная система гораздо четче разграничивает области памяти, в которых находится код, и становится способной перехватывать попытки выполнения кода, выходящие за эти границы. Это уменьшает уязвимость Windows перед атаками.

Для выявления ошибок компоновщик (linker) на платформе x64 по умолчанию присваивает адресам загрузки исполняемых файлов первое значение, большее 32-разрядного числа (4 Гб). Это помогает быстро находить проблемные места в существующем коде после его переноса на Win64. В частности, если указатель хранится как 32-битное значение (например, как DWORD), то при работе в Win64-версии вашей программы он окажется усеченным и станет недопустимым, тут же вызвав нарушение доступа к памяти (access violation). Такой прием резко упрощает отлов ошибок, связанных с указателями.

Затронутый вопрос указателей и DWORD-значений позволяет плавно перейти к системе типов в Win64. Какой размер должен иметь указатель? Как насчет LONG? И описателей (handles) наподобие HWND? К счастью, Microsoft, ведя нас по весьма запутанному пути от Win16 к Win32, заодно создала новые модели типов, легко расширяемые и до 64-разрядных. В общем, если не считать нескольких исключений, все типы, отличные от указателей и size_t, совершенно одинаковы, что в старой Win32, что в новой Win64. То есть у 64-битного указателя размер 8 байтов, а у int, long, DWORD и HANDLE остался прежний размер - 4 байта.

Формат файлов в Win64 называется PE32+. С точки зрения структуры, он почти во всем идентичен формату PE в Win32. Лишь некоторые поля вроде ImageBase в заголовке расширены, одно поле удалено и одно изменено так, чтобы оно отражало новый тип процессоров (табл. 3).

Табл. 3. Изменения в полях заголовков PE-файлов

Поле заголовка Изменение
Magic 0x20b вместо 0x10b
BaseOfData Убрано
ImageBase Расширено до 64 битов
SizeOfStackReserve Расширено
SizeOfStackCommit Расширено
SizeOfHeapReserve Расширено
SizeOfHeapCommit Расширено

 

Помимо заголовка PE, изменений не так уж много. В некоторых структурах, например IMAGE_LOAD_CONFIG и IMAGE_THUNK_DATA, часть полей просто расширена до 64 битов. Больший интерес представляет введение раздела PDATA, так как он высвечивает одно из основных различий между реализациями Win32 и Win64: концепцию обработки исключений.

На платформе x86 обработка исключений базируется на стеке. Когда Win32-функция содержит код try/catch или try/finally, компилятор генерирует инструкции, создающие небольшие блоки данных в стеке. Каждый блок данных try указывает на предыдущую структуру данных try, образуя связанный список, в котором структуры, добавленные последними, помещаются в начало списка. По мере вызова функций и выхода из них начало связанного списка обновляется. Как только возникает исключение, ОС просматривает связанный список блоков в стеке, отыскивая подходящий обработчик. Все детали этого процесса я изложил в своей статье за январь 1997 г. (microsoft.com/msj/0197/Exception/Exception.aspx), так что здесь я не буду вдаваться в подробности.

В Win64 (в версиях для x64 и Itanium) применяется табличная обработка исключений. Никакого связанного списка блоков данных try в стеке не создается. Вместо этого каждый исполняемый файл в Win64 содержит таблицу функций периода выполнения (runtime function table). В каждой записи этой таблицы хранятся начальный и конечный адреса функции, а также местонахождение большого набора данных о коде, обрабатывающем исключения в данной функции, и структура ее фрейма стека. Детальное содержимое этих структур см. в определении IMAGE_RUNTIME_FUNCTION_ENTRY в файле WINNT.H и в x64 SDK.

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

А как быть, если вы сгенерировали код непосредственно в памяти, не используя нижележащий модуль формата PE32+? В Win64 имеется API-функция RtlAddFunctionTable, позволяющая сообщить ОС о динамически генерируемом коде.

Недостаток табличной обработки исключений (в сравнении с x86-моделью на основе стека) заключается в том, что поиск записей в таблице функций по адресам кода занимает больше времени, чем простой просмотр связанного списка. Зато исключаются издержки, связанные с тем, что в x86-модели приходится обновлять блок данных try при каждом выполнении функции.

В x64-совместимых версиях Windows не появилось слишком уж много новых API-функций - большинство таковых в Win64 добавлено в выпуски Windows для процессоров Itanium. Две наиболее важные API-функции - IsWow64Process и GetNativeSystemInfo - позволяют Win32-приложениям определять, выполняются ли они в Win64, и, если да, выяснять реальные возможности данной системы. Если же 32-разрядный процесс обращается к GetSystemInfo, он видит лишь те возможности, которые свойственны обычной 32-разрядной системе. Так, GetSystemInfo способна сообщать о диапазонах адресов лишь 32-разрядных процессов. В табл. 4 перечислены API-функции для платформы x64, которых не было на платформе x86.

 

Табл. 4. Изменения в полях заголовков PE-файлов

 

Функциональность API-функции
Обработка исключений RtlAddFunctionTable RtlDeleteFunctionTable RtlRestoreContext RtlLookupFunctionEntry RtlInstallFunctionTableCallback
Реестр RegDeleteKeyEx RegGetValue RegQueryReflectionKey
NUMA (Non-Uniform Memory Access) GetNumaAvailableMemoryNode GetNumaHighestNodeNumber GetNumaNodeProcessorMask GetNumaProcessorNode
Перенаправление WOW64 Wow64DisableWow64FsRedirection Wow64RevertWow64FsRedirection RegDisableReflectionKey RegEnableReflectionKey
Разное GetLogicalProcessorInformation QueryWorkingSetEx SetThreadStackGuarantee GetSystemFileCacheSize SetSystemFileCacheSize EnumSystemFirmwareTables GetSystemFirmwareTable

4. Разработка для x64 с помощью Visual C++

 

Хотя x64-код можно было писать в Microsoft C++ до появления Visual Studio 2005, это было весьма неудобно. Поэтому здесь исходим из того, что вы работаете в Visual Studio 2005 и что вы выбрали инструментарий для платформы x64, который по умолчанию не устанавливается. Предположим, что у нас уже есть какой-то Win32-проект (пользовательского режима) на C++, который мы хотим компилировать для обеих платформ - как x86, так и x64.

Первый шаг в компиляции программы для x64 - создание конфигурации 64-разрядной сборки. Как пользователь Visual Studio, известно, что по умолчанию у проектов две конфигурации сборки: Debug и Retail. Поэтому остается создать еще две конфигурации: Debug и Retail для x64.

Начнем с загрузки существующего проекта/решения. В меню Build выберите Configuration Manager. В диалоговом окне Configuration Manager в раскрывающемся списке Active Solution Platform выберите New (рис. 1). После этого вы должны увидеть диалог New Solution Platform.

 

 

Рис. 1. Создание новой конфигурации сборки

 

Выберем x64 в качестве новой платформы (рис. 2), прочие параметры оставьте в состоянии по умолчанию и щелкните OK. Вот и все! Теперь у нас должно быть четыре конфигурации сборки: Win32 Debug, Win32 Retail, x64 Debug и x64 Retail. Переключаться между ними мы будем через Configuration Manager.

 

Рис. 2. Выбор платформы сборки

 

Теперь посмотрим, насколько совместим с x64 наш код. Создадим конфигурацию x64 Debug по умолчанию и соберем проект. Если его код не тривиален, все шансы за то, что мы получим при компиляции ошибки, не встречавшиеся в Win32-конфигурации. Но справиться с этими проблемами и сделать код действительно совместимым как с Win32, так и с x64 сравнительно легко, если только мы не нарушали все принципы написания портируемого C++-кода. И не потребуются тонны директив условной компиляции.

 

Date: 2015-09-18; view: 450; Нарушение авторских прав; Помощь в написании работы --> СЮДА...



mydocx.ru - 2015-2024 year. (0.005 sec.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав - Пожаловаться на публикацию