Предыдущая глава | РАЗДЕЛЬНАЯ ТРАНСЛЯЦИЯ ПРОГРАММЫ | Следующий раздел |
В п. 9.1 отмечалось, что преобразование виртуальной прикладной программы, записанной на языке программирования, в реальную машинную программу выполняет системная обрабатывающая программа, называемая лингвистическим процессором. На самом деле для такого преобразования требуются три лингвистических процессора – транслятор, редактор связей и загрузчик. В принципе, их можно объединить в единую программу, которая, естественно, также будет лингвистическим процессором. Именно так устроены современные среды программирования, позволяющие «выполнять» виртуальные программы. Кроме перечисленных программ они могут включать также текстовый редактор для набора программ, а также отладчик. Вне зависимости от того, используются ли перечисленные лингвистические процессоры и утилиты по отдельности, или в виде модулей среды программирования, общая схема получения и выполнения прикладной программы остается прежней (рис.59). Преобразование файлов, образующих исходную программу, в машинную программу, схематично показано на рис.60.
На первом этапе в диалоге с текстовым редактором программист вводит в память ВС текст исходной программы. Текст исходной программы состоит из одного или нескольких фрагментов исходной программы. В свою очередь фрагмент исходной программы состоит из одного или нескольких текстовых файлов, называемых исходными модулями. Эти исходные модули связываются в единый фрагмент исходной программы с помощью операторов %include. Программист сам определяет, как разбить свою исходную программу на модули и фрагменты. Более того, он может программировать свои исходные фрагменты на разных языках.
Рис. 59. Общая схема создания и выполнения программы
Исходный фрагмент является входными данными для транслятора того языка, на котором написан данный фрагмент исходной программы. В результате одного выполнения транслятора исходный программный модуль преобразуется в объектный программный модуль.
Все полученные до сих пор нами программы были небольшими. При этом каждая исходная программа представляла собой единственный исходный фрагмент, обрабатываемый транслятором ассемблера за одно его выполнение. Получаемый при этом транслятором объектный модуль был нам не нужен, и поэтому мы просили транслятор о выдаче готового загрузочного модуля в виде com-файла. Такая просьба не вызывала затруднений у транслятора, так как единственный объектный модуль программы не сильно отличается от ее загрузочного модуля. Другое дело, если исходная программа состоит из нескольких фрагментов, каждый из которых требует, естественно, своего выполнения транслятора. В этом случае выдача загрузочного модуля транслятором в принципе невозможна по следующим причинам.
Рис. 60. Преобразование файлов программы
При обработке транслятором фрагмента исходной программы частично решается задача получения последовательности машинных команд, отображающих алгоритм исходного фрагмента. Транслятор окончательно записывает коды операций машинных команд, а также проставляет в эти команды номера используемых регистров. Что касается символьных имен, то они обрабатываются транслятором по-разному.
Рассмотрим преобразование адресов транслятором. Адрес – место в ОП, которое займет соответствующий программный объект – команда или данное. Любой язык программирования позволяет программисту использовать в программе не адреса, а их заменители – символьные имена (метки). Основные типы меток: 1) метки операторов; 2) имена переменных; 3) имена процедур (имя процедуры заменяет для программиста адрес, по которому первая команда процедуры находится в ОП).
Все символьные имена в исходном фрагменте делятся на внешние и внутренние. Внутреннее символьное имя определено и используется в том фрагменте исходной программы, который «видим» транслятором в конкретном его выполнении. Такой «видимый» фрагмент исходной программы образует главный исходный модуль (исходный файл), указанный в качестве операнда команды ОС, запускающей транслятор, а также исходные модули, заданные с помощью операторов %include как в главном исходном файле, так и в связанных с ним с помощью операторов %include файлах.
Все внешние метки делятся на входные и выходные. Внешние выходные метки определены внутри данного фрагмента исходной программы, «видимого» транслятором, а используются вне его. Это: 1) имена процедур, входящих в состав данного исходного фрагмента, но которые могут вызываться в других фрагмантах исходной программы; 2) имена переменных, которые определены в данном исходном фрагменте, а используются вне его. Внешние входные метки определены вне данного исходного фрагмента, а используются в нем. Это: 1) имена процедур, не входящих в данный фрагмент программы, но используемых в нем; 2) имена переменных, которые используются в исходном фрагменте, но определены вне его.
При получении объектного модуля вместо внутренних и внешних выходных меток транслятор проставляет в те машинные команды, которые используют соответствующие адреса, или смещения относительно текущего содержимого указателя команд IP, или смещения относительно начала сегмента ОП, в котором находятся соответствующие программные объекты. В первом из этих вариантов расчет смещения относительно начала сегмента памяти производится в момент выполнения данной машинной команды на ЦП. Что касается внешних входных меток, то обработать их транслятор не может. Он ничего не знает о размещении соответствующих программных объектов в памяти, так как имеет в своем распоряжении тот фрагмент исходной программы, в котором этих объектов нет. Дальнейшее преобразование программы выполняет системная программа, называемая редактором связей.
Редактор связей (компоновщик) связывает (“сшивает”) все объектные модули программы в единый загрузочный модуль. При получении загрузочного модуля редактор связей записывает объектные модули один за другим в ОП. Поэтому он “знает”, где расположен в памяти каждый программный объект. Следовательно, он может заменить все внешние метки, оставленные транслятором, на соответствующие численные адреса (адреса-сегменты и адреса-смещения). В конце своей работы редактор связей записывает загрузочный модуль в файл на магнитном диске и выдает программисту сообщение, называемое картой памяти. Эта карта описывает распределение памяти машинной программе.
В операционной системе DOS допускается существование двух типов загрузочных модулей – типа com и типа exe (тип модуля совпадает с расширением имени файла, содержащего загрузочный модуль). Загрузочный модуль типа com применяется для создания небольших машинных программ (объемом не более 64K), а типа exe – для создания любых программ. Основное различие между этими загрузочными модулями заключается в том, что com–файл содержит готовую машинную программу, не требующую “доводки” при загрузке в память, а exe–файл содержит “заготовку” машинной программы, а также служебную информацию, используемую загрузчиком для настройки программы.
Как говорилось в п.9.1, пользователь осуществляет запуск прикладных и системных обрабатывающих программ с помощью интерпретатора команд ОС. (ИК для DOS существует в виде отдельного загрузочного модуля Command.com.) В диалоге с ним пользователь сообщает имя загрузочного модуля требуемой программы. В ответ интерпретатор команд вызывает системную программу – загрузчик, передав ему имя загрузочного модуля.
Загрузчик переписывает загрузочный модуль из ВП в ОП и, возможно, настраивает некоторые адреса в полях машинных команд. Настройка – изменение адреса в зависимости от фактического расположения машинной программы в ОП. Нетрудно предположить, что при загрузке exe-файла настройка производится, а для com-файла – не производится.
Предыдущая глава | В начало | Следующий раздел |