русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Текст лекции


Дата добавления: 2013-12-23; просмотров: 1045; Нарушение авторских прав


Ключевые вопросы

Лекция № 6. Загрузка программ. Часть 2

Продолжительность:2 часа (90 мин.)

· Основные понятия и определения: операционная система (ОС), программное обеспечение (ПО), системное программное обеспечение.

· Относительная загрузка

· Базовая адресация

· Позиционно-независимый код

· Оверлеи

· Сборка программ

 

14.2.1 Относительная загрузка — до 15 мин.

 

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

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

Сложность здесь в том, что если абсолютные адресные поля можно най­ти анализом кодов команд (деассемблированием), то значение в адрес­ный регистр может загружаться задолго до собственно адресации, при­чем, как мы видели в примерах кода для процессора SPARC, формирование значения регистра может происходить и по частям. Без помощи программиста или компилятора (в этой главе мы не будем различать написанный на ассемблере или компилированный код, а того, кто генерировал код, будем называть программистом) решить вопрос о том, какая из команд загружает в регистр скалярное значение, а какая — будущий адрес или часть адреса, невозможно. Та же проблема возникает в случае, если мы используем в качестве указателя ячейку статически инициализованных данных (пример 14.1).



 

Пример 14.1. Примеры статически инициализованных указателей в С

int buf[20], *bufptr=buf;

char * message="No message defined yet\n";

void do_nothing hook(int);

void (*hook)(int)=do_nothing_hook;

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

Пример 14.2. Реализация косвенного перехода по адресу dst_seg:dst_offs

push dst_seg ; Это и будет ссылкой на абсолютный адрес

push dst offs

retf

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

Ассемблер при каждой ссылке на такой символ генерирует не только "заготовку" адреса в коде, но и запись в таблице перемещений (relocation table). Эта запись хранит место ссылки на такой символ в коде или данных. Если в ссылке используется только часть адреса, как в командах sethi НО, Ihi(addr) процессора SPARC, или move ax, segment addr процессора 8086, мы запоминаем и этот факт.

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

Файл, содержащий таблицу перемещений, гораздо сложнее абсолютного загружаемого модуля и носит название относительного или перемешаемого загрузочного модуля. Именно такой формат имеют ехе-файлы в системе MS DOS (пример 14.2).Пример 14.2. Заголовок ЕХЕ-файла MS DOS. Цитируется по WINT.H из поставки

14.2.2 Базовая адресация —до 15 мин.

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

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

Именно так происходит загрузка corn-файлов в системе MS DOS. Система выделяет свободную память, настраивает для программы базовые регистры DS и CS, которые почему-то называются сегментными, и передает управле­ние на стартовый адрес. Ничего больше делать не надо.

14.2.3 Позиционно-независимый код —до 15 мин.

За всеми этими разговорами мы чуть было не забыли о третьем способе формирования адреса в программе. Это относительная адресация, когда ад­рес получается сложением адресного поян команды и адреса самой этой ко­манды — значения счетчика команд. Код, в котором используется только такая адресация, можно загружать с любого адреса без всякой перенастрой­ки. Такой код называется позиционно-независимый {position-independent).

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

К тому же, на многих процессорах, например, на Intel 80S0/8085 или многих современных RISC-процессорах, описанная выше реализация позиционно-независимого кода вообще невозможна, так как эти процессоры не поддер­живают соответствующий режим адресации для данных. На процессорах гарвардской архитектуры адресовать данные относительно счетчика команд вообще невозможно — команды находятся в другом адресном пространстве.

Поэтому такой стиль программирования используют только в особых случа­ях. Например, многие вирусы для MS DOS и драйверы для RT-11 написаны именно таким образом.

Компиляторы современных систем семейства UNIX — GNU С или стандартный С-компилятор UNIX SVR4 имеют ключ -f PIC (Position-Independent Code}. Впрочем, код, порождаемый при использовании этого ключа, не является позиционно-независимым в указанном выше смысле: этот код все-таки содержит перемещаемые адресные ссылки. Задача состоит не в том, чтобы избавиться от таких ссылок полностью, а лишь в том, чтобы собрать все эти ссылки в од­ном месте и разместить их, по возможности, отдельно от кода. Какая от этого польза, мы поймем несколько позже, а сейчас обсудим технические приемы, используемые для решения этой задачи.

 

14.2.4 Оверлеи —до 15 мин.

 

Еще более интересный способ загрузки программы — это оверлейная загруз­ка (over-lay, лежащий сверху) или, как это называли в старой русскоязычной литературе, перекрытие. Смысл оверлея состоит в том, чтобы не загружать программу в память целиком, а разбить ее на несколько модулей и поме­щать их в память по мере необходимости. При этом на одни и те же адреса в различные моменты времени будут отображены разные модули. Отсюда и название потребность в таком способе загрузки появляется, если у нас виртуальное адресное пространство мало, например I Мбайт или даже всего 64 Кбайт (на некоторых машинах с RT-1I бывало и по 48 Кбайт, и многие полезные программы нормально работали!), а программа относительно велика. На со­временных 32-разрядных системах виртуальное адресное пространство обычно измеряется гигабайтами, и большинству программ этого хватает, проблемы с нехваткой можно решать совсем другими способами. Тем не менее, существуют различные системы, даже и 32-разрядные, в которых нет устройства управления памятью, и размер виртуальной памяти не может превышать объема микросхем ОЗУ, установленных на плате. Пример такой системы — упоминавшийся выше транспьютер. Важно подчеркнуть, что, несмотря на определенное сходство между задача­ми, решаемыми механизмом перекрытий и виртуальной адресацией, одно ни в коем случае не является разновидностью другого. При виртуальной ад­ресации мы решаем задачу отображения большого адресного пространства на ограниченную оперативную память. При использовании оверлея мы ре­шаем задачу отображения большого количества объектов в ограниченное адресное пространство. Основная проблема при оверлейной загрузке состоит в следующем: прежде чем ссылаться на оверлейный адрес, мы должны понять, какой из оверлей­ных модулей в данный момент там находится. Для ссылок на функции это просто: вместо точки входа функции мы вызываем некую процедуру, назы­ваемую менеджером перекрытий (overlay manager). Эта процедура знает, какой модуль куда загружен, и при необходимости "подкачивает" то, что загружено не было. Перед каждой ссылкой на оверлейные данные мы должны выполнять аналогичную процедуру, что намного увеличивает и замедляет Программу. Иногда такие действия возлагаются на программиста (Winl6, Mac OS до версии 10 — подробнее управление памятью в этих системах J., иногда — на компилятор (handle pointer в Zortech " C/C++ для MS DOS), но чаше всего с оверлейными данными вообще предпочитают не иметь дела. В таком случае оверлейным является только код. В старых учебниках по программированию и руководствах по операционным системам уделялось много внимания тому, как распределять процедуры между оверлейными модулями. Действительно, загрузка модуля с диска :представляет собой довольно длительный процесс, поэтому хотелось бы ми­нимизировать ее. Для этого нужно, чтобы каждый оверлейный модуль был как можно более самодостаточным. Если это невозможно, стараются выне­сти процедуры, на которые ссылаются из нескольких оверлеев, в отдельный модуль, называемый резидентной частью или резидентным ядром. Это мо­дуль, который всегда находится в памяти и не разделяет свои адреса ни с каким другим оверлеем. Естественно, оверлейный менеджер должен быть частью этого ядра. Каждый оверлейный модуль может быть как абсолютным, так и перемещае­мым. От этого несколько меняется устройство менеджера, по не более того, fin Архитектурах типа i80x86 можно делать оверлейные модули, каждый из ко-;торых адресуется относительно значения базового регистра cs и ссылается на данные, статически размещенные в памяти, относительно постоянного значения регистра ds. Такие модули можно загружать в память с любого адреса, может быть, даже вперемежку с данными. Именно так и ведут себя оверлейные менеджеры компиляторов Borland и Zortech.'3.7.

 

14.2.5 Сборка программ —до 15 мин.

В предыдущем разделе шла речь о типах исполняемых модулей,

но не гово­рилось ни слова о том, каким образом эти модули получаются. Вообще го­воря, способ создания загружаемого модуля различен в различных ОС, но в настоящее время во всех широко распространенных системах этот процесс выглядит примерно одинаково. Это связано, прежде всего, с тем, что эти системы используют одни и те же языки программирования и правила меж­модульного взаимодействия, в которых явно или неявно определяют логику раздельной компиляции и сборки.В большинстве современных языков программирования программа состоит из отдельных слабо связанных модулей. Как правило, каждому такому моду­лю соответствует отдельный файл исходного текста. Эти файлы независимо обрабатываются языковым процессором (компилятором), и для каждого из них генерируется отдельный файл, называемый объектным модулем. Затем запускается программа, называемая редактором связей, компоновщиком или линкером (linker ~ тот, кто связывает), которая формирует из заданных объ­ектных модулей цельную программу.

Объектный модуль отчасти похож по структуре на перемещаемый загрузоч­ный модуль. Дело в том, что сборку программы из нескольких модулей можно уподобить загрузке в память нескольких программ. При этом возни­кает та же задача перенастройки адресных ссылок, что и при загрузке отно­сительного загрузочного файла. Поэтому объектный модуль дол­жен в той или иной форме содержать таблицу перемещений. Можно, конечно, потребовать, чтобы весь модуль был позиционно-независимым, но это, как говорилось выше, накладывает очень жесткие ограничения на стиль программирования, а на многих процессорах (например Intel 8085) просто невозможно,

Кроме ссылок на собственные метки, объектный модуль имеет право ссы­латься на символы, определенные в других модулях. Типичный пример та­кой ссылки — обращение к функции, которая определена в другом файле исходного текста.

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

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

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

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

Таблицу объектов, определенных в этом модуле, на которые можно ссылаться из других модулей. В некоторых случаях ее называют списком экс­
порта. Иногда таблицы экспорта и импорта объединяют и называют все
это таблицей глобальных символов. В этом случае для каждого символа
приходится указывать, определен он в данном модуле или нет, а если он­
определен, то как. Различную служебную информацию, такую, как имя модуля, программу, которая его создала (например, строка "gcc compiled").

Как правило, код и данные разбиты на именованные секции. В masm/iasm (MASM — Microsoft Assembler, Tasm — Turbo Assembler) такие секции назы­ваются сегментами, в DEC'obckhx и UNIX'oBbix ассемблерах — программны­ми секциями {psect). В готовой программе весь код или данные, описанный в разных модулях, но принадлежащий к одной секции, собирается вместе. Например, о системах семейства Unix программы, написанные на языке С, состоят из минимум трех программных секций:

.text — исполняемый код (современные компиляторы иногда помещают в эту секцию и данные, описанные как const);

.data — статически инициализированные данные;

.bss — неинициализированные данные.

 



<== предыдущая лекция | следующая лекция ==>
Текст лекции | Текст лекции


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.004 сек.