Системное программирование

Автор: Пользователь скрыл имя, 10 Марта 2013 в 15:44, курс лекций

Описание работы

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

Работа содержит 1 файл

Системное программирование 15 лекций.doc

— 946.00 Кб (Скачать)

u программа, написанная в виде одного модуля, может оказаться слишком большой для ассемблирования;

u отдельные части программы могут быть написаны разными группами программистов, ассемблирующих свои модули раздельно;

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

Каждая программа  ассемблируется отдельно и генерирует собственный уникальный объектный (OBJ) модуль. Программа компоновщик (LINK) затем компонует объектные модули в один объединенный выполняемый (EXE) модуль.

Обычно выполнение начинается с основной программы, которая  вызывает одну или более подпрограмм. Подпрограммы, в свою очередь, могут вызывать другие подпрограммы.

Существует много  разновидностей организации подпрограмм, но любая организация должна быть «понятна» и Ассемблеру, и компоновщику, и этапу выполнения. Следует быть внимательным к ситуациям, когда, например, подпрограмма 1 вызывает подпрограмму 2, которая вызывает подпрограмму 3 и, которая в свою очередь вызывает подпрограмму 1. Такой процесс, известный как рекурсия, может использоваться на практике, но при неаккуратном обращении может вызвать любопытные ошибки при выполнении.

Команды CALL используются для внутрисегментных вызовов, то есть, для вызовов внутри одного сегмента. Внутрисегментный CALL может быть короткий (в пределах от +127 до -128 байт) или  длинный (превышающий указанные границы). В результате такой операции «старое» значение в регистре IP запоминается в стеке, а «новый» адрес перехода загружается в этот регистр.

Например, внутрисегментный CALL может иметь следующий объектный  код: E82000. Шест.E8 представляет собой  код операции, которая заносит 2000 в виде относительного адреса 0020 в регистр IP. Затем процессор объединяет текущий адрес в регистре CS и относительный адрес в регистре IP для получения адреса следующей выполняемой команды. При возврате из процедуры команда RET восстанавливает из стека старое значение в регистре IP и передает управление таким образом на следующую после CALL команду.

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

Таким образом  в стеке запоминаются и адрес  кодового сегмента и смещение для  последующего возврата из подпрограммы.

Например, межсегментный CALL может состоять из следующего объектного кода:

9A 0002 AF04

Шест.9A представляет собой код команды межсегментного вызова которая записывает значение 0002 в виде 0200 в регистр IP, а значение AF04 в виде 04AF в регистр CS. Комбинация этих адресов указывает на первую выполняемую команду в вызываемой подпрограмме:

Кодовый сегмент 04AF0

Смещение в IP 0200

Действительный адрес 04CF0

При выходе из вызванной  процедуры межсегментная команда  возврата REP восстанавливает оба адреса в регистрах CS и IP и таким образом передает управление на следующую после CALL команду.

4

Атрибуты EXTRN и PUBLIC

Директива EXTRN

Директива EXTRN имеет  следующий формат:

EXTRN имя:тип [, ... ]

Можно определить более одного имени (до конца строки) или закодировать дополнительные директивы EXTRN.

В другом ассемблерном  модуле соответствующее имя должно быть определено и идентифицировано как PUBLIC.

Тип элемента может  быть ABS, BYTE, DWORD, FAR, NEAR, WORD. Имя может  быть определено через EQU и должно удовлетворять реальному определению имени.

Директива PUBLIC

Директива PUBLIC указывает  Ассемблеру и компоновщику, что адрес  указанного идентификатора доступен из других программ. Директива имеет  следующий формат:

PUBLIC идентификатор [, ... ]

Можно определить более одного идентификатора (до конца  строки) или закодировать дополнительные директивы PUBLIC. Идентификаторы могут  быть метками (включая PROC-метки), переменными  или числами.

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

 

 

  

 

4

Компоновка программ на языке С  и Ассемблере

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

u Большинство версий языка C обеспечивают передачу параметров через стек в обратной (по сравнению с другими языками) последовательности. Обычно доступ, например, к двум параметрам, передаваемым через стек, осуществляется следующим образом: 
MOV ES,BP MOV BP,SP  
MOV DH,[BP+4]  
MOV DL,[BP+6] ... 
POP BP RET

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

u В некоторых версиях языка C требуется, чтобы ассемблерные программы, изменяющие регистры DI и SI, записывали их содержимое в стек при входе и восстанавливали эти значения из стека при выходе.

u Ассемблерные программы должны возвращать значения, если это необходимо, в регистре AX (одно слово) или в регистровой паре DX:AX (два слова).

u Для некоторых версий языка C, если ассемблерная программа устанавливает флаг DF, то она должна сбросить его командой CLD перед возвратом.

4

Выполнение COM-программы

В отличие от EXE-файла, COM-файл не содержит заголовок  на диске. Так как организация COM-файла  намного проще, то для DOS необходимо «знать» только то, что тип файла — COM.

Загруженным в  память COM- и EXE-файлам предшествует префикс  программного сегмента. Первые два  байта этого префикса содержат команду INT 20H (возврат в DOS). При загрузке COM-программы DOS устанавливает в четырех сегментных регистрах адрес первого байта PSP.

Затем устанавливается  указатель стека на конец 64 Кбайтового сегмента (шест.FFFE) или на конец памяти, если сегмент не достаточно большой. В вершину стека заносится  нулевое слово. В командный указатель помещается шест.100 (размер PSP). После этого управление передается по адресу регистровой пары CS:IP, то есть, на адрес непосредственно после PSP. Этот адрес является началом выполняемой COM-программы и должен содержать выполнимую команду.

При выходе из программы команда RET заносит в регистр IP нулевое слово, которое было записано в вершину стека при инициализации. В этом случае в регистровой паре CS:IP получается адрес первого байта PSP, где находится команда INT 20H. При выполнении этой команды управление передается в резидентную часть COMMAND.COM. (В случае, если программа завершается по команде INT 20H вместо RET, то управление непосредственно передается в COMMAND.COM).

4

Выполнение EXE-программы

EXE-модуль, созданный  компоновщиком, состоит из следующих двух частей: 1) заголовок — запись, содержащая информацию по управлению и настройке программы и 2) собственно загрузочный модуль.

В заголовке  находится информация о размере  выполняемого модуля, области загрузки в памяти, адресе стека и относительных  смещениях, которые должны заполнить машинные адреса в соответствии с относительными шест.позициями:

00 Шест.4D5A

Компоновщик устанавливает  этот код для идентификации правильного EXE-файла.

02

Число байтов в  последнем блоке EXE-файла.

04

Число 512 байтовых блоков EXE-файла, включая заголовок.

06

Число настраиваемых  элементов.

08

Число 16-байтовых блоков (параграфов) в заголовке, (необходимо для локализации начала выполняемого модуля, следующего после заголовка).

0A

Минимальное число  параграфов, которые должны находится после загруженной программы.

0C

Переключатель загрузки в младшие или старшие  адреса. При компоновке программист  должен решить, должна ли его программа  загружаться для выполнения в  младшие адреса памяти или в старшие  Обычным является загрузка в младшие адреса. Значение шест.0000 указывает на загрузку в старшие адреса, а шест.FFFF — в младшие. Иные значения определяют максимальное число параграфов, которые должны находиться после загруженной программы.

0E

Относительный адрес сегмента стека в выполняемом модуле.

10

Адрес,который  загрузчик должен поместить в  регистр SP перед передачей управления в выполнимый модуль. 

 

12

Контрольная сумма  — сумма всех слов в файле (без  учета переполнений) используется для  проверки потери данных.

14

Относительный адрес, который загрузчик должен поместить в регистр IP до передачи управления в выполняемый модуль.

16

Относительный адрес кодового сегмента в выполняемом  модуле. Этот адрес загрузчик заносит  в регистр CS.

18

Смещение первого  настраиваемого элемента в файле.

1A

Номер оверлейного  фрагмента: нуль обозначает, что заголовок  относится к резидентной части EXE-файла.

1C

Таблица настройки, содержащая переменное число настраиваемых  элементов, соответствующее значению по смещению 06.

Заголовок имеет  минимальный размер 512 байтов и может быть больше, если программа содержит большое число настраиваемых элементов. Позиция 06 в заголовке указывает число элементов в выполняемом модуле, нуждающихся в настройке. Каждый элемент настройки в таблице, начинающейся в позиции 1C заголовка, состоит из двухбайтовых величин смещений и двухбайтовых сегментных значений.

Система строит префикс программного сегмента следом за резидентной частью COMMAND.COM, которая  выполняет операцию загрузки. Затем COMMAND.COM выполняет следующие действия:

u Считывает форматированную часть заголовка в память.

u Вычисляет размер выполнимого модуля (общий размер файла в позиции 04 минус размер заголовка в позиции 08) и загружает модуль в память с начала сегмента.

u Считывает элементы таблицы настройки в рабочую область и прибавляет значения каждого элемента таблицы к началу сегмента (позиция OE).

u Устанавливает в регистрах SS и SP значения из заголовка и прибавляет адрес начала сегмента.

u Устанавливает в регистрах DS и ES сегментный адрес префикса программного сегмента.

u Устанавливает в регистре CS адрес PSP и прибавляет величину смещения в заголовке (позиция 16) к регистру CS. В случае, если сегмент кода непосредственно следует за PSP, то смещение в заголовке равно 256 (шест.100). Регистровая пара CS:IP содержит стартовый адрес в кодовом сегменте, то есть, начальный адрес программы.

После инициализации  регистры CS и SS содержат правильные адреса, а регистр DS (и ES) должны быть установлены  в программе для их собственных  сегментов данных:

1. PUSH DS ;Занести адрес PSP в стек

2. SUB AX,AX ;Занести нулевое значение  в стек 

3. PUSH AX ; для обеспечения выхода  из программы 

4. MOV AX,datasegname ;Установка в регистре DX

5. MOV DS,AX ; адреса сегмента данных

При завершении программы команда RET заносит в регистр IP нулевое значение, которое было помещено в стек в начале выполнения программы. В регистровой паре CS:IP в этом случае получается адрес, который является адресом первого байта PSP, где расположена команда INT 20H. Когда эта команда будет выполнена, управление перейдет в DOS.

4

Функции загрузки и выполнения программы

Рассмотрим теперь, как можно загрузить и выполнить  программу из другой программы. Функция  шест.4B дает возможность одной программе  загрузить другую программу в  память и при необходимости выполнить.

Для этой функции  необходимо загрузить адрес ASCIIZ-строки в регистр DX, а адрес блока параметров в регистр BX (в действительности в регистровую пару ES:BX). В регистре AL устанавливается номер функции 0 или 3:

AL=0. Загрузка и выполнение

Данная операция устанавливает префикс программного сегмента для новой программы, а также адрес подпрограммы реакции на Cntrl/Break и адрес передачи управления на следующую команду после завершения новой программы. Так как все регистры, включая SP, изменяют свои значения, то данная операция не для новичков.

Блок параметров, адресуемый по ES:BX, имеет следующий  формат:

0

Двухбайтовый  сегментный адрес строки параметров для передачи.

2

Четырехбайтовый указатель на командную строку в PSP+80H.

6

Четырехбайтовый указатель на блок FCB в PSP+5CH.

10

Четырехбайтовый указатель на блок FCB в PSP+6CH.

AL=3. Оверлейная загрузка

Данная операция загружает программу или блок кодов, но не создает PSP и не начинает выполнение.

Таким образом  можно создавать оверлейные программы. Блок параметров адресуется по регистровой паре ES:BX и имеет следующий формат:

0

Двухбайтовый  адрес сегмента для загрузки файла.

2

Двухбайтовый  фактор настройки загрузочного модуля.

Возможные коды ошибок, возвращаемые в регистре AX: 01, 02, 05, 08, 10 и 11.

Важно:

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

 

u Будьте внимательны при использовании рекурсий, когда подпрограмма 1 вызывает подпрограмму 2, которая в свою очередь вызывает подпрограмму 1.

Информация о работе Системное программирование