Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
У. Столлингс ГЛАВА 7 Управление памятью.doc
Скачиваний:
40
Добавлен:
11.05.2015
Размер:
281.09 Кб
Скачать

Приложение. Загрузка и связывание

Первым шагом в создании активного процесса является загрузка программы в основную память и создание образа процесса (рис. 7.13). На рис. 7.14 показан типичный для большинства систем сценарий. Приложение состоит из ряда скомпилированных или ассемблированных модулей в виде объектного кода. Эти модули связываются для разрешения всех ссылок между ними, а также обращений к библиотечным подпрограммам (которые могут быть внедрены в программу или быть совместно используемым кодом, представляемым операционной системой). В этом приложении мы познакомимся с ключевыми свойствами компоновщиков и загрузчиков. Для ясности изложения мы начнем с описания задачи загрузки программы, состоя­щей из одного модуля, когда связывание не требуется.

Загрузка

На рис. 7.14 загрузчик размещает загружаемый модуль в основной памяти, начиная с адреса х. При этом должны удовлетворяться требования к адресации процесса, приведенные на рис. 7.1. В целом могут использоваться три подхода.

  • Абсолютная загрузка.

  • Перемещаемая загрузка.

  • Динамическая загрузка времени исполнения.

Абсолютная загрузка

Абсолютный загрузчик требует, чтобы данный загружаемый модуль всегда располагался в одном и том же месте в памяти. Следовательно, в модуле, передаваемом для загрузки, все обращения должны быть к конкретным, или абсо­лютным, адресам основной памяти.

Назначение определенных адресов ссылкам к памяти в программе может быть выполнено либо программистом, либо автоматически в процессе компиляции или ассемблирования (табл. 7.2,а). У такого подхода имеется ряд серьезных недостатков. Во-первых, каждый программист должен знать стратегию размещения модулей в основной памяти. Во-вторых, при любых изменениях в программе, которые включают вставку или удаление кода или данных, требуется соответствующим образом изменить все адреса. Поэтому желательно, чтобы все адреса в памяти были выражены символьно, с тем чтобы в процессе компиляции или ассемблирования разрешить эти символьные ссылки (см. рис. 7.15). Каждая ссылка на команду или элемент данных изначально представлена символом. При подготовке модуля к абсолютной загрузке ассемблер или компилятор преобразуют все эти ссылки в конкретные адреса, как показано на рис. 7.15,в.

Таблица 7.2. Связывание адресов

а) Загрузчик

Этап связывания Действия

Разработка Программист использует конкретные физические адреса непосредственно

программы в программе

Компиляция Программа содержит ссылки на символьные адреса, которые преобразуются в реальные физические адреса компилятором или ас­семблером

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

Исполнение Загруженная программа использует относительные адреса, которые

программы динамически конвертируются процессором в абсолютные

б) Компоновщик

Этап компоновки Действия

Разработка Не разрешены никакие ссылки на внешний код или данные. Программист

программы должен разместить в программе исходный код всех под­программ, на которые имеются ссылки

Компиляция Код каждой подпрограммы, к которой имеется обращение, должен быть ассемблирован в качестве модуля

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

загружаемого адресов. Эти модули связываются в одно целое, и все

модуля ссылки пересчитываются относительно начала образующегося единого модуля

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

Исполнение Внешние ссылки не разрешаются до тех пор, пока внешний вызов не

программы будет выполнен процессором. В этот момент процесс прерывается и необходимый модуль связывается с вызывающей программой

Переместимая загрузка

Недостаток привязки обращений к памяти к конкретным адресам до загрузки заключается в том, что полученный модуль может быть загружен только в одну область основной памяти. Однако когда память совместно используется несколькими программами, нежелательно заранее определять, в какую область памяти должен быть загружен тот или иной модуль. Такое решение лучше принимать в момент загрузки. Следовательно, нам требуется, чтобы загружаемый модуль мог быть размещен в произвольном месте памяти.

Для удовлетворения этого нового требования ассемблер или компилятор генерируют не абсолютные адреса, а адреса относительно некоторой известной точки, такой, как начало программы. Этот метод продемонстрирован на рис. 7.15,в. Началу загружаемого модуля назначается относительный адрес 0, и все прочие ссыл­ки внутри модуля выражаются относительно его начала.

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

Динамическая загрузка времени исполнения

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

Альтернативное решение состоит в том, чтобы отложить вычисление абсолютного адреса до того момента, пока он не потребуется реально во время исполнения. С этой целью модуль загружается в основную память со ссылками в относительном виде (рис. 7.15,в), и только при реальном выполнении команды вычисляется абсолютный адрес. Для того чтобы такой метод не снижал общую производительность, требуется аппаратная поддержка вычисления абсолютного адреса (о чем говорилось в разделе 7.2),

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

Компоновка

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

Редактор связей

Природа компоновки адресов зависит от типа создаваемого модуля и времени компоновки (см. табл. 7.2,6). Если требуется создание перемещаемого модуля, то компоновка обычно выполняется следующим образом. Каждый скомпилированный объектный модуль создается со ссылками относительно начала объектного модуля. Все эти модули объединяются в единый перемещаемый загружаемый модуль, в котором все ссылки даны относительно начала единого модуля. Такой модуль можно использовать для переносимой загрузки или динамической загрузки времени исполнения.

Компоновщик, который создает перемещаемый загрузочный модуль, часто называется редактором связей (на рис. 7.16 проиллюстрирована его работа).

Динамический компоновщик

Как и в случае загрузки, некоторые функции компоновки могут быть отложены. Метод, при котором компоновка отдельных внешних модулей откладывается на время после создания загружаемого модуля, называется динамической компоновкой (dynamic linking). Таким образом, загружаемый модуль содержит неразрешенные обращения к другим программам, которые могут быть разрешены либо в процессе загрузки, либо во время работы программы.

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

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

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

  • Независимым производителям программного обеспечения становится легче расширять функциональность таких распространенных операционных систем, как, например, Linux. Разработчик может предоставить новую полезную функцию для ряда приложений в виде динамически компонуемого модуля.