Схема работы статического загрузчика.
При своём вызове статический загрузчик (в дальнейшем – просто загрузчик) получает в качестве параметра имя файла загрузочного модуля. Работа начинается с чтения паспорта загрузочного модуля и определения объёма памяти, необходимого для счёта программы. Обычно это сумма длин всех сегментов программы, однако иногда в процессе счёта программа может запрашивать у операционной системы дополнительные сегменты (или, как говорят, блоки) памяти. Эти блоки чаще всего используются для размещения динамических переменных, так как заранее неизвестно, сколько памяти потребуется для хранения этих динамических переменных. Обычно в паспорте можно указать минимальный объём дополнительной памяти, без наличия которого нельзя запускать программу на счёт, и максимальный объём такой памяти, который может запросить программа.[53]
Итак, если на компьютере нет необходимого объёма свободной памяти, то загрузчик выдаёт аварийную диагностику и не запускает программу на счёт. В противном случае из загрузочного модуля читаются и размещаются в памяти все сегменты этого модуля,[54] для каждого сегмента, таким образом, определяется адрес его начала в оперативной памяти.
Затем загрузчик просматривает паспорт загрузочного модуля и заполняет в сегментах все поля, которые ещё не имели необходимых значений. В нашем примере для загрузочного модуля p.exe это поля с именами Data и Code в сегменте команд (см. рис. 10.2). В эти поля загрузчик записывает соответственно адреса начал сегментов данных и кода, делённые на 16. На этом настройка программы на конкретное месторасположение в оперативной памяти заканчивается.
Далее загрузчик, анализируя паспорт, определяет тот сегмент, который будет начальным сегментом стека программы (как мы знаем, этот сегмент имеет параметр Stack в директиве начала сегмента). Адрес начала этого сегмента, делённый на 16, записывается в сегментный регистр SS, а длина этого сегмента – в регистр вершины стека SP. Таким образом, стек программы готов к работе.[55]
И, наконец, последним действием загрузчик производит дальний абсолютный переход с возвратом на начала загруженной программы, например, по команде
call Code:Start
Здесь Code – адрес начала головного кодового сегмента, а Start – входная точка программы, с которой начинается её выполнение (эта информация, как уже говорилось, содержится в паспорте загрузочного модуля). Далее начинается собственно выполнение загруженной программы.
Как можно догадаться из описания схемы работы загрузчика, макрокоманда finish должна, в конечном счете, возвратить управление загрузчику, чтобы он освободил занимаемую программой память и подготовился к загрузке следующей программы. Такой же возврат к загрузчику должен производиться и при аварийном завершении программы, например, при делении на ноль. Как Вы можете догадаться, это должна делать процедура-обработчик соответствующего прерывания. В нашем курсе мы не будем более подробно рассматривать весь этот механизм.
В заключение рассмотрения схемы работы загрузчика отметим, что иногда, хотя и редко, требуется в одной служебной программе объединить функции редактора внешних связей и загрузчика. Например, это может понадобиться в том случае, когда некоторая программа получает новый объектный модуль или изменяет один или несколько существующих объектных модулей и тут же хочет загрузить и выполнить изменённую программу. Системная программа, которая объединяет в себе функции редактора внешних связей и загрузчика, называется обычно связывающим загрузчиком. В нашем курсе мы не будем изучать подробности работы связывающего загрузчика.
Итак, мы изучили схему выполнения модульной программы, эта схема включает в себя два этапа: редактирование внешних связей и загрузки программы в оперативную память с настройкой по месту её расположения в этой памяти. Такая схема счёта носит название "статическая загрузка и статическое связывание модулей". Главное достоинство этой схемы выполнения модульной программы состоит в том, что после того, как программа загружена в память и начала выполняться, для её работы не требуется вмешательство системных программ при использовании внешних связей, все статические внешние связи уже установлены, а соответствующие внешние адреса известны и записаны в сегментах.
Поймём теперь, что эта схема выполнения модульной программы со статической загрузкой и статическим связыванием модулей имеет очень серьёзный недостаток. Предположим, что наша достаточно сложная программа состоит из 100 модулей (для простоты будем считать, что в каждом модуле содержится одна из процедур программы). Тогда перед началом работы все эти 100 процедур должны быть размещены в оперативной памяти и связаны между собой и с основной программой (т.е. в нужных местах проставлены адреса этих процедур).
В то же время чаще всего бывает так, что при каждом конкретном счёте программы, в зависимости от введённых данных, на самом деле понадобится вызвать только относительно небольшое количество этих процедур (скажем, 10 из 100). Тогда получается, что для каждого запуска программы необходимы лишь 10 процедур из 100, а остальные только зря занимают место в памяти, обращений к ним не будет. Конечно, для следующего запуска программы (с другими входными данными) могут понадобиться другие 10 процедур из 100, но в целом картина не меняется: каждый раз около 90% памяти не используется!
Исходя из вышесказанного понятно, что на первых ЭВМ, когда оперативной памяти было мало, схема счёта со статическим связыванием и статической загрузкой модулей применялась редко. Первые программисты не могли себе позволить так нерационально использовать дорогую оперативную память, поэтому при счёте модульных программ применялась схема с динамическим связыванием и динамической загрузкой модулей в оперативную память. Однако в дальнейшем, при увеличении объёмов оперативной памяти, и особенно после появления так называемой виртуальной памяти,[56] стала в основном использоваться схема счёта модульных программ со статической загрузкой и связыванием.
Далее отметим, что в настоящее время счёт модульных программ (а подавляющее большинство программ только такие теперь и есть), уже снова чаще всего выполняется с динамическим связыванием и динамической загрузкой модулей. Причина здесь состоит в том, что, несмотря на сильно возросший объём памяти в современных ЭВМ, сложность задач и число реализующих их модулей растёт значительно быстрее, что объём памяти.[57]
Перейдём теперь непосредственно к изучению схемы счёта модульной программы с использованием динамического связывания и динамической загрузки модулей. Обычно та системная программа, которая занимается динамическим связыванием и динамической загрузкой модулей, называется динамическим загрузчиком.
Дата добавления: 2015-10-05; просмотров: 650;