
Танненбаум Е. Архітектура компютера [pdf]
.pdf

5 4 4 Глава 7. Уровень языка ассемблера
Конец модуля
Словарь перемещений
Машинныекомандыиконстанты
Таблица внешних ссылок Таблица точек входа Идентификация
Рис. 7.5. Внутренняяструктураобъектного модуля
Вторая часть объектного модуля — это список символов, определенны ле, вместе с их значениями. К этим символам могут обращаться другие Например, если модуль состоит из процедуры bigbug, то элемент табли содержать цепочку символов «bigbug», за которой будет следовать соотв щий адрес. Программист наязыке ассемблера с помощью директивы PUBLI вает, какие символьные имена считаются точками входа.
Третья часть объектного модуля состоит из списка символьных имен, используются в этом модуле, но определены в других модулях. Здесь такж ся список, который показывает, какие именно символьные имена испо теми или иными машинными командами. Второй список нужен для тог компоновщик мог вставить правильные адреса в команды, которые исп внешние имена. Процедура может вызывать другие независимо трансл процедуры, объявив имена вызываемых процедур внешними. Программис ке ассемблера с помощьюдирективы EXTERN указывает, какие символы нуж вить внешними. В некоторых компьютерах точки входа и внешние ссылки нены в одной таблице.
Третья часть объектного модуля — это машинные команды и конста единственная часть объектного модуля, которая будет загружаться в па выполнения. Остальные 5 частей используются компоновщиком, а затем ваются еще до начала выполнения программы.
Пятая часть объектного модуля — это словарь перемещений. К коман торые содержат адреса памяти, должна прибавляться константа перем (см. рис. 7.4). Компоновщик сам не может определить, какие слова в ч части содержат машинные команды, а какие — константы. Поэтому в этой содержится информация о том, какие адреса нужно переместить. Это мо битовая таблица, где на каждый бит приходится потенциально переме адрес, либо явный список адресов, которые нужно переместить.
Шестая часть содержит указание на конец модуля, а иногда — конт суммудля определения ошибок, сделанныхво время чтения модуля, и адре рого нужно начинать выполнение.
Большинству компоновщиков требуется два прохода. На первом

Связывание и загрузка |
54 |
Время принятия решения и динамическое перераспределение памяти
В мультипрограммной системе программу можно считать в основную память, з пустить ее на некоторое время, записать на диск, а затем снова считать в основну память для выполнения. В большой системе с большим количеством програм трудно быть уверенным, что программа считывается каждый раз в одно и то ж место в памяти.
На рис. 7.6 показано, что произойдет, если уже перемещенная программа (с рис. 7.4, б) будет загружена в адрес 400, а не в адрес 100, куда ее изначально по местил компоновщик. Все адреса памяти будут неправильными. Более тог информация о перемещении уже давно удалена. Даже если эта информация был бы доступна, перемещать все адреса при каждой перекачке программы было б неудобно.
Проблема перемещения программ, уже связанных и размещенных в памят близко связана с моментом времени, в который совершается финальное связыв ние символических имен с абсолютными адресами физической памяти. В програм ме содержатся символические имена для адресов памяти (например, BR L). Врем в которое определяется адрес в основной памяти, соответствующий L, называетс временем принятия решения. Существует по крайней мере шесть вариантов дл времени принятия решения относительно привязок:
1.Когда пишется программа.
2.Когда программа транслируется.
3.Когда программа компонуется, но еще до загрузки.
4.Когда программа загружается.
5.Когда загружается базовый регистр, который используется для адресации
6.Когда выполняется команда, содержащая адрес.
Если команда, содержащая адрес памяти, перемещается после связывания, это адрес будет неправильным (предполагается, что объект, на который происходи ссылка, тоже перемещен). Если транслятор производит исполняемый двоичны код, то связывание происходит во время трансляции и программа должна быт запущена в адресе, в котором этого ожидает транслятор. При применении метод описанного в предыдущем разделе, во время связывания символические имен соотносятся с абсолютными адресами, и именно по этой причине перемещать про граммы после связывания нельзя (см. рис. 7.6).
Здесь возникают два вопроса. Первый — когда символические имена связыва ются с виртуальными адресами, а второй — когда виртуальные адреса связыва ются с физическими адресами? Только после двух этих операций процесс связы вания можно считать завершенным. Когда компоновщик связывает отдельны


Связывание и загрузка |
547 |
Любой механизм, который позволяет легко изменять отображение виртуальных адресов на адреса основной физической памяти, будет облегчать перемещение программы в основной памяти, даже если они уже связаны с виртуальным адресным пространством. Одним из таких механизмов является разбиение на страницы. Если программа перемещается в основной памяти, нужно изменить только ее таблицу страниц, но не саму программу.
Второймеханизм — использование регистраперемещения. Компьютер CDC 6600 и его последователи содержали такой регистр. В машинах, в которых используется эта технология перемещения, регистр всегда указывает на физический адрес начала текущей программы. Аппаратное обеспечение прибавляет регистр перемещения ко всем адресам памяти, прежде чем отправить их в память. Весь процесс перемещения является «прозрачным» для каждой пользовательской программы. Пользовательские программы даже не подозревают, что этот процесс происходит. Если программа перемещается, операционная система должна обновить регистр перемещения. Такоймеханизмменееобычен,чемразбиениенастраницы, поскольку перемещаться должна вся программа целиком (однако если есть отдельные регистры для перемещения кода и перемещения данных, как, например, в процессоре Intel 8088, то в этом случае программу нужно перемещать как два компонента).
Третий механизм можно использовать в машинах, которые могут обращаться к памяти относительно счетчика команд. Всякий раз, когда программа перемещается в основной памяти, нужно обновлять только счетчик команд. Программа, все обращения к памяти которой либо связаны со счетчиком команд, либо абсолютны (например, обращения к регистрам устройств ввода-вывода в абсолютных адресах), называется позиционно-независимой программой. Позиционно-независи- мую процедуру можно поместить в любом месте виртуального адресного пространства без настройки адресов.
Динамическое связывание
Стратегия связывания, которую мы обсуждали в разделе «Задачи компоновщика», имеет одну особенность: связь со всеми процедурами, нужными программе, устанавливается до начала работы программы. Однако если мы будем устанавливать все связи до начала работы программы в компьютере с виртуальной памятью, то мы не используем всех возможностей виртуальной памяти. Многие программы содержат процедуры, которые вызываются только при определенных обстоятельствах. Например, компиляторы содержат процедуры для компиляции редко используемых операторов, а также процедуры для исправления ошибок, которые встречаются редко.
Более гибкий способ связывания отдельно скомпилированных процедур — установление связи с каждой процедурой в тот момент, когда она впервые вызывается. Этот процесс называется динамическим связыванием. Впервые он был применен в системе MULTICS. В следующих разделах мы рассмотрим применение динами-
ческого связывания в нескольких системах.


Связывание и загрузка |
5 |
ются к первому слову соответствующего блока, как показано на рис. 7.7, а. Ком лятор заполняет это слово либо недействительным адресом, либо специальн набором битов, который вызывает системное прерывание (ловушку).
Когда вызывается процедура в другом сегменте, попытка косвенно обратит к недействительному слову вызывает системное прерывание компоновщика. тем компоновщик находит цепочку символов в слове, которое следует за нед ствительным адресом, и начинает искать пользовательскую директорию для ск пилированной процедуры с таким именем. Затем этой процедуре приписывае виртуальный адрес (обычно в ее собственном сегменте), и этот виртуальный ад записывается поверх недействительного адреса, как показано на рис. 7.7, б. По этого команда, которая вызвала ошибку, выполняется заново, что позвол программе продолжать работу с того места, где она находилась до системного п рывания.
Все последующие обращения к этой процедуре будут выполняться без ошиб поскольку слово с косвенным адресом теперь содержит действительный вир альный адрес. Следовательно, компоновщик вызывается только тогда, ко некоторая процедура вызывается впервые. После этого вызывать компоновщ уже не нужно.
Динамическое связывание в системе Windows
Все версии операционной системы Windows, в том числе NT, поддерживают ди мическое связывание. При динамическом связывании используется специальн файловый формат, который называется DLL (Dynamic Link Library — динами ски подключаемая библиотека). Динамически подключаемые библиотеки мо содержать процедуры, данные или и то и другое вместе. Обычно они использу ся для того, чтобы два и более процессов могли разделять процедуры и данн библиотеки. Большинство файлов DDL имеют расширение .dll, но встречаю
идругие расширения, например .drv (для библиотек драйверов — driver librar
и.fon (для библиотек шрифтов — font libraries).
Самая распространенная форма динамически подключаемой библиотеки библиотека, состоящая из набора процедур, которые могут загружаться в пам
ик которым имеют доступ несколько процессов одновременно. На рис. 7.8 пока ны два процесса, которые разделяют файл DLL, содержащий 4 процедуры, А, В
иD. Программа 1 использует процедуру А; программа 2 использует процедуру хотя они вполне могли бы использовать одну и ту же процедуру.
Файл DLL строится компоновщиком из коллекции входных файлов. Постр ние файла DDL очень похоже на построение исполняемого двоичного кода, тол при создании файла DLL компоновщику передается специальный флаг, котор сообщает ему, что создается именно файл DLL. Файлы DLL обычно конструи ются из набора библиотечных процедур, которые могут понадобиться нескольк процессорам. Типичными примерами файлов DLL являются процедуры сопря

5 5 0 Глава 7. Уровень языка ассемблера
номно. Если мы будем использовать файлы DLL, то каждая библиотека б являться один раз на диске и один раз в памяти.
Пользовательсий |
Пользовательский |
процесс 1 |
процесс 2 |
Рис. 7.8. Два процесса используют один файл DLL
Этот подход, кроме того, упрощает обновление библиотечных процедур ляет осуществлять обновление процедур, даже после того как программы, зующие их, были скомпилированы и связаны. Для коммерческих прогр пакетов, где пользователям обычно недоступна входная программа, исп ние файлов DLL означает, что поставщик программного обеспечения мож руживать ошибки в библиотеках и исправлять положение, просто распро новые файлы DLL по Интернету, причем при этом не требуется производ каких изменений в основных бинарных программах.
Основное различие между файлом DLL и исполняемой двоичной прог состоит в том, что файл DLL не может запускаться и работать сам по с скольку у него нет ведущей программы). Он также содержит совершенно информацию в заголовке. Кроме того, файл DLL имеет несколько допол ных процедур, не связанных с процедурами в библиотеке. Например, сущ одна процедура, которая вызывается автоматически всякий раз, когда нов цесс связывается с файлом DLL, и еще одна процедура, которая вызывает матически всякий раз, когда связь процесса с файлом DLL отменяется. Э цедуры могут выделять и освобождать память или управлять другими рес которые необходимы файлу DLL.
Программа может установить связь с файлом DLL двумя способами: щью неявного связывания и с помощью явного связывания. При неявно

Краткое содержание главы |
5 |
ма может быть связана с несколькими библиотеками импорта. Когда програм которая применяет неявное связывание, загружается в память для выполнен система Windows проверяет, какие файлы DLL использует эта программа и все эти файлы уже находятся в памяти. Те файлы, которых еще нет в памяти, загруж ются туда немедленно (но необязательно целиком, поскольку они разбиты на ст ницы). Затем производятся некоторые изменения в структурах данных в би лиотеках импорта так, чтобы можно было определить местоположение вызываем процедур (это похоже на изменения, показанные на рис. 7.7). Их тоже нуж отобразить в виртуальное адресное пространство программы. С этого момен пользовательскую программу можно запускать. Теперь она может вызывать пр цедуры в файлах DLL, как будто они статически связаны с ней.
Альтернативой неявного связывания является явное связывание. Такой п ход не требует библиотек импорта, и при нем не нужно загружать файлы D одновременно с пользовательской программой. Вместо этого пользовательск программа делает явный вызов прямо во время работы, чтобы установить свя с файлом DLL, а затем совершает дополнительные вызовы, чтобы получить адр процедур, которые ей требуются. Когда все это сделано, программа соверш финальный вызов, чтобы разорвать связь с файлом DLL Когда последний пр цесс разрывает связь с файлом DLL, этот файл может быть удален из памяти.
Важно осознавать, что процедура в файле DLL не имеет отличительных о бенностей (как поток или процесс). Она работает в потоке вызывающей програ мы и для своих локальных переменных использует стек вызывающей программ Она может содержать специфичные для процесса статические данные (а так разделенные данные) и в остальном работает как статически связанная проце ра. Единственным существенным отличием является способ установления свя
Динамическое связывание в системе UNIX
В системе UNIX используется механизм, по сути сходный с файлами DLL в W dows. Это библиотека коллективного доступа. Как и файл DLL, библиотека ко лективного доступа представляет собой архивный файл, содержащий несколь процедур или модулей данных, которые присутствуют в памяти во время рабо программы и одновременно могут быть связаны с несколькими процессами. Ста дартная библиотека С и большинство сетевых программ являются библиотека коллективного доступа.
Система UNIX поддерживает только неявное связывание, поэтому библиоте коллективного доступа состоит из двух частей: главной библиотеки (host librar которая статически связана с исполняемым файлом, и целевой библиотеки (tar library), которая вызывается во время работы программы. Несмотря на некотор различия в деталях, по существу это то же, что файлы DLL.

5 52 Глава 7. Уровень языка ассемблера
чеством ресурсов (например, кредитные карточки, различные приборы и п ные цифровые устройства). Программа на языке ассемблера — это символ репрезентация программы на машинном языке. Она транслируется на ма язык специальной программой, которая называется ассемблером.
Если для успеха какого-либо аппарата требуется быстрое выполнение п мы, то лучше всего сначала написать программу на языке высокого уровн путем измерений установить, выполнение какой части программы занима шую часть времени, и переписать на языке ассемблера только эту часть п мы. Практика показывает, что часто небольшая часть всей программы з большую часть всего времени выполнения этой программы.
Во многих ассемблерах предусмотрены макросы, которые позволяют п мистам давать символические имена целым последовательностям команд но эти макросы могут быть параметризированы прямым путем. Макросы ются с помощью алгоритма обработки строковых литералов.
Большинство ассемблеров двухпроходные. Во время первого прохода с таблица символов для меток, литералов и объявляемых идентификаторо вольные имена можно либо не сортировать и искать путем последовательн смотра таблицы, либо сначала сортировать, а потом применять двоичны либо хэшировать. Если символьные имена не нужно удалять во время перв хода, то хэширование — это лучший метод. Во время второго прохода про порождение программы. Одни директивы выполняются при первом пр другие — при втором.
Программы, которые ассемблируются независимо друг от друга, мож зать вместе и получить исполняемую двоичную программу, которую можн кать. Эту работу выполняет компоновщик. Его задачи — это перемещение ти и связывание имен. Динамическое связывание — это технология, при определенные процедуры не связываются до тех пор, пока они не будут в Библиотеки коллективного пользования в системе UNIX и файлы DLL (д чески подсоединяемые библиотеки) в системе Windows используют техн динамического связывания.
Вопросы и задания
1. 1% определенной программы отвечает за 50% времени выполнен программы. Сравните следующие три стратегии с точки зрения программирования и времени выполнения. Предположим, что для ния программы на языке С потребуется 100 человеко-месяцев, а пр на языке ассемблера написать в 10 раз труднее, но зато она работает эффективнее.
1. Вся программа написана на языке С.