Chapter_10 (1110562), страница 6
Текст из файла (страница 6)
Таким образом, загруженная насчёт программа пользователя после своего успешного или аварийного завершения должна была возвратиться вэту управляющую программу, которая обычно имела имя COMMAND.COM.15В то же время чаще всего бывает так, что при каждом конкретном запуске программы на счёт, взависимости от введённых данных, на самом деле понадобится вызвать только относительно небольшое количество этих процедур (скажем, 10 из 100). Тогда получается, что для каждого запуска программы необходимы лишь 10 процедур из 100, а остальные только зря занимают место в памяти, обращений к ним не будет. Конечно, для следующего запуска программы (с другими входными данными) могут понадобиться другие 10 процедур из 100, но в целом эта безрадостная картина не меняется: каждый раз во время счёта программы около 90% оперативной памяти для её хранения не используется!Исходя из вышесказанного понятно, что на первых ЭВМ, когда оперативной памяти было мало,схема счёта со статическим связыванием и статической загрузкой модулей применялась редко.
Первые программисты не могли себе позволить так нерационально использовать дорогую оперативнуюпамять, поэтому при счёте модульных программ применялась схема с динамическим связываниеми динамической загрузкой модулей в оперативную память (с этой схемой мы будем знакомитьсядалее).
Однако в дальнейшем, при увеличении объёмов оперативной памяти, и особенно после появления так называемой виртуальной памяти, 1 стала в основном использоваться схема счёта модульных программ со статической загрузкой и связыванием, как более простая.Далее отметим, что в настоящее время счёт модульных программ (а подавляющее большинство"больших" программ только такие теперь и есть), снова чаще всего выполняется с динамическимсвязыванием и динамической загрузкой модулей. Причина здесь состоит в том, что, несмотря насильно возросший объём памяти в современных ЭВМ, сложность решаемых задач и число реализующих их модулей растёт быстрее, что объём памяти.Второй серьёзный недостаток схемы счёта модульной программы со статическим связыванием истатической загрузкой проявляется при так называемом мультипрограммном режиме работы ЭВМ(отметим, что в настоящее время большинство ЭВМ работают именно в этом режиме). С мультипрограммным режимом работы мы будем детально знакомиться далее, пока лишь отметим, что в этомрежиме в памяти ЭВМ могут одновременно находиться несколько независимых друг от друга и готовых к счёту программ разных пользователей.
И вот оказывается, что во многих программах частоприходится использовать одинаковые программные модули. Эти модули выполняют функции, безкоторых не может обойтись большинство программ: организация диалогового интерфейса с пользователями (всевозможные окна, меню, формы для ввода данных и т.д.), работа с файлами, работа сдинамическими переменными и многое другое. 2 При схеме счёта со статическим связыванием и статической загрузкой приходится все эти общие для многих программ модули включать на этапе редактирования связей в состав каждой такой программы и хранить в каждом загрузочном модуле.Схема счёта модульной программы с динамической загрузкой и динамическим связыванием модулей призвана решить и эту проблему.Перейдём теперь непосредственно к изучению схемы счёта модульной программы с использованием динамического связывания и динамической загрузки модулей. Как уже говорилось, обычно тасистемная программа, которая занимается динамическим связыванием и динамической загрузкоймодулей, называется динамическим загрузчиком.10.4.
Схема работы динамического загрузчикаИтак, пусть наша программа состоит из головного модуля (основной программы) и какого-точисла процедур и функций, располагающихся в остальных модулях.Суть работы динамического загрузчика состоит в следующем. Сначала он размещает в памятине всю программу целиком, как статический загрузчик, а только её основную часть (головной модуль программы). Все остальные модули, содержащие процедуры и функции, загружаются в оперативную память по мере необходимости, когда к этим процедурам и функциям будет реальное обра-1Виртуальная память позволяет, в частности, использовать в программе объём памяти, превышающий физический объём памяти компьютера. Например, при физической памяти 220 байт (как в нашей младшей модели) можно использовать под (одновременное!) размещение сегментов программы, например, 224 байт.
Виртуальную память студенты факультета вычислительной математики и кибернетики МГУ изучают в курсе третьего семестра "Системное программное обеспечение".2Это так называемая проблема повторного использования (reuse) программного обеспечения: разработанные для одной задачи программные модули часто можно использовать и в других задачах.16щение из головного модуля или других (уже загруженных) модулей программы. Иногда это называется загрузкой по требованию.Отметим здесь важную особенность такой загрузки по требованию.
После размещения головного модуля в оперативной памяти динамический загрузчик не проверяет, что все другие модули, которые могут вызываться в процессе работы программы, на самом деле существуют (на это остаётся только надеяться изо всех сил ☺). 1 Естественно, что, если какой-нибудь модуль не будет найден, когда понадобится его вызвать, то будет зафиксирована ошибка времени выполнения программы. Отметим, что это один из недостатков такой схемы счёта модульной программы. Впрочем, если посмотреть с другой стороны, то возможность выполнять программы, не все модули которых ещёна самом деле написаны, позволяет начать отладку большой программы на самых ранних стадиях еёреализации, что, конечно, является одним из достоинств этой схемы выполнения.Надо отметить, что динамические загрузчики в современных ЭВМ достаточно сложны и, к томуже, сильно различаются в разных системах программирования, поэтому мы рассмотрим только упрощённую схему работы такого загрузчика.
Эта схема будет похожа на схему работы динамическихзагрузчиков в ЭВМ второго-третьего поколений 70-х годов прошлого века.Сначала разберёмся с редактированием внешних связей при динамической загрузке модулей(это и называется динамическим связыванием модулей).
Работу динамического загрузчика будемрассматривать на примере программы, головной модуль которой может вызывать три внешних процедуры с именами A,Beta и C12. Ниже приведён фрагмент сегмента кода этого головного модуляна Ассемблере:; Фрагмент головного модуля с точкой входаCode segmentassume cs:Code,ds:Data,ss:StackStart:movax,Datamovds,ax. . .extrn A:farcall A. .
.extrn Beta:farcall Beta. . .extrn C12:farcall C12. . .finishCode endsendStart; головной модульПусть для простоты внешние процедуры с именами A,Beta и C12 расположены каждая в своёмотдельном модуле, где эти имена, естественно, объявлены общедоступными (public) и имеют типдальних меток (far). При своём вызове динамический загрузчик получает в качестве параметра имяголовного объектного модуля, по существу это первый параметр редактора внешних связей (загрузочного модуля у нас нет, и не будет).
Сначала динамический загрузчик размещает в оперативнойпамяти все сегменты головного модуля и начинает настройку его внешних адресов. Для этих целейон строит в оперативной памяти две вспомогательные таблицы: таблицу внешних имён (ТВИ) итаблицу внешних адресов (ТВА), пусть каждая из этих таблиц располагается в своём отдельномсегменте памяти.В таблицу внешних имён заносятся все внешние имена выполняемой программы (в начале работы у нас это имена внешних процедур головного модуля A,Beta и C12).
Каждое имя будем представлять в виде текстовой строки, заканчивающейся, как это часто делается, символом с номером1В современных технологиях программирования проверка того, что существуют все модули, вызов которых возможен при счёте программы, может оказаться весьма трудоёмкой операцией. Дело в том, что эти модули могут, в принципе, находится где угодно, например, в сетевой (удалённой) библиотеке объектных модулей на другом конце Земли. В качестве примера для продвинутых читателей можно сослаться, например, на такназываемую COM технологию программирования [19].17ноль в алфавите (будем обозначать этот символ \0, как это принято в языке С). Ссылка на имя – этосмещение начала этого имени от начала ТВИ.
В двух первых байтах ТВИ хранится ссылка на началосвободного места в этой таблице (номер первого свободного байта). На рис. 10.3 приведён вид ТВИпосле обработки головного модуля, сейчас в этой таблице три имени: A,Beta и C12 (в каждойстроке таблицы, кроме заголовка, мы для удобства чтения разместили по четыре символа).02610ТВИ segmentFree=13'A'\0'B''t''a'\0'1''2'\0'e''C'Рис. 10.3. Вид таблицы внешних имён послезагрузки головного модуля.Другая таблица динамического загрузчика, таблица внешних адресов, состоит из строк, каждаястрока содержит пять полей. Первое поле имеет длину четыре байта, в нём динамический загрузчикразмещает команду близкого абсолютного перехода jmp LoadGo .
Это переход на начало некоторой служебной процедуры динамического загрузчика. Мы назвали эту служебную процедуруименем LoadGo, что будет хорошо отражать её назначение – загрузить внешнюю процедуру в оперативную память и перейти на выполнение этой процедуры. Отметим, что сама процедура LoadGoзагружается в память вместе с головным модулем до начала счёта и статически связана с этим модулем.Во втором поле (назовём его именем Offset) длиной 2 байта находится адрес (смещение) загруженной внешней процедуры на специальном рабочем поле, о котором мы расскажем немногониже.
До первого обращения к внешней процедуре в это поле динамический загрузчик записываетконстанту 0FFFFh=-1, что является признаком отсутствия данной процедуры на рабочем поле. Втретьем поле тоже длиной в два байта расположена ссылка на имя этой внешней процедуры в таблице внешних имён. В четвёртом поле длиной в два байта с именем Length будет храниться длинавнешней процедуры (пока там значение ноль). И, наконец, пятое поле, тоже длиной в 2 байта, содержит различную служебную информацию (флаги режимов работы) для динамического загрузчика,о чём мы также немного поговорим далее. Таким образом, каждая строка таблицы внешних имёнописывает одну внешнюю процедуру и имеет длину 12 байт.
В заголовке (первых двух байтах) ТВАсодержится ссылку на начало свободного места в этой таблице. Таким образом, перед началом счётапрограммы ТВА будет иметь вид, показанный на рис. 10.4. Заметим, что таблицы ТВИ и ТВА будуттолько расти сверху-вниз, строки из них никогда не будут удаляться.ТВА segment2142638Free=38Jmp LoadGoJmp LoadGoJmp LoadGo0FFFFh0FFFFh0FFFFh2 ('A')4 ('Beta')9 ('C12')000000000000000FlagsFlagsFlagsРис. 10.4.
Вид таблицы внешних адресов после загрузки головногомодуля.Каждая команда дальнего вызова внешней процедуры в головном модуле заменяется динамическим загрузчиком на команду дальнего перехода с возвратом на соответствующую строку ТВА. Например, команда call Beta заменяется на команду call ТВА:14 , а команда в головном модуле call C12 заменяется на команду call ТВА:26 .Проследим работу нашей программы.