Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

TextmetukSPO

.pdf
Скачиваний:
16
Добавлен:
28.06.2021
Размер:
340.6 Кб
Скачать

МИНИСТЕРСТВО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ Омский государственный технический университет

А.Н. ФЛОРЕНСОВ

ЛАБОРАТОРНЫЙ ПРАКТИКУМ ПО СПО

Методические указания

ОМСК 2006

1

УДК 681.3

ББК 32.973.73

Ф 73

Рецензенты:

Составитель: Флоренсов А.Н.

Ф 73 Лабораторный практикум по СПО.

Метод. указ. – Омск: Изд-во ОмГТУ, 2006. 64 с.

Предназначены для профессиональной подготовки студентов и бакалавров направления «Информатика и вычислительная техника» по дисциплине "Системное программное обеспечение".

Подготовлено кафедрой «Информатика и вычислительная техника» и одобрено редакци- онно-издательским советом ОмГТУ.

© Флоренсов А.Н., 2006

© Омский государственный

технический университет, 2006

2

Лабораторная работа №1.

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

Предварительные сведения. Некоторые операционные системы, более дружественные к профессиональному разработчику, чем другие, позволяют ему доступ к системным функциям API через механизм программных прерываний. Этот вариант доступа практически доступен только в программировании на уровне языка ассемблера. Вместо вызова системной функции по имени, как в случае общего доступа к API функциям, здесь используется команда с мнемокодом INT, имеющая в качестве аргумента единственный операнд – номер прерывания. Это значение для архитектуры IA32 должно находиться в пределах от 0 до 255, причем конкретное значение выбрано разработчиками ОС. Сложившейся практикой в этой области разработок является использование общего номера прерывания (как бы общего номера входа), а детализация – какая именно функция API запрашивается - задается программистом в регистре.

Для ОС Linux в качестве указанного номера выбран 80H, а в однозадачной ОС MS-DOS, входящей в состав современных ОС от Microsoft, для большинства системных функций должен быть номер 21H. Указанные константы заданы в шестнадцатеричной системе, в форме принятой для большинства языков ассемблера с помощью префикса, который задается латинской буквой 'H'. Практической альтернативой такой записи является задание указанных констант в десятичной форме, что дает для них записи 128 и 33, соответственно. Следует учитывать, что в профессиональном программировании на ассемблере используется преимущественно шестнадцатеричная запись констант, что обусловлено выдачей системными программами дизассемблеров большей части своей информации именно в этой форме, поскольку она лучше соответствует побайтовому размещению информации в ячейках машинной памяти.

ВОС Linux номер системной функции должен быть помещен в регистр EAX,

ав MS-DOS – в регистр AH. Никакого семантического контроля над программистом средства программирования на ассемблере в большинстве ситуаций не имеют, поэтому следует быть особенно внимательным при задании подобной информации.

ВОС Linux номерами системных функций write, read и exit, служат, соответственно, 4, 3 и 1. Более обширный список системных функций вместе с их номерами, обеспечивающими доступ через программное прерывание INT 80H, дает файл unistd.h, размещаемый обычно в каталоге /usr/include/asm. Часть информации из начала этого файла приведена в табл. 1.

 

Табл. 1. Номера системных функций в Linux

#define __NR_exit

1

#define __NR_fork

2

#define __NR_read

3

#define __NR_write

4

#define __NR_open

5

3

#define __NR_close

6

#define __NR_waitpid

7

#define __NR_creat

8

#define __NR_link

9

#define __NR_execve

11

#define __NR_chdir

12

#define __NR_time

13

#define __NR_mknod

14

#define __NR_mkdir

39

#define __NR_rmdir

40

#define __NR_dup

41

#define __NR_ioctl

54

#define __NR_fcntl

55

#define __NR_dup2

63

#define __NR_reboot

88

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

ВОС MS-DOS номерами системных функций write, read и exit, служат, соответственно, 40H, 3fH и 2EH. Из-за практической неупотребительности этой ОС где-либо, кроме традиционного преподавания программирования на ассемблере в области операционных систем от Microsoft, другие системные функции MS-DOS заметного интереса для обучения уже не представляют и не приводятся. Заинтересованным студентам следует обратиться к соответствующей литературе [1-3, 5-8, 11-17].

Аргументы системных функций перед выполнением команды программного прерывания помещаются в регистры. Операционная система Linux использует систематический подход в таком размещении исходной информации. Согласно ему, информация задающая аргументы вызова системной функции, соответствующие прототипу функции на языке Си, помещается последовательно в регистры EBX, ECX, EDX, ESI, EDI. Причем для первого аргумента она помещается в регистр EBX, для второго аргумента - в регистр ECX, для третьего - в регистр EDX и т.д.

ВОС MS-DOS подобные правила не систематизированы и чаще всего относятся только к конкретной системной функции. Последнее одной из причин имеет отсутствие в этой ОС стандартизированного программного интерфейса на языке Си, поскольку вся эта ОС в свое время разрабатывалась, модифицировалась и чаще всего использовалась программистами на уровне языка ассемблер. При обучении на указанной ОС следует иметь в виду, что вся система и программирование на ней используют исключительно 16-ти и 8-битные регистры.

При вызове в ОС MS-DOS функций write, read, которые используют номера 40H, 3f H, значение хэндла должно быть помещено в регистр BX. Регистр DX служит при этом для передачи адреса буфера ввода или вывода, откуда выводятся или куда читаются данные. Регистр CX служит для передачи в системную функ-

4

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

Прочитанное число символов при вводе системной функцией чтения (по значению хэндла открытого файла) помещается в Linux в регистр EAX, а в MS-DOS помещается в регистр AX.

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

MOV регистр, имя_области_данных

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

имя_области_данных + число.

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

Обращение к системной функции write в ОС Linux для записи пяти символов из области данных txt в стандартный вывод (т.е. в простейшем случае, на экран), будет таким образом записываться в виде фрагмента ассемблерной программы в виде

MOV EAX, 4

MOV EBX, 1 MOV ECX, txt MOV EDX, 5 INT 80H

Здесь в качестве хэндла стандартого вывода использовано постоянное для Unix

подобных систем значение 1.

Аналогичное обращение к системной функции для вывода на экран в ОС MSDOS запишется в виде

MOV AH, 40H MOV BX, 1 MOV DX, txt MOV CX, 5 INT 21H

5

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

MOV [имя_области + порядковый_номер_байта], значение байта

Например, заменить на символ @ шестой байт области с именем area, запишется на ассемблере как

MOV [area+4], '@'

Здесь следует обратить внимание на два важных момента. Первым в общечеловеческом понимании нумерации байтом этой области будет байт, обозначаемый как [area+0] или просто как [area]. Это "самый первый" или, точнее, начальный ее байт. Следующим по порядку – уже не начальным байтом – будет байт, обозначаемый как [area+1]. Поэтому [area+4] – это действительно пятый байт, поскольку для нумерации байтов используется в качестве базы отсчета не единица, а нулевое значение. Практически ситуация полностью соответствует использованию индексированных переменных, называемых также элементами одномерного массива в языке Си. Напомним, что после описания в этом языке имени area как массива, запись area[0] обозначает первый его элемент, запись area[1] – второй, и, в общем случае, area[n] обозначает (n+1)-й элемент этого массива.

Источник указанной особенности, "неудобной" для начинающих программистов, кроется как раз во внутреннем уровне программирования на машинных кодах и языке ассемблера. Он вытекает из того, что в машинных командах используется не составная запись, подобная [имя_области + порядковый_номер_байта], а уже приведенная служебной программой компилятора запись, которая на ассемблере или дизассемблере имеет вид [смещение]. Здесь элемент смещение является результатом выполнения промежуточных операций по вычислению действительного или эффективного (в зависимости от частной инженерной терминологии) адреса. Это смещение задает число байтов, на которое отстоит от начала сегмента данных элемент, подвергаемый операции в команде, использующей такой операнд. Каждое имя для области данных имеет свое значение, вычисляемое транслятором, и равное его смещению относительно начала сегмента области данных. Переход от [имя_области + порядковый_номер_байта] к форме [смещение] осуществляет транслятор путем сложения значения имени имя_области со значением порядковый_номер_байта. Поэтому [area+0] отвечает тому же смещению, что и начало области area, т.е. начальному (первому) байту. Значение вычисляемого смещения от записи [area+4] соответствует месту, на четыре позиции удаленному от начала области данных area, и это в обычной нумерации оказывается как раз пятый байт.

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

Второй, более мелкой особенностью, записи на ассемблере команды MOV [area+4], '@'

6

является необходимость включения в апострофы символа, который требуется переслать этой командой. Эта особенность может быть привычной для программирующего на языках высокого уровня. Но все же следует обратить внимание, что команда MOV [area+4], '7' помещает в восьмой байт области area символ цифры 7, а команда MOV [area+4], 7 помещает в восьмой байт этой области значение числа 7. Если обратиться к таблицам кодировки алфавитно-цифровых символов для используемого семейства компьютеров, а именно к таблицам кодировки ASCII, то можно увидеть, что шестнадцатеричным кодом цифры 7 будет код 37H, а для числа 7 соответствующий шестнадцатеричный код будет просто 07H. Таким образом, различие существенно не только по форме записи, но и по результату использования, от которого, как правило, будет зависеть дальнейшее поведение программы.

При программировании на языке ассемблера совершенно необходимо учитывать сегментную структуру программы, соотносимую реальной внутренней структуре исполняемых файлов современных операционных систем. Лишь упрощенное программирование для старой ОС MS-DOS позволяет ограничиваться одним сегментом. Во всех остальных используемых ОС требуется, как минимум, указывать отдельно сегмент данных и сегмент команд. Для этих целей предназначена директива ассемблера SEGMENT, имеющая в простейших случаях только один операнд – имя сегмента. По причине установления связей с другими компонентами системы программирования на этапе компоновки, в качестве стандартного имени для сегмента команд в Linux принято имя .text, включающее в качестве начального символа "точку" как специальный символ. Для сегмента данных в качестве стандартного принято имя .data. В этой же ОС для системного компоновщика, называемого ld, необходимо указать точку входа в выполняемую программу. С этой целью первая исполняемая команда, не обязательно первая по порядку записи, должна быть снабжена стандартной меткой _start, где символ подчеркивания входит в состав имени. Кроме того, это имя должно быть указано как доступное со стороны других программ, в частности, запускающей на выполнения, путем указания в качестве операнда в составе директивы GLOBAL.

Следует обратить особое внимание, что в директивах ассемблера, в частности, GLOBAL и SEGMENT, операнд должен отделяться от мнемокода директивы одним или более пробелом. Аналогичное требование относится и к командам. В них совершенно необходимо пробелом или пробелами отделять мнемокод и операнды. Если в языках высоко уровня разделителями содержательных частей операторов (обычно называемые в методах трансляции лексемами) могут служить вспомогательных символы – точки, знаки операций и т.п., то для сложившегося стиля построения и использования языков ассемблера это соглашение не всегда действует. Следует взять за правило, всегда разделять мнемокоды и операторную часть команд и директив ассемблера пробелами. Отклонение от указанного правила трансляторами нередко не контролируется и, впоследствии, приводит к неочевидным ошибкам поведения программ.

Весь лабораторный цикл, в той его части, где используется программирование на ассемблере, предполагает применения свободно распространяемого инструментального пакета программ nasm. Этот пакет включает как собственно компилятор, так и вспомогательный компоновщик alink.exe для операционных систем

7

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

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

nasm –h

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

Для Windows этот транслятор имеет модификацию имени в виде nasmw.exe. Все эти компиляторы являются многофункциональными и поддерживают форматы объектных файлов нескольких типов, пригодные для нескольких операционных систем. Обычно для "своей собственной" системы компилятор как исполняемый файл обеспечивает формат по умолчанию. В общем случае, этот формат задается опцией –f. Основными форматами, записываемыми символически в составе опции, для данного цикла лабораторных работ являются bin, obj, win32, elf. Первый из них производит безформатные односегментные файлы для операционной системы MS-DOS, а последний – для Linux. Кроме того, целесообразно использовать опцию принудительного задания имени результирующего файла, поскольку его именование по умолчанию имеет свои недостатки. Эта опция задается ключом –o, за которым должно следовать собственно название создаваемого файла. Для операционных систем Microsoft это название следует иметь расширение com (допустимо и использование расширения exe, но по традиции в MS-DOS исполняемые односегментные файлы должны иметь расширение именно com).

Таким образом, полная строка вызова транслятора в командной строке для исходного файла prog.asm будет записываться для MS-DOS как

nasmw –f bin -o prog.com prog.asm

В этой ОС для односегментной программы может не использоваться настройка компоновщиком и поэтому в нашем варианте этот вызов непосредственно дает исполняемый файл типа com.

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

nasm –f elf -o prog.o prog.asm

Результатом работы транслятора при отсутствии ошибок будет объектный файл prog.o, который необходимо превратить в исполняемый файл вызовом компоновщика в виде текста командной стоки

ld -o prog.exe prog.o

Если разработчику удобнее запускать исполняемый файл не через файловый менеджер типа MidnightCommander или подобный ему, то непосредственно в командной строке требуется задавать вызов в виде

./prog.exe

(Вначале точка, обозначающая текущий каталог, затем разделитель для указания далее имени исполняемого файла, и только потом собственное имя этого файла.)

Последнее, на что следует обратить внимание при выполнении работы, это запись инициализированных данных (исходных данных). Для формирования

8

отдельных информационных объектов в качестве простейших кирпичиков описания используются директивы DB, DW, DD. Первая формирует элементы размером в байт, вторая – размером в 16-битное слово, а последняя 32-битные элементы. При этом следует иметь в виду, что при задании элементов описания в виде шестнадцатеричного кода младшие цифры этого описания задают значения младших байтов области данных. Таким образом, описание

prim DD 123456H

поместит в начало области данных, обозначаемой именем prim, байт со значением 56H, в следующий байт поместиться значение 34H, а в старший байт, т.е. байт, который в программе можно обозначить как [prim+3], нулевое значение.

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

symmm DD 'abc'

в первый (начальный) байт области symmm запишет символ 'a', далее - символ 'b', а в последний байт этой области занесется нулевое значение.

Задание. Разработать на языке ассемблера программу для операционной системы Linux, которая должна выводить приглашение для ввода текста, вводить произвольный текст со стандартного ввода, изменять второй из введенных символов на восклицательный знак и выводить полученный текст на экран. Затем разработать на языке ассемблера программу для операционной системы MS-DOS, которая выводит четыре строки текста, причем первая и четвертые строки завершаются управляющими символами 13,10, вторая - только управляющим символом 13, а вторая строка завершается только управляющим символом 10. Объяснить результаты работы программы. Ввод и вывод обеспечить путем использования программных прерываний с соответствующим номером и помещением аргументов в требуемые для этого регистры.

Контрольные вопросы.

1.Как запустить на выполнение односегментную программу для MS-DOS, если в nasmw использовать формат по умолчанию?

2.Что получится, если при вызове системной функции записи на экран в MSDOS номер функции поместить в регистр EAX, а не в AH ?

3.Перепишите обращение к функциям MS-DOS через помещение аргументов исключительно в 32-битные регистры, не использую при этом 8-ми и 16-битные регистры.

4.Что выведет на экран программа, которая заносит в регистр ECX адрес области obl, в сегменте данных область описана как

obl DD 'A', 'B', 'C'

и запрашивается вывод трех символов.

Лабораторная работа №2

9

Содержание работы. Изучение ассемблерных средств условных и безусловных переходов в программе.

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

В простейшем случае, часть условие может формироваться одной отдельной командой, которая в мнемокоде ассемблеров обозначают обычно CMP. Эта команда имеет два операнда, которые сравниваются. Результат сравнения фиксируется в специальном регистре, который в архитектуре IA32 называется Eflags. Он содержит набор отдельных битов, называемых флагами, каждый из которых фиксирует результат сравнения или другой операции, устанавливающей эти флаги. Существуют отдельный флаг ZF для фиксации равенства нулю, что при сравнении равносильно равенстве сравниваемых операндов. Имеется отдельный флаг NF для отрицательности, равносильной тому, что первый из сравниваемых операндов меньше второго. Используется еще целый набор флагов, в частности, флаг CF фиксации наличия переноса при выполнении операции и т.д. Детальное знание этих флагов не требуется для современных программистов на ассемблере, поскольку их содержательное использование опирается на мнемонику команд

условного перехода.

Эти команды имеют в ассемблерах, сложившихся для архитектуры IA32, обозначение вида Jcc, где cc обозначает, в свою очередь, мнемонику кода условия (Condition Code). Например, команда JE, кратко обозначающая условный переход по условию равенства (equal), в фрагменте программы

CMP EAX, EDX JE metka1

обеспечивает переход к выполнению команды программы, помеченной меткой metka1, если перед выполнением команды JE metka1 оказывается, что содержимое регистров EAX и EDX равны. Само сравнение при этом выполняется командой CMP EAX, EDX, предшествующей команде условного перехода. Если же при выполнении программу содержимое этих регистров на указанный момент не равно, то процессор выполняет команду, непосредственно следующую в программе за данной командой условного перехода. Никакого "перехода" при этом не происходит, а выполняется естественная линейная последовательности команд, следующих в памяти компьютера одна за другой, как последовательность машинных кодов в аппаратной памяти компьютера.

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

Не всегда требуется явно выполнять команду CMP сравнения, поскольку большинство команд, преобразующих информацию, одновременно с этим форми-

10