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

Textnew2

.pdf
Скачиваний:
13
Добавлен:
06.02.2018
Размер:
1.48 Mб
Скачать

;извлечение из этой записи размера файла и номера его

;начального кластера

mov ax,[es:bx+0001Ch]; AX=мл. слово Size of File mov dx,[es:bx+0001Eh]; DX=ст. слово Size of File div word [SectSiz]; AX=число секторов в файле

inc al

; скорректировать, с учетом того,

 

; что последний сектор м.б. не полон

mov cl,al

; CL=число секторов файла, которые

 

; нужно прочесть с диска

mov dx,[es:bx+01Ah]; DX=начальный кластер файла

;переход от номера кластера к порядковому номеру начального

;сектора для требуемого файла

dec dx

 

; скорректировать, с учетом того, что два

dec dx

 

; первых номера для кластеров не используется

mov al,[ClustSiz]; AL=секторов на кластер

xor ah,ah

; AH := 0

mul dx

;(DX,AX)=порядковый номер сектора для

; текущего кластера от начала DataArea

add ax,[Dat0]

; + число секторов до начала DataArea

mov bx,00800h ; SEG-адрес 32K для регистра ES

mov es,bx

; поместить этот адрес в ES

xor di,di

; DI :=0

push es

 

; SEG-адрес для загрузчика OSLoader в Stack

push di

; смещение для начального значения IP в

;OSLoader (запомнить в Stack)

;(DX,AX)=порядковый номер первого сектора начального кластера загрузчика OSLoader

;CL=сколько секторов в этом файле

call ReadFlopp

; вызов процедуры чтения секторов диска

;для чтении файла в оперативную память, размещая его

;содержимое с адреса 08000h этой памяти (c 32 Кб)

 

retf

; перейти к началу выполнения загруженной программы,

 

 

; используя стартовый адрес в стеке

nextc:mov si, msg1

 

 

jmp short towiw

 

pmsg2:

 

mov si, msg2

towiw:

 

call wiw

 

 

sti

 

; запретить все аппаратные прерывания

cicl:

jmp short cicl

; для "глухого" зацикливания

wiw:

lodsb

; загрузить байт, на который указывает DS:SI,

171

 

; в AL и инкремент SI

or al,al

; загруженный в AL байт нулевой ? (конец сообщ. ?)

je short retu ; если да, то конец вывода через переход на ; возврат из процедуры

mov ah,byte 0Eh ; иначе вызов функции 0Eh

 

; для BIOS Video Service

mov bx,7

; страница видео =0, attribute = white on black

int 10h

 

; Записать символ в режиме телетайпа

jmp short wiw

; переход на извлечение следующего

 

; символа сообщения

retu: retn

; возврат в вызвавшую подпрограмму

;PROC процедура последовательного чтения секторов диска в обл.

;буфера; DI=offset в области буфера, ES=SEG адрес буфера,

;AX=порядковый номер начального сектора для чтения

;CL=число секторов, требующих чтения в область буфера

ReadFlopp: push ax

; сохранить рабочие регистры

push cx

 

;

push ax

; сохранить номер сектора для чтения в стеке

mov ax,[HeadCnt]; AX=число головок для диска mul byte [TrkSize]; AX=число секторов,

 

; принадлежащих одному цилиндру

mov bx,ax

; BX = число секторов в цилиндре

pop ax

; AX=порядковый номер первого сектора для чтения

xor dx,dx

; DX :=0 - только для floppy (секторов <65536)

div bx

 

; AX=первый цилиндр читаемой области, DX=номер сектора на нем xchg dx,ax ; DX=первый цилиндр читаемой области,

;AX=номер сектора на нем

;DL - начальный цилиндр для чтения

div byte [TrkSize]; AL=номер дорожки для начала чтения,

 

; AH=номер сектора на ней

xchg ah,al

; обменять AH и AL

mov bx,ax

; BH=номер дорожки для начала чтения,

 

; BL=номер сектора на ней

inc bl

; BL = действ. номер сектора для начала чтения

 

; (отсчет секторов с 1)

mov ax,[TrkSize]; AX=секторов на дорожке

sub al,bl

; AL=число секторов, требуемых с этой дорожки

 

; (-1,т.к. был увеличен BL)

inc ax

; +1 (коррекция, учитывающая нумерацию с 1)

push ax

; сохранить это значение в стеке

; занесение аргументов в рег. для функции чтения через INT 13h

172

mov AH,2 ; функция BIOS Чтения с диска mov CH,dl

mov CL,bl

mov DL,[LogDriv]; DL=лог. номер дисковода (для A: - 0) mov DH,bh ; DH=головка

mov BX,di ; BX=смещение в буфере

;AL-cnt of sectors, AH=02h, CH-cyl, CL-Sector, DH-Head, DL-drive

;ES:BX - buffer

int 13h

; BIOS Disk Service

met2: jb short pmsg2

; если ERROR переход на метку pmsg2

; иначе BX=числу секторов, прочитанных с дорожки

pop bx

; восстановить BX

pop cx

; восстановить CX

mov ax,bx

; AX=Number reading Sectors

mul word [SectSiz]; (DX,AX)=число прочитанных байтов

add di,ax

; увеличить смещение в DI на это число

pop ax

; восстановить AX

add ax,bx

; AX=порядковый номер сектора для следующей

;операции чтения с диска

;CX=сколько всего секторов должно было быть прочитано перед

;обращением к Disk Service

;BX=число секторов, действительно прочитанных с дорожки

sub cl,bl

; уменьшить число секторов, требующих

 

; последующего чтения

jg short ReadFlopp; повторить чтение с диска

retn

; возврат

msg1 DB 'NO OSLoader for loading microOS!!',13,10,0 msg2 DB 'Error reading from floppy!! ',13,10,0 fnameDB 'OS2BOOT '

TIMES 510-$+start DB 0 DB 55h, 0Aah

Рис. 7.7.1. Программа для загрузочного сектора дискеты

Начальный загрузчик состоит из главной программы, начинающейся с метки beg и двух подпрограмм с именами ReadFlopp и wiw. Подпрограмма wiw предназначена для вывода сообщений и используется для сообщений об ошибках. Сами тексты сообщений представлены именованными областями данных msg1 и msg2, причем эти данные кроме содержательного текста содержат управляющие символы для вывода в режиме консоли (символы с кодами 13 и 10), а также завершающий нулевой байт.

Процедура wiw для вывода на экран использует функцию видеосервиса BIOS IBM совместимых компьютеров, заложенную в эту архитектуру еще более двадца-

173

ти лет назад. Функции видеосервиса вызываются через прерывание с номером 10h, а используемая функция имеет номер 0Eh, который должен быть помещен перед вызовом в регистр AH. Функции BIOS оказываются доступными в начальном загрузчике потому, что программа исходной фазы загрузки, записанная в ПЗУ компьютера, после тестирования аппаратуры (и выполнения действий подсистемы Plug and Play в современных компьютерах) включает в работу все обработчики функций BIOS (подобно тому, как в разделах 8.1 и 8.2 мы включали свои пользовательские обработчики). После этого не только функции группы прерывания INT 10h, но оказываются доступными и обработчики прерываний с номером 13h, которые предназначены для примитивного обслуживания дисковых устройств памяти.

Функция 0Eh из группы видеосервиса требует (в качестве аргументов) задания кода символа в регистре AL и байта атрибута в регистре BL. (Кроме того, в регистре BH должен задаваться номер видеостраницы, который обычно берется нулевым). Для вывода всех символов сообщения используется вспомогательная команда lodsb, которая загружает очередной байт сообщения в регистр AL и инкрементирует содержимое регистра SI. Аргументом процедуры wiw служит содержимое регистра SI, в который перед вызовом этой процедуры заносится величина смещения для сообщения, выводимого на экран.

Процедура ReadFlopp предназначена для чтения последовательности секторов гибкого диска (в прототипе аналогичная процедура может использоваться и для чтения последовательности секторов из раздела жесткого диска). Эта процедура в качестве аргументов используется содержимое регистров AX, CL, DI и ES (прототип использует и регистр DX, в котором там содержится старшая часть номера сектора диска, с которого требуется произвести чтение). (Регистр BX используется этой процедурой в качестве рабочего, но для сокращения объема всей программы даже не сохраняется в процедуре и, соответственно, не восстанавливается.) Мы вернемся к описанию действий этой процедуры несколько позже.

Главная часть загрузчика в самом начале запрещает прерывания и устанавливает начальные значения для сегментных регистров SS и DS. В первый из них записывается нулевое значение, так что в дальнейшем (до переустановки SS в какой-то последующей программе) системный стек будет размещаться с начала оперативной памяти. (Теоретически при приближении к переполнению стека может быть затронута таблица векторов прерываний, но практически до этого дела не дойдет, так как размер стека достаточно велик.) Этот размер устанавливается занесением в регистр SP значения, равного 7C00h (что составляет целых 28 Кбайт). Заметим, что данная программ выполняет занесение нулевых значений в регистры самым компактным (с точки зрения используемых машинных кодов команд) способом, а именно применением команды XOR reg,reg).

Использованный размер стека определяется одним важным обстоятельством: начиная с самых первых загрузчиков для IBM совместимых компьютеров, программа загрузки из ПЗУ размещает содержимое загрузочного сектора диска в оперативной памяти с адреса 7C00h (Когда-то предполагалось, что больше 32 Кбайт памяти в

174

компьютере может и не быть, поэтому использовано это инженерно выбранное решение). Следствие упомянутого решения является занесение в сегментный регистр DS указанного значения 7C00h (начального сегментного адреса, с которого в памяти размещены машинные коды данной программы после ее загрузки).

Дальнейшие действия основной программы заключаются в:

n подготовке данных для последующего чтения с загрузочного диска;

n последовательного чтения секторов диска, содержащих область корневого оглавления диска (ROOT) в область буфера для оглавления;

n последовательный просмотр записей оглавления в этом буфере на предмет нахождения записи для фиксированного имени файла;

n извлечения из этой записи размера файла и номера его начального кластера;

n переходе от номер кластера к порядковому номеру начального сектора для требуемого файла;

n чтении файла в оперативную память, размещая его содержимое с фиксированного адреса этой памяти;

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

Для буфера области корневого оглавления в оперативной памяти использован начальный адрес 010000h (64-й Кбайт памяти), а для начальной границы, с которой грузится в память требуемый файл, выбран адрес 08000h (32-й Кбайт памяти). Именно с последнего из указанных адресов будут размещаться в памяти программы, рассмотренные в предыдущих разделах и использующие данный начальный загрузчик.

Сегментный адрес буфера для корневого оглавления устанавливается путем помещения константы 01000h в регистр ES перед первым вызовом подпрограммы ReadFlopp в основной программе, а адрес начала размещения загружаемой программы устанавливается путем занесения в тот же регистр константы 0800h перед вторым вызовом подпрограммы ReadFlopp.

Наиболее простым из перечисленных выше основных действий является последнее - запуск программы. Но выполняется оно очень своеобразным и практически не используемым в прикладных программах способом. Для этого в стек укладывается значение сегментного адреса для запускаемой программы (содержимое регистра ES, которое равно 0800h), затем в стек укладывается нулевое значение (для будущего содержимого регистра IP), а затем (после обращения к подпрограмме ReadFlopp) выполняется команда RETF. При выполнении последняя команда снимает со стека указанные значения и тем самым запускает программу.

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

175

цией собственно программ, опираясь на физические номера секторов данных логического диска. Физические номера или CHS-номера задаются тремя параметрами, отражающими физическое строение магнитного диска: номером цилиндра (cylinder), номером головки (head) и номером сектора на дорожке. Номер дорожки совпадает с номером цилиндра, а сам цилиндр представляет собой совокупность всех дорожек с одинаковым номером. Логическая нумерация заключается в том, что следующий за последним сектором текущей дорожки считается начальный сектор дорожки для следующей головки (следующей поверхности пакета дисков) на том же цилиндре. Если же дорожка, исходная в рассмотрении следующего логического номера, отвечает последней головке диска, то следующим считается начальный сектор нулевой головки для следующего номера цилиндра (т.е. следующего номера дорожки). Заметим, что физические номера хранятся в специальных служебных частях реальных секторов для данных на диске и используются для контроля работы магнитных дисков. Следует также иметь в виду, что реально используемая при этом нумерация головок (поверхностей) начинается с нуля. С нулевого значения начинается и нумерация дорожек (цилиндров) на диске. Нумерация же секторов данных начинает со значения единицы. (В действительности секторы с нулевыми номерами существуют, но используются не для хранения данных, а для служебной информации самого магнитного носителя). Такая ситуация требует коррекции, где это оказывается необходимым, потому что ряд математических расчетов дают порядковые номера, базированные относительно нуля.

В области BPB используемого загрузочного сектора должны находится числовые значения параметров, точно отвечающие характеристикам магнитного диска. Рассматриваемая программа специализирована на характеристики дискеты стандартного емкости в 1.44 Мбайт, отформатированной для файловой системы FAT. Для других вариантов технических характеристик эта программа не будет работать, также как и для дискет, отформатированных для других файловых систем (к счастью для начинающих пользователей, им будет совсем не просто найти такую "неподходящую" файловую систему). Назначение полей отдельных числовых характеристик BPB приведено в комментариях к соответствующих именованным областям данных и более подробно объясняться здесь не будет.

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

SEGMENT .text

 

org 100h

 

start: mov ah,3dh

; функция DOS открытия файла

mov al, 0 ; по режиму только чтения

mov dx, fboot ; отн. адрес имени открываемого файла

int 21h

; вызов функции DOS

jc errop

; если ошибка открытия файла

176

mov bx, ax

;

запомнить значение handle в BX

mov ah,3fh

;

функция DOS чтения из файла

mov cx, 512

; число читаемых байтов из файла

mov dx, buffer ; отн. адрес буфера в памяти для функции

int 21h

 

; вызов функции DOS

jc errf

 

; если ошибка чтения из файла

xor dl,dl

; dl - логический номер Floppy Drive

mov al,1

; число записываемых секторов

mov cl,1

; номер начального сектора для записи

mov ch,0

; номер цилиндра для записи

mov dh,0

; номер головки для записи

mov bx, buffer ; отн. адрес буфера, откуда выводятся данные

push ds

; помещение сегментного адреса буфера

pop es

 

; в регистр ES (по требованием функции записи)

mov ah,03h

; номер функции BIOS для записи секторов

int 13h

 

; вызов функции Disk Service

jc err13

; если ошибка операции с диском

mov ah,9

; номер функции вывода сообщения в DOS

mov dx, prompt ; отн. адрес текста сообщения

int 21h

 

; обращение к функции BIOS

exit: mov ah,4ch; номер функции завершения программы в DOS

int 21h

 

; вызов функции DOS

err13: mov ah,9; номер функции вывода сообщ. в DOS

mov dx, merr13; отн. адрес сообщ. об ошибке записи на диск

int 21h

 

; вызов функции DOS

jmp exit

; переход на выход из программы

errop: mov ah,9; номер функции вывода сообщ. в DOS

mov dx, merrop; отн. адрес сообщения об ошибке открытия файла

int 21h

 

; вызов функции DOS

jmp exit

; переход на выход из программы

errf: mov ah,9

; номер функции вывода сообщения в DOS

mov dx, merrf ; отн. сообщения об ошибки чтения из файла

int 21h

 

; вызов функции DOS

jmp exit

; переход на выход из программы

merr13

db 10,13,'Error Int 13h',13,10,'$'

merrop

db 10,13,'Error Open File bootrec.rec!',13,10,'$'

merrf

db 10,13,'Error Action with file bootrec.rec!',13,10,'$'

fboot

db 'bootrec.rec',0

prompt

db 'Запиcaно содержимое BOOT сектора на дисковод A:',13,10,'$'

buffer

TIMES 512 db 0

Рис. 7.7.2. Программа записи в загрузочный сектор начального загрузчика

177

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

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

8.БИБЛИОТЕКИ ДИНАМИЧЕСКОЙ КОМПАНОВКИ

8.1.Понятие о статической и динамической компоновке

Одним из важнейших компонентов многозадачной операционной системы являются библиотеки динамической компоновки (Dynamic Linking Library). Чтобы освоиться с ними, следует предварительно рассмотреть, как выполняется окончательное построение двоичных кодов машинных команд. В процессе автоматического выполнения приложения (программных кодов с их данными и вспомогательной информацией) аппаратура компьютера имеет дело практически только с машинными кодами - двоичными кодами, которые являются для нее командами процессора, командами отдельных контроллеров и внутренними представлениями данных. Имен привычных для человека - последовательностей символов латинского или русского алфавита - аппаратура использовать непосредственно не может. Вместо них аппаратура использует адреса и номера позиций в различных таблицах (начало таких таблиц для аппаратуры указывается опять же адресами).

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

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

178

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

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

имножества заготовок из библиотек. (Заметим, что в последних операционных системах исполняемый файл имеет расширение EXE.) Это формирование выполняет компоновщик.

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

CALL FUNA

игде-то в этой же программе записано

EXTERN FUNA

но функция FUNA - как последовательность реализующих ее действий - описана в другом модуле (другой единице компиляции), то в этом исходным тексте нет достаточной информации о начальном адресе машинных кодов этой функции. Компилятор с языка ассемблера - для компьютеров IBM PC и подобных им - оператор вызова функции преобразует в 5 байтов с шестнадцатеричным кодом

179

E8 00000000

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

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

Запись строки листинга исходной программы для рассматриваемого примера

вызова программы FUNA будет иметь вид

 

 

A7A6A5A4A3A2A1A0 E8 00000000e

CALL

FUNA

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

FUNA Near32 FLAT:---- Extern

Листинг - это полезный файл для профессионального программиста, хотя и является необязательным. Он отображает содержимое объектного файла, который имеет специальный формат. В объектном файле кроме части, полностью воспроизводящей код машинных команд из листинга, присутствует информации словаря внешних имен и словаря перемещений. В современных системах программирования эта информация стандартизована в небольшом числе используемых форматов. Практически в объектном файле для рассматриваемого примера присутствует специальная запись, в которой условным кодом задано, что имя, записываемое как FUNA, внешнее, имеет атрибут типа NEAR и это имя используется в поле с адресом B7B6B5B4B3B2B1B0, который при трансляции вычисляется как значение на единицу большее адреса A7A6A5A4A3A2A1A0 (большее не единицу, потому что длина кода команды перед полем операнда равна в этом примере 1). В упомянутом частном примере для исходного смещения 0000049 команды, адрес поля в записи словаря перемещений есть 0000004a.

Таким образом, в объектном коде присутствует информация вида |Внешнее имя| Атрибут | Определено вне| Отн. адрес использования|

В нашем примере эта подобная информация имеет общий вид (не обязательно повторяясь в деталях)

180

Соседние файлы в предмете Операционные системы