ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 24.12.2021

Просмотров: 12179

Скачиваний: 10

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

5 4 8 Глава 7. Уровень языка ассемблера

рая может быть вызвана. Этот блок информации начинается со слова, зарезерви-
рованного для виртуального адреса процедуры, а за ним следует имя процедуры,
которое сохраняется в виде цепочки символов.

Сегмент процедуры А

CALL EARTH

CALL FIRE

CALL ATR

CALL WATER

CALL EARTH

CALL WATER

Сегмент связывания

Косвенная

Е А

R

Косвенная

А

1 R

адресация

Т

»У/

адресация

УУ/ЛУ/

Косвенная

F

1 R

адресация

Е

 У/%

1

//,

Косвенная

W A T

адресация

Е

R

 У/у

 Слово с косвенным адресом

I Информация о связывании

Г для процедуры AIR

Имя процедуры хранится
в виде цепочки символов

Сегмент процедуры А

Сегмент связывания

CALL EARTH

CALL FIRE

CALL ATR

CALL WATER

CALL EARTH

CALL WATER

Адрес процедуры

EARTH

E | A | R | T | H

Косвенная адресация

R

Косвенная адресация

Косвенная адресация

W | A | T | E 1 R

Связан с процедурой EARTH

Рис. 7.7. Динамическое связывание: до вызова процедуры EARTH (a);

после того как процедура EARTH была вызвана и связана (б)

При применении динамического связывания вызовы процедур во входном язы-

ке транслируются в команды, которые с помощью косвенной адресации обраща-


background image

Связывание и загрузка 549

ются к первому слову соответствующего блока, как показано на рис. 7.7,

 а.

 Компи-

лятор заполняет это слово либо недействительным адресом, либо специальным
набором битов, который вызывает системное прерывание (ловушку).

Когда вызывается процедура в другом сегменте, попытка косвенно обратиться

к недействительному слову вызывает системное прерывание компоновщика. За-
тем компоновщик находит цепочку символов в слове, которое следует за недей-
ствительным адресом, и начинает искать пользовательскую директорию для ском-
пилированной процедуры с таким именем. Затем этой процедуре приписывается
виртуальный адрес (обычно в ее собственном сегменте), и этот виртуальный адрес
записывается поверх недействительного адреса, как показано на рис. 7.7,

 б.

 После

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

Все последующие обращения к этой процедуре будут выполняться без ошибок,

поскольку слово с косвенным адресом теперь содержит действительный вирту-
альный адрес. Следовательно, компоновщик вызывается только тогда, когда
некоторая процедура вызывается впервые. После этого вызывать компоновщик
уже не нужно.

Динамическое связывание в системе Windows

Все версии операционной системы Windows, в том числе NT, поддерживают дина-

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

 DLL (Dynamic Link Library

 —

 динамиче-

ски подключаемая библиотека).

 Динамически подключаемые библиотеки могут

содержать процедуры, данные или и то и другое вместе. Обычно они используют-
ся для того, чтобы два и более процессов могли разделять процедуры и данные
библиотеки. Большинство файлов DDL имеют расширение

 .dll,

 но встречаются

и другие расширения, например .drv (для библиотек драйверов — driver libraries)
и .fon (для библиотек шрифтов — font libraries).

Самая распространенная форма динамически подключаемой библиотеки —

библиотека, состоящая из набора процедур, которые могут загружаться в память
и к которым имеют доступ несколько процессов одновременно. На рис. 7.8 показа-
ны два процесса, которые разделяют файл DLL, содержащий 4 процедуры, А, В, С
и D. Программа 1 использует процедуру А; программа 2 использует процедуру С,
хотя они вполне могли бы использовать одну и ту же процедуру.

Файл DLL строится компоновщиком из коллекции входных файлов. Построе-

ние файла DDL очень похоже на построение исполняемого двоичного кода, только
при создании файла DLL компоновщику передается специальный флаг, который
сообщает ему, что создается именно файл DLL. Файлы DLL обычно конструиру-
ются из набора библиотечных процедур, которые могут понадобиться нескольким
процессорам. Типичными примерами файлов DLL являются процедуры сопряже-
ния с библиотекой системных вызовов Windows и большими графическими биб-
лиотеками. Применяя файлы DDL, мы можем сэкономить пространство в памяти
и на диске. Если какая-то библиотека была связана с каждой программой, ис-
пользующей ее, то она будет появляться во многих исполняемых двоичных про-

граммах в памяти и на диске, а забивать пространство такими дубликатами неэко-


background image

5 5 0 Глава 7. Уровень языка ассемблера

номно. Если мы будем использовать файлы DLL, то каждая библиотека будет по-
являться один раз на диске и один раз в памяти.

Пользовательсий

процесс 1

Пользовательский

процесс 2

Рис. 7.8. Два процесса используют один файл DLL

Этот подход, кроме того, упрощает обновление библиотечных процедур и позво-

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

Основное различие между файлом DLL и исполняемой двоичной программой

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

Программа может установить связь с файлом DLL двумя способами: с помо-

щью неявного связывания и с помощью явного связывания. При

 неявном связы-

вании

 пользовательская программа статически связывается со специальным фай-

лом, так называемой

 библиотекой импорта,

 которая образована обслуживающей

программой (утилитой), извлекающей определенную информацию из файла DLL.

Библиотека импорта обеспечивает связующий элемент, который позволяет пользо-

вательской программе получать доступ к файлу DLL. Пользовательская програм-


background image

Краткое содержание главы 551

ма может быть связана с несколькими библиотеками импорта. Когда программа,
которая применяет неявное связывание, загружается в память для выполнения,
система Windows проверяет, какие файлы DLL использует эта программа и все ли
эти файлы уже находятся в памяти. Те файлы, которых еще нет в памяти, загружа-
ются туда немедленно (но необязательно целиком, поскольку они разбиты на стра-
ницы). Затем производятся некоторые изменения в структурах данных в биб-
лиотеках импорта так, чтобы можно было определить местоположение вызываемых
процедур (это похоже на изменения, показанные на рис. 7.7). Их тоже нужно
отобразить в виртуальное адресное пространство программы. С этого момента
пользовательскую программу можно запускать. Теперь она может вызывать про-
цедуры в файлах DLL, как будто они статически связаны с ней.

Альтернативой неявного связывания является

 явное связывание.

 Такой под-

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

Важно осознавать, что процедура в файле DLL не имеет отличительных осо-

бенностей (как поток или процесс). Она работает в потоке вызывающей програм-
мы и для своих локальных переменных использует стек вызывающей программы.
Она может содержать специфичные для процесса статические данные (а также
разделенные данные) и в остальном работает как статически связанная процеду-
ра. Единственным существенным отличием является способ установления связи.

Динамическое связывание в системе UNIX

В системе UNIX используется механизм, по сути сходный с файлами DLL в Win-
dows. Это библиотека коллективного доступа. Как и файл DLL, библиотека кол-

лективного доступа представляет собой архивный файл, содержащий несколько

процедур или модулей данных, которые присутствуют в памяти во время работы
программы и одновременно могут быть связаны с несколькими процессами. Стан-
дартная библиотека С и большинство сетевых программ являются библиотеками
коллективного доступа.

Система UNIX поддерживает только неявное связывание, поэтому библиотека

коллективного доступа состоит из двух частей:

 главной библиотеки

 (host library),

которая статически связана с исполняемым файлом, и

 целевой библиотеки

 (target

library), которая вызывается во время работы программы. Несмотря на некоторые
различия в деталях, по существу это то же, что файлы DLL.

Краткое содержание главы

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


background image

5 5 2 Глава 7. Уровень языка ассемблера

чеством ресурсов (например, кредитные карточки, различные приборы и портатив-
ные цифровые устройства). Программа на языке ассемблера — это символическая
репрезентация программы на машинном языке. Она транслируется на машинный
язык специальной программой, которая называется ассемблером.

Если для успеха какого-либо аппарата требуется быстрое выполнение програм-

мы, то лучше всего сначала написать программу на языке высокого уровня, затем
путем измерений установить, выполнение какой части программы занимает боль-
шую часть времени, и переписать на языке ассемблера только эту часть програм-
мы. Практика показывает, что часто небольшая часть всей программы занимает
большую часть всего времени выполнения этой программы.

Во многих ассемблерах предусмотрены макросы, которые позволяют програм-

мистам давать символические имена целым последовательностям команд. Обыч-
но эти макросы могут быть параметризированы прямым путем. Макросы реализу-
ются с помощью алгоритма обработки строковых литералов.

Большинство ассемблеров двухпроходные. Во время первого прохода строится

таблица символов для меток, литералов и объявляемых идентификаторов. Сим-
вольные имена можно либо не сортировать и искать путем последовательного про-
смотра таблицы, либо сначала сортировать, а потом применять двоичный поиск,
либо хэшировать. Если символьные имена не нужно удалять во время первого про-
хода, то хэширование — это лучший метод. Во время второго прохода происходит
порождение программы. Одни директивы выполняются при первом проходе, а

другие — при втором.

Программы, которые ассемблируются независимо друг от друга, можно свя-

зать вместе и получить исполняемую двоичную программу, которую можно запус-
кать. Эту работу выполняет компоновщик. Его задачи — это перемещение в памя-
ти и связывание имен. Динамическое связывание — это технология, при которой
определенные процедуры не связываются до тех пор, пока они не будут вызваны.

Библиотеки коллективного пользования в системе UNIX и файлы DLL (динами-

чески подсоединяемые библиотеки) в системе Windows используют технологию
динамического связывания.

Вопросы и задания

1. 1% определенной программы отвечает за 50% времени выполнения этой

программы. Сравните следующие три стратегии с точки зрения времени
программирования и времени выполнения. Предположим, что для написа-
ния программы на языке С потребуется 100 человеко-месяцев, а программу
на языке ассемблера написать в 10 раз труднее, но зато она работает в 4 раза
эффективнее.

1. Вся программа написана на языке С.

2. Вся программа написана на ассемблере.
3. Программа сначала написана на С, а затем нужный 1% программы пере-

писан на ассемблере.

2. Для двухпроходных ассемблеров существуют определенные соглашения.

Подходят ли они для компиляторов?