Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
flor_apparato-orientirovnnoe_prog.doc
Скачиваний:
89
Добавлен:
15.06.2014
Размер:
926.72 Кб
Скачать

5. Взаимосвязи программных единиц

5.1. Многомодульная разработка программ

Только самые простые программы изготавливаются из одного файла исходной программы. Более сложные программы в процессе разработки разбиваются на множество исходных модулей, как наиболее обще называют отдельные файлы исходной программы. Такое разбиение помогает справиться с общей сложностью программы и выполнять разработку по частям: вначале написать и отладить только часть программы, затем аналогичное выполнить с другой частью и т.д. Все современные языки предоставляют средства для такого исходного разбиения программы и указания связи получаемых частей. (Заметим, что Паскаль - в отличие от Турбо Паскаля и его модификаций - не позволял разбивать программу на отдельно транслируемые модули.)

Проблема связей программ, которые в совокупности после соединения составляют единую выполняемую программу, особенно четко просматривается именно с позиций программирования на ассемблере и машинных структур объектных файлов. Большинство современных систем программирования работают по принципу компиляция-компоновка-выполнение. Это значит, что непосредственным результатом обработки файла исходной программы служит файл специального служебного формата, называемый объектным модулем (или объектным файлом). В типовых системах разработки программ для операционных систем Windows, OS/2, MS-DOS имена объектных файлов традиционно имеют расширение OBJ, а в Unix-системах - расширение имени, задаваемое единственной буквой o.

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

Из языков высокого уровня, подобных Алголу, читателю должно быть известно, что никакие информационные объекты нельзя использовать без их описания как данных. Такое описание задается специальными конструкциями, которые неявно задают размер этих объектов в памяти компьютера и место, занимаемое ими в этой памяти. На ассемблере подобные описания, как мы уже видели, представляют собой конструкции на основе директив RESx и Dx, где символ x обобщенно обозначает одну из букв B, W D. Именно эти директивы явно задают место в памяти компьютера для данных.

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

Такая запись на языке ассемблера NASM делается в директиве EXTERN. Последняя имеет вид

EXTERN имя_внешнего_имени

или, в общем случае,

EXTERN перечисление_внешних_имен

где перечисление_внешних_имен представляет собой перечисление через запятую набора различных внешних имен.

В свою очередь те имена, которые предполагается использовать для доступа к данным и подпрограммам из другого исходного модуля, необходимо дополнительно сопровождать соответствующей информацией для системы разработки. Эта информация на ассемблере NASM задается в директиве GLOBAL, с которой читатель уже частично знаком. Именно, все имена, которые будут использоваться как внешние (или которые только могут использоваться как внешние), следует включить в состав перечисления после служебного слова GLOBAL. Имена, перечисленные в одной из директив GLOBAL, называются глобальными. Существенной особенностью таких имен является то, что они попадают в объектный модуль, сохраняя в нем свое обозначение. В пояснение заметим, что все остальные (не глобальные) имена не сохраняются в объектном модуле, а превращаются в нем лишь в числовые смещения от начала соответствующего сегмента. Те имена исходного файла, которые, согласно сделанным пояснениям, программист должен объявить глобальными или внешними, могут быть произвольно сгруппированы в одном, нескольких или во многих директивах (соответственно, GLOBAL или EXTERN).

Рассмотрим для дальнейшего изучения описанных средств демонстрационную программу, которая образуется двумя исходными файлами prim5.asm и prim5a.asm на ассемблере. Текст этих программ приведен в листингах 5.1.1 и 5.1.2.

; Многомодульные программы (использование GLOBAL и EXTERN)

; Главная программа - файл prim5.asm

GLOBAL _start

GLOBAL sla, slb, sum

EXTERN funa

SEGMENT .code

_start: mov DWORD [sla], 27

mov WORD [slb], 19

call funa

mov eax, [sum]

; сюда следует вставить преобразование

; в десятичную систему и вывод результата на экран

mov esi,10 ; base of position digit system

mov ecx, 0 ; reset digit counter

pov: mov edx, 0 ; null into left part of devident

div esi ; divide for next digit = rest

add dl, '0'

push edx

inc ecx ; step into counter

cmp eax, 0

jne pov

mov [cnt], ecx

mov ebx, numtxt

izv: pop edx

mov byte [ebx],dl

inc ebx

loop izv

mov eax,4 ; N function=write

mov ebx,1 ; N handle=1 (stdout)

mov ecx, numtxt ; address of text

mov edx,[cnt] ; number of byte

int 80h

mov eax, 1

int 80h ; function=exit

SEGMENT .data

sla DD 0

slb DW 0

sum DD 0

cnt dd 0

SEGMENT .bss

numtxt times 10 db 0

Листинг 5.1.1. Файла prim5.asm многомодульной программы для Linux

; Многомодульные программы (использование GLOBAL и EXTERN)

; Дополнительный файл Prima.asm:

SEGMENT .data

EXTERN sla, slb, sum

GLOBAL funa

SEGMENT .code ; may be other segment, for example, .codes

; procedure funa

funa:

push eax

push ebx

mov eax,[sla]

mov ebx, 0

mov bx, [slb]

add eax, ebx

mov [sum], eax

pop ebx

pop eax

ret

; end procedure funa

Листинг 5.1.2. Файла prim5a.asm многомодульной программы для Linux

В первом модуле этой программы, названном для определенности prim5.asm, определены именованные области данных sla, slb и sum. Причем в предположении их использования в другом модуле эти имена объявлены директивой GLOBAL как глобальные. В этом же исходном модуле используется имя funa, которому в программе исходного модуля не соответствует никакой метки и вообще никакого конкретного места внутри программы. Если бы в программу исходного модуля не была вставлена директива EXTERN funa, то при ее трансляции появилась бы ошибка, что имя funa не определено. Последняя же директива поясняет компилятору, что программист совсем не ошибся, а место в программе, обозначаемое меткой funa, будет находиться где-то в другом исходном модуле, и окончательное согласование ее использования выполняет уже компоновщик.

Во втором модуле программы, названном для определенности prim5a.asm, имя funa определено как метка (начинающая по существу процедуру). Чтобы сделать это имя доступным для использования компоновщику, в этом втором модуле имя funa указано в директиве GLOBAL. Если такое указание не сделано в этом модуле, то при его компиляции ошибка не появится - для глобального имени все равно, будет оно использоваться извне или нет. Но при выполнении компоновки обоих получающихся объектных модулей появится сообщение, что в программе модуля prim5.o сделана ссылка на несуществующее внешнее имя.

Кроме того, в исходном модуле prim5a.asm используются имена областей данных sla, slb и sum, которым в этом модуле не отведено никакое место в памяти. В то же время этот модуль содержит директиву EXTERN, в которой указанные имена перечисляются, обозначаясь как внешние. Если директиву EXTERN опустить, ошибка появится уже при компиляции исходного модуля prim5a.asm, так как компилятор имеет все основания полагать, что программист забыл о каком-либо определении или объявлении упоминаемых имен.

Заметим, что в языках высокого уровня делается терминологическое отличие определения имен и объявления имен. Более ясно эта проблема смотрится на ассемблере. При определении имени ему оно соотносится с конкретным местом в памяти. Программист может явно (буквально, «пальцем») показать, какие данные или команды идут перед местом, обозначенным этим именем, а какие - после него (в действительности имени соотносится совершенно конкретное смещение в сегменте данных или команд, соответственно). При объявлении имени (директивой EXTERN в NASM) лишь объявляется, что где-то в другом участке результирующей программы будет определено место, которое будет обозначаться этим именем. Таким образом, объявление имени содержит скорее намерение в дальнейшем определить имя, чем его действительное определение (другое дело, что модуль, в котором это имя определено, может быть изготовлен по времени гораздо раньше - компилятор не знает и не может знать об этом).

Существенной деталью использования директивы GLOBAL является необходимость ее указания до места использования перечисленных в ней имен (иначе возникают ошибки на стадии компоновки). (Возможно, эта особенность будет устранена в следующих версиях NASM, так как рациональной причины оставлять эту особенность не видно.) Для ассемблеров MASM и TASM не следует размещать директиву EXTRN в сегменте, отличном от того, в котором действительно будут определены перечисленные в ней имена. В старых версиях операционных систем такое требование было связано с устремлением обеспечить, по возможности, максимальную степень контроля над именами в ходе разработки. (Сейчас при одном сегменте изменяемых данных и одном сегменте машинных команд большого смысла в подобном требовании нет.)

Если порознь выполнить компиляцию исходных модулей, приведенных в листингах 5.1.1 и 5.1.2, используя рекомендованные выше соглашения, то получатся объектные модули prim5.o и prim5a.o. Для связывания (компоновки) их в единую выполняемую программу следует использовать вызов компоновщика в виде команды

ld -o prim5.exe prim5.o prim5a.o

Автоматизировать процесс разработки данной программы можно с помощью командного файла, текст которого приведен в листинге 5.1.3.

nasm -f elf $1.asm -l $1.lst

nasm -f elf $2.asm -l $2.lst

ld -o $1.exe $1.o $2.o

Листинг 5.1.3. Командный файла nasml2 для изготовления программы

из двух исходных моделей в Linux

Применение такого командного файла особенно удобно при многократных ошибках в составлении программы и необходимости многократного запуска системы разработки. Если данный командный файл назван nasml2, то его использование для данной задачи заключается в вызове с помощью командной строки

nasml2 prim5 prim5a

При выполнении командного файла первый и второй аргумент этого вызова используются вместо формальных аргументов $1 и $2 командного файла. Этот же командный файл можно использовать для разработки любой программы, состоящей из двух исходных модулей.

В ассемблерах MASM и TASM вместо директивы EXTERN используется директива EXTRN. Кроме отсутствия в ее названии одной буквы (для сокращения) в директиве EXTRN необходимо для каждого имени указывать его атрибут. Эти атрибуты задаются служебными словами BYTE, WORD, DWORD, NEAR, FAR и должны следовать за именем через разделяющее двоеточие. Для аналога приведенного примера в указанных ассемблерах следует записать

EXTRN funa: NEAR

EXTRN sla: DWORD , slb:BYTE, sum: DWORD

Таким образом, в указанных ассемблерах внутри директивы EXTRN задаются не только внешние имена, но и характеристики размера памяти, связанной с каждым из этих имен.

Вместо директивы GLOBAL в указанных ассемблерах имеется соответствующая директива PUBLIC, применение которой ничем кроме ключевого слова не отличается от уже рассмотренной.

Соседние файлы в предмете Системное программное обеспечение