Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
385
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

Я, кстати, добавил специальный инструмент в свою программу ProcessInfoexe (см. главу 4) Он показывает список всех модулей, находящихся в адресном пространстве процесса. В колонке BaseAddr сообщается виртуальный адрес, по которому загружен модуль. Справа от BaseAddr расположена колонка ImagAddr Обычно оиа пуста, указывая, что соответствующий модуль загружен по его предпочтительному базовому адресу. Так и должно быть для всех модулей Однако, ссли в этой колонке присутствует адрес в скобках, значит, модуль загружен не по предпочтительному базовому адресу, и в колонке ImagAddr показывается базовый адрес, взятый из заголовка его файла на диске.

Ниже приведена информация о процессе Acrord32.exe, предоставленная моей программой ProcessInfo Обратите внимдние, что часть модулей загружена по предпочтительным базовым адресам, а часть — нет. Для последних сообщается один и тот же базовый адрес, 0x10000000; значит, автор этих DLL не подумал о проблемах модификации базовых адресов — пусть ему будет стыдно.

Связывание модулей

Модификация базовых адресов действительно очень важна и позволяет существенно повысить производительность всей системы Но Вы можете сделать еще больше. Допустим, Вы должным образом модифицировали базовые адреса всех модулей своего приложения. Вспомните из главы 19, как загрузчик определяет адреса импортируемых идентификаторов он записывает виртуальные адреса идентификаторов в раздел импорта ЕХЕ-модуля. Это позволяет, ссылаясь на импортируемые идентификаторы, адресоваться к нужным участкам в памяти

Давайте поразмыслим Сохраняя виртуальные адреса импортируемых идентификаторов в разделе импорта ЕХЕ-модуля, загрузчик записывает их на те страницы памяти, где содержится этот раздел Здесь включается в paбoтy механизм копирования при записи, и их копии попадают в страничный файл. И у нас опять та же проблема, что и при модификации базовых адресов: отдельные части проекции модуля периодически сбрасываются в страничный файл и вновь подгружаются из него. Кроме того, загрузчику приходится преобразовывать адреса всех импортируемых идентификаторов (для каждого модуля), на что может понадобиться немалое время.

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

В Visual Studio есть еще одна утилита, Bind.exe. Информацию о том, как ею пользоваться, Вы получите, запустив Bind.exe без ключей в командной строке. Она описана в документации Platform SDK, и я не буду ее здесь детально рассматривать Добавлю лишь, что в ней, как и в утилите Rebase, тоже нет ничего сверхъестественного: она просто вызывает функцию BindlmageEx для каждого указанного файла. Вот что представляет собой эта функция.

BOOL BindImageEx(

DWORD dwFlags, // управляющие флаги

PSTR pszImageName, // полное имя обрабатываемого файла PSTR pszDllPath, // пугь для поиска образов файлов

PSTR pszSymbolPath, // путь для поиска О1ладочной информации

PIMAGEHLP_STATUS_ROUTINE StatusRoutine); // функция обратного вызова

Последний параметр, StatusRoutine, — адрес функции обратного вызова, к которой периодически обращается BindImageEx, позволяя отслеживать процесс связывания Прототип функции обратного вызова должен выглядеть так:

BOOL WINAPI StatusRoutine(

IMAGtHLP_STATUS_RLASON Reason, // причина неудачи

PSTR pszImageName, // полное имя обрабатываемою файла

PSTR pszDllName. // полное имя DLL

ULONG_PTR VA, // вычисленный виртуальный адрес

ULONG_PTR Parameter); // дополнительные сведения (зависят от значения Reason)

Когда Вы запускаете утилиту Bind, указывая ей нужный файл, она выполняет следующие операции.

1.Открывает раздел импорта указанного файла.

2.Открывает каждую DLL, указанную в разделе импорта, и просматривает ее заголовок, чтобы определить предпочтительный базовый адрес.

3.Отыскивает все импортируемые идентификаторы в разделе экспорта DLL

4.Получает RVA (относительный виртуальный адрес) идентификатора, суммирует его с предпочтительным базовым адресом модуля и записывает полученное значение в раздел импорта обрабатываемого файла.

5.Вносит в раздел импорта модуля некоторую дополнительную информацию, включая имена всех DLL, с которыми связывается файл, и их временные метки.

Вглаве 19 мы исследовали раздел импорта CaIc,exe с помощью утилиты DumpBln. В конце выведенного ею текста можно заметить информацию о связывании, добавленную при операции по п 5 Вот эти строки

Header contains the following bound import information: Bound to SHELL32 dll [36E449EO] Mon Мяг 08 14:06:24 1999 Bound to MSVCRT dll [36BB8379] Fri Feb Ob 15:49:13 1999 Bound to ADVAPI32.dll [36E449E1] Mon Mar 08 14:06:25 1999 Bound to KERNEL32 dll [36DDAD55] Wed Mar 03 13:44:53 1999 Bound to GDI32 dll [36E449EO] Mon Mar 08 14:06:24 1999 Bound to USER32 dll [36E449EO] Mon Mar 08 14:06:24 1999