Оверлейная загрузка

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

Третьим способом формирования адреса в программе. называемая относительная адресация, когда адрес получается сложением адресного поля команды и адреса самой этой команды - значения счетчика команд (IP - Instruction Pointer ). Видно, что модуль, в котором используется только такая адресация, можно грузить с любого адреса без всякой перенастройки. Такой код называется позиционно-независимым.

Позиционно-независимые программы очень удобны для загрузки, но, к сожалению, их написание накладывает довольно жесткие ограничения на стиль программирования. Например, нельзя пользоваться статически инициализованными переменными указательного типа. На многих процессорах, например, на Intel 8080/8085 или многих современных RISC-процессорах позиционно-независимый код вообще невозможен - эти процессоры не поддерживают соответствующий режим адресации для данных. Возникают серьезные неудобства при сборке программы из нескольких модулей. Поэтому такой стиль программирования используют только в особых случаях. Например, многие вирусы и драйверы под MS DOS написаны именно таким образом.

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

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

Основная проблема при оверлейной загрузке состоит в следующем: прежде чем ссылаться на оверлейный адрес, мы должны понять, какой из оверлейных модулей в данный момент там находится. Для ссылок на функции это просто: вместо точки входа функции мы вызываем некую процедуру, называемую менеджером перекрытий (overlay manager). Эта процедура знает, какой модуль куда загружен, и при необходимости подкачивает то, что загружено не было. Перед каждой ссылкой на оверлейные данные мы должны выполнять аналогичную процедуру, что намного увеличивает и замедляет программу. Иногда такие действия возлагаются на программиста (MS Windows, Mac OS, иногда - на компилятор (handle pointer в Zortech C/C++ для MS DOS), но чаще всего с оверлейными данными вообще предпочитают не иметь дела. В таком случае оверлейным является только код.

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

Каждый оверлейный модуль может быть как абсолютным, так и перемещаемым. От этого несколько меняется устройство менеджера, но не более того. На архитектурах типа i80x86 можно делать оверлейные модули, каждый из которых адресуется относительно своего значения базового регистра CS и ссылается на данные, статически размещенные в памяти, относительно постоянного значения регистра DS. Такие модули можно загружать в память с любого адреса, может быть, даже вперемежку с данными. Именно так и ведут себя оверлейные менеджеры компиляторов Borland и Zortech.