Память MS-DOS
Процессы MS-DOS
Резидентные программы
Функция 4Bh - загрузка и выполнение программ
Переключение контекста и переключение стека
Введение в резидентную часть оперативной памяти
REMOVE - пример интегрированной программы
Заключение
В предыдущей главе были изучены средства создания программ
MS-DOS и различные пути их структурирования. Теперь рассмотрим
как программы MS-DOS существуют в среде MS-DOS. При этом мы для
более полного пояснения некоторых тем, ссылающихся на предыдущие
главы, неоднократно будем возвращаться назад: префикс программно-
го сегмента, работа по распределению памяти MS-DOS, и механизм,
используемый для загрузки программ MS-DOS. В заключение мы введем
механизм для установки программ, остающихся резидентными - тема,
развиваемая в главе 4 и обсуждающая завершение и оставление в па-
мяти резидентных программ (TSR - terminate and stay resident
programs).
Память MS-DOS
Самый простой способ понимания среды функционирования MS-DOS
состоит в рассмотрении формата памяти MS-DOS, моделей распределе-
ния своей ограниченной памяти для всех выполняемых ею и конкури-
рующих между собой целей. Несмотря на отсутствие общих предписа-
ний MS-DOS по отдельным форматам памяти, огромная популярность
стандартов фирмы "ИБМ" и их последовательное принятие обеспечива-
ют нас форматом памяти де-факто.
Формат физической памяти MS-DOS
MS-DOS была разработана для устройств центрального процессора
(CPU) 8086/8088, позволяющих адресовать суммарную память объемом
1 Мбайт. Типичное использование и размещение этой памяти показано
на Рис.3-1. Первые 10 сегментов ("кусков" по 64 кбайт) этой памя-
ти относятся к области пользователя. Это область, объемом в 640
Кбайт, в которой расположены сама MS-DOS и прикладные программы
пользователя. Оставшиеся 6 сегментов, составляющие в сумме
384 Кбайт, называются системной областью и резервируются для ис-
пользования ROM-BIOS (Постоянное запоминающее устройство (ПЗУ)-
базовая система ввода-вывода) и для связи с другими платами в
системе. Заметим, что Рис.3-1 значительно упрощает использование
системной области. В действительности, имеется много типов плат,
которые используют эту область для многих целей, однако, будем
рассматривать только общую схему.
Расширяемая и расширенная память
С появлением MS-DOS были разработаны более мощные центральные
процессоры. Центральные процессоры 80286 и 80386, каждый из кото-
рых имеет расширяемые пределы адресуемой памяти,позволяют в одной
системе размещать мегабайты памяти. Какая польза от того, что MS-
DOS получит сколько-нибудь байтов из этой дополнительной памяти?
- 3-2 -
Адрес Использование памяти
FFFFF .------------------------------------------
| Системное ПЗУ | ^ ^
F0000 |-------------------------| | |
| Используется системой | | |
E0000 |-------------------------| 384 кбайт |
| Используется системой |ПЗУ или др. |
D0000 |-------------------------| | |
| Видеопамять (Video RAM) | | |
C0000 |-------------------------| | |
| Графика EGA | V |
A0000 |-------------------------|------- |
| Пользователь | ^ |
90000 |-------------------------| | |
| Пользователь | | |
80000 |-------------------------| | 1 мбайт
| Пользователь | | |
|-------------------------| | |
| Пользователь | | |
60000 |-------------------------| 640 кбайт |
| Пользователь | Область |
50000 |-------------------------|пользователя |
| Пользователь | | |
40000 |-------------------------| | |
| Пользователь | | |
30000 |-------------------------| | |
| Пользователь | | |
20000 |-------------------------| | |
| Пользователь | | |
10000 |-------------------------| | |
| Используется системой | V V
00000 ------------------------------------------
Рис.3-1. Стандартный формат памяти IBM PC/XT/AT для MS-DOS
Непосредственно никакая, но в большинстве случаев эта допол-
нительная "расширенная память" (поскольку она простирается выше
границы в 1 Мбайт) может часто быть использована как псевдодиск
или в более общем плане, как другой тип дополнительной памяти MS-
DOS, называемой "расширяемой памятью" (потому, что она "расширя-
ет" основной предел MS-DOS в 640 Кбайт).
Для MS-DOS версии 3.3 и более ранних версий продукты расширя-
емой памяти доступны в трех разновидностях. Первая спецификация
расширяемой памяти была разработана совместно фирмами "Лотус",
"Интел" и "Майкрософт" и называлась LIM EMS версии 3.2 (Limit
Expanded Memory Specification - предельная спецификация расширяе-
мой памяти). Несколько позже фирмы "Аштон-Тэйт", "Квэдрим" и
"АСТ" разработали улучшенный стандарт AQA EEMS (the Enhanced
Expanded Memory Specification - улучшенную спецификацию расширяе-
мой памяти). Фирмы "Лотус", "Интел" и "Майкрософт" соединили луч-
шие стороны AQA EEMS в LIM EMS версии 4.0. Все системы EMS состо-
ят из памяти (на соединительной плате или плате расширения) и
администратора улучшенной памяти EMM (the Enhanced Memory
Manager) - устанавливаемого драйвера устройства. Для установки
функций EMS резервируется прерывание MS-DOS с номером 67h. MS-DOS
версии 4.0 и выше, как часть операционной системы, поддерживает
стандарт LIM EMS версии 4.0. Реализация аппаратных средств от
производителя к производителю меняется. Программное обеспечение
EMS MS-DOS версии 4.0 состоит из устанавливаемого драйвера уст-
ройства, а, фактически, любого драйвера устройства EMS и совмес-
тимого соединения аппаратных средств, которые могут быть заменены
- 3-3 -
применяемой операционной системой.
Расширяемая память является результатом появления в среде
MS-DOS устойчивых традиций использования страничной памяти или
памяти коммутации банков. При этом подходе большой раздел памя-
ти, который лежит вне адресного пространства процессора, "отобра-
жается" малыми областями на многие маленькие разделы памяти, ле-
жащие внутри адресного пространства процессора. В то время как
процессор не может адресовать большой раздел памяти непосредс-
твенно, он может выбрать или дойти до любой конкретной части, по-
добно выбору страницы в книге.
В спецификации расширяемой памяти MS-DOS или EMS большая фи-
зическая память отображается в 16-килобайтные разделы памяти
MS-DOS, называемые страницами. Соответствующее 16-килобайтное ад-
ресное пространство в памяти MS-DOS называется страничным фрей-
мом. Количество поддерживаемых страничных фреймов и размещение их
внутри системы MS-DOS изменяется в зависимости от типа платы ис-
пользуемой расширяемой памяти, и существующей конфигурации систе-
мы.
Глава 7 посвящена обзору памяти EMS, описывающей методы дос-
тупа, стандарт EMS и пр. В данном обсуждении мы хотя и признаем
существование памяти EMS, но не уделяем ей большого внимания.
Прежде всего нас будет интересовать, как сама MS-DOS использует
память, и для нас достаточно отметить, что память EMS должна быть
отображена на стандартное адресное пространство памяти для того,
чтобы быть доступной для MS-DOS. (Имеются соображения о том, что
последующие версии MS-DOS могут использовать память EMS непос-
редственно, реально преодолевая границу в 640 Кбайт).
Использование памяти MS-DOS
К этому моменту мы установили, что по текущему стандарту фак-
тически MS-DOS имеет 640 Кбайт памяти для использования ею самой
и прикладными программами пользователя. В типовой системе MS-DOS
эта память будет распределяться так, как показано на Рис. 3-2.
Рассматривая Рис.3-2, можно заметить, что большинство приведенных
адресов указано приблизительно и зависит от версии MS-DOS, физи-
ческой конфигурации системы и опций, указываемых пользователями в
файлах конфигурации системы CONFIG.SYS и AUTOEXEC.BAT. Кроме то-
го, размеры сегментов, указанные на Рис. 3-2, показаны не в масш-
табе, а в соответствии с данной относительной позицией различных
компонентов.
На Рис.3-2 есть несколько областей, которые требуют поясне-
ния. Заметим, что первая область COMMAND.COM появляется на схеме
памяти дважды. Загружены две копии COMMAND.COM? Нет, просто
COMMAND.COM загружается в две отдельные области. Область, разме-
щенная выше драйверов устройств, сохраняется в памяти постоянно и
называется резидентной частью. Эта часть отвечает за корректность
завершения программ обработки и любые ошибки программы пользова-
теля, которые возникают в результате завершения работы программы.
Этот раздел является "порождающей программой" любой выполняемой
программы пользователя. Другой раздел COMMAND.COM, размещенный в
верхней части схемы памяти, является частью, которая обеспечивает
интерфейс пользователя с MS-DOS. Эта часть называется нерезидент-
ной, потому что она представлена только тогда, когда не выполня-
ются программы пользователя, или когда программа пользователя пы-
тается загрузить другую программу.
- 3-4 -
Адрес Использование памяти
А0000 .-----------------------------------------
или вершина облас-| COMMAND.COM | ^ ^
ти пользователя |-----------------------| | |
| | | |
| Нерезидентная область |Используется |
| программы | программами |
| | | |
|-----------------------| | |
| Резидентные программы | V |
|--------------------------------- |
| COMMAND.COM | Область
|-----------------------| пользователя
| Драйверы устройств | (максимум
|-----------------------| 640 кбайт)
| Буферы MS-DOS | |
10000 - 14000 |-----------------------| |
| | |
| Ядро MS-DOS | |
| | |
08000 - 0A000 |-----------------------| |
| Интерфейс BIOS | |
00040 |-----------------------| |
| Векторы прерываний | V
00000 -----------------------------------------
Рис.3-2. Использование памяти пользователя MS/PC-DOS
Нерезидентная часть обрабатывает внутренние команды MS-DOS (DIR,
COPY, SET и др.) и содержит загрузчик программ. Он используется
для загрузки программ либо при обращении к COMMAND.COM (в ответ
на внешние команды) или по запросу программы пользователя. Позд-
нее в этой главе будет показано, как одна программа может исполь-
зовать эту возможность для загрузки других программ или перекры-
тия программ.
Раздел Рис.3-2, помеченный как "Резидентные программы", со-
держит резидентные программы, завершаемые и оставляемые в памяти
(TSR), такие как, например, программа "Borland's Sidekick". Раз-
мещение памяти, показанное на Рис.3-2, применяется для TSR, заг-
ружаемых из файла AUTOEXEC.BAT, или непосредственно при инициали-
зации системы. В главе 4 программы TSR рассматриваются более под-
робно.
Раздел "Драйверы устройств" относится к устанавливаемым драй-
верам устройств, т.е. к тем драйверам, которые указываются с по-
мощью команды DEVICE = в файле CONFIG.SYS. Устанавливаемые драй-
веры устройств являются предметом рассмотрения главы 6. Драйверы
устройств, назначаемые по умолчанию и применяемые в системе, при-
ведены в разделе "Интерфейс BIOS", где они используются во время
загрузки или инициализации системы MS-DOS.
"Ядро MS-DOS" - это раздел MS-DOS, который обрабатывает раз-
личные функции MS-DOS, такие, например, как функция прерывания
21h. Этот раздел является "мостом" между программами пользователя
или COMMAND.COM и различными драйверами устройств, а также аппа-
ратными средствами.
Раздел "Векторы прерываний" содержит описание 256 векторов
прерываний системы.
Оставшаяся область - это "Нерезидентная область программы"
- 3-5 -
(TPA). (Название TPA восходит ко временам операционной системы
CP/M - прародительнице MS-DOS). Эта область, куда загружаются
программы пользователя, и которой мы и будем заниматься далее.
В некотором смысле Рис.3-2 не совсем точно отражает действи-
тельную ситуацию. Не все элементы, показанные на нем, имеют свой
собственный блок памяти,и, наоборот, имеются элементы, которые не
показаны на Рис.3-2, но имеют свои отдельные блоки памяти. Рас-
смотрим сначала более подробно, каким методом пользуется MS-DOS
для организации своих собственных областей TPA.
Цепочки памяти MS-DOS
Управление памятью MS-DOS начинается при загрузке MS-DOS.
Все блоки памяти MS-DOS либо свободны, либо распределены, начи-
ная с блока управления памятью (MCB - memory control block). Эти
блоки управления, показанные на Рис.3-3, идентифицируют тип и
размер блока памяти и программу (или процесс), которая владеет
им.
Два типа блоков управления памятью являются цепочками блоков,
тип которого есть 4Dh, и конечный блок цепочки - тип 5Ah. Тип
блока хранится в первом байте блока MCB.
Следующие два байта в блоке MCB являются словом, которое
идентифицирует владельца блока памяти. Значение нуль указывает,
что блок нераспределен или свободен. Если поле владельца ненуле-
вое, то это значит, что блок распределен. Это слово содержит
идентификатор владельца процесса (PID - process identifier). PID
для процесса пользователя получается от адреса сегмента, взятого
из сегмента программного префикса (PSP - program segment prefix)
данного процесса.
Четвертый и пятый байты в MCB являются словом, которое содер-
жит размер блока памяти, следующего за этим блоком. Этот размер
выражается в параграфах (блоки по 16 байт) и не включает размер
самого блока MCB. Оставшиеся 11 байтов MCB не определены.
Несмотря на то, что полный список блоков управления часто от-
носят к цепочке распределения памяти, блоки MCB, на самом деле,
не редактируются вместе, MCB только указывает на распределенный
блок памяти. Вернее, каждый блок MCB непосредственно следует в
памяти перед блоком, которым он управляет. Если MCB и соответс-
твующий ему блок памяти не последние в цепочке, то за ними непос-
редственно следуют другой MCB и блок памяти.
Начиная от данного MCB, адрес сегмента следующего MCB в це-
почке получается путем сложения размера (в параграфах) текущего
блока с текущим адресом сегмента MCB, плюс 1. При этом способе
может быть просмотрена вся цепочка MCB, но только в прямом нап-
равлении. Начиная от данного MCB, можно определить адрес предыду-
щего MCB. Как затем можно узнать, какие блоки находятся в памяти?
Функция MS-DOS с номером 52h (прерывание int 21h) является
недокументируемой функцией, которая возвращает указатель на спи-
сок внутренних значений MS-DOS. Указатель возвращается в паре
ES:BX. Как раз перед этим списком в слове, указываемом значением
ES:[BX - 2], находится адрес первого MCB. Из этой начальной точки
может быть определена вся цепочка MCB.
Эти способы использованы в программе SHOWMEM (отобразить па-
мять), приведенной в листинге 3-1.
- 3-6 -
Адрес Тип Владелец Размер
0А00:0.---------------------------------------.
| 4D | 0008 | 1600 | |
0A01:0|---------------------------------------|
| |
| Распределенный блок, владельцем кото- |
| рого является MS-DOS |
2001:0|---------------------------------------|
| 4D | 2013 | 0010 | |
2002:0|---------------------------------------|
| |
| Распределенный блок, владельцем кото- |
| рого является процесс 2013 |
2012:0|---------------------------------------|
| 4D | 2013 | 0500 | |
2013:0|---------------------------------------|
| |
| Распределенный блок, владельцем кото- |
| рого является процесс 2013 |
2513:0|---------------------------------------|
| 5A | 0000 | 7AEC | |
2514:0|---------------------------------------|
| |
| Cвободный блок (владелец - MS-DOS). |
|Содержит остаток в верхней части памяти|
| |
9FFF:F ---------------------------------------
Рис. 3-3. Блоки управления памятью MS-DOS
Листинг 3-1 содержит как исходный файл SHOWMEM.ASM, так и заголо-
вок файла PCP.INC (который мы рассмотрим несколько подробней).
Рис.3-4 изображает результаты работы программы SHOWMEM. Подпрог-
рамма ShowMCBInfo в программе SHOWMEM.ASM отображает содержимое
блока MCB. Процедура main содержит коды для размещения начального
блока и, после метки show_mem, вычислительные операции для нахож-
дения следующего блока в цепочке. Дополнительные коды в подпрог-
рамме ShowMCBOwner могут совсем не иметь смысла. Эти коды исполь-
зуются для отображения имени процесса, который владеет блоком па-
мяти, и поясняются в следующих разделах.
Рассмотрев Рис.3-4, можно усвоить несколько интересных момен-
тов. В частности, можно увидеть, что автор программы загрузил в
память три резидентные программы: RETRIEVE (восстановление), MODE
(режим) и SWITCH (переключатель). На Рис.3-4 можно также увидеть,
что программа SHOWMEM имеет очень большой выделенный для нее блок
памяти - 555 Кбайт! Кроме того, можно также увидеть, что каждая
загруженная программа имеет два распределенных для нее блока па-
мяти. Вот это последнее обстоятельство мы первым и объясним.
- 3-7 -
1 SM-ShowMem, Version 1.00 c Copyright 1988
2 MCB Size Owner Command Line
-----------------------------------------------------------
0A01 08D7 0008 DOS
12D9 00D3 12DA [ SHELL ]
13AD 0003 0000 [ available ]
13B1 0032 12DA [ SHELL ]
13E4 0004 13EA c:\bin\RETRIEVE.COM
13E9 00A9 13EA c:\bin\RETRIEVE.COM
1493 000F 14A4 S:\MODE.COM\*
14A3 0017 14A4 S:\MODE.COM\*
14BB 0010 14CD c:\ws2000\SWITCH.COM
14CC 0018 14CD c:\ws2000\SWITCH.COM
14E5 0011 14F8 c:\GUIDE\EXAMPLES\SHOWMEM.EXE
14F7 8B08 14F8 c:\GUIDE\EXAMPLES\SHOWMEM.EXE
<<<------------- End of Memory Block List ------------->>> 3
Рис. 3-4. Пример отображения результатов работы программы
SHOWMEM:
1 - программа показа памяти - ShowMem, версия 1.00, авторское
право 1988; 2 - блок управления памятью, размер, владелец, ко-
мандная строка; 3 - конец списка блоков памяти.
Листинг 3-1. SHOWMEM - программа отображения блоков памяти MS-DOS
----------------------------------------------------------------
SHOWMEM.ASM
PAGE 60,132
; **** SHOWMEM *************************************************
; ShowMem - Отображение блоков управления памятью, отредактиро-
; ванных MS-DOS
; Этот файл создает программу SM.EXE
;
;***** ВКЛЮЧЕНИЯ ИЛИ ЭКВИВАЛЕНТЫ *******************************
;
INCLUDE stdmac.inc
INCLUDE psp.inc
;
BlocMCB EQU 4Dh ; тип цепочечного MCB
LastMCB EQU 5Ah ; тип последнего MCB
FreeMCB EQU 0000h ; владелец свободного MCB
;
NameSig EQU 0001h ; сигнатура имени процесса
;
; **** КОМПОНЕНТЫ СЕГМЕНТОВ DGROUP (ДАННЫЕ) ********************
;
_DATA SEGMENT BYTE PUBLIC 'DATA'
_DATA ENDS
;
STACK SEGMENT PARA STACK
dw 1024 dup (?) ; стек 2 Кбайт
STACK ENDS
;
DGROUP GROUP _DATA, STACK
- 3-8 -
;
;**** ПАМЯТЬ ДАННЫХ ИЛИ ШАБЛОНЫ ********************************
;
_DATA SEGMENT BYTE PUBLIC 'DATA'
;
; Текстовые сообщения для отображения формата в виде:
;
; "MCB Size Owner Coommand Line"
; "------------------------------------------------------------"
; "xxxx xxxx xxxx cccccccc..."
; "<<<--------------- End of Memory Block List ------------->>>"
;
$Title db CR,LF
db 'SM-ShowMem, Version 1.00, c Copyright 1988'
db CR,LF,CR,LF
db 'MCB Size Owner Command Linr'
db '--------------------------------------------'
db '----------------'
db CR,LF,'$'
$Space db ' $'
$Free db '[ available ]$'
$DOS db 'DOS$'
$shell db '[ SHELL ]$'
$MCBad db CR,LF
db '********** Error in MCB Chains : Aborting List'
db ' **********'
$End db CR,LF
db '<<< ************ End of Memory Block List'
db ' ------------ >>>'
$Crlf db CR,LF,'$'
;
; Шаблоны структур
mcb STRUC ; структура блока управления памятью
TypeMCB db ? ; тип блока
OwnerMCB dw ? ; владелец блока
SizeMCB dw ? ; размер блока
mcb ENDS
;
_DATA ENDS
;
; **** Здесь начинается код программы **************************
;
_TEXT SEGMENT byte public 'code'
ASSUME cs:_TEXT, ds:DGROUP, es:DGROUP, ss:DGROUP
;
EXTRN bin2hex:NEAR ; шестнадцатиричное отображение
main PROC FAR
mov ax,DGROUP ; установка сегмента данных
mov ds,ax
;
; Отображение заголовка для списка блоков памяти
@DisStr $Title
;
; Нахождение начала очереди блоков памяти
mov ah,52h ; получение параметров DOS
int 21h ; возврат указателя в ES:BX
sub bx,2 ; указывает на 1-й адрес MCB
- 3-9 -
mov ax,word ptr es:[bx] ; получение начального блока
mov es,ax
xor di,di ; очистка индекса
cmp byte ptr es:[di].TypeMCB,BlocMCB
jne bad_chain ; выход, если не начало цепочки
;
; Цикл для нахождения и отображения каждого блока памяти
show_mem:
call ShowMCBInfo ; дамп содержимого MCB
cmp byte ptr es:[di].TypeMCB,LastMCB
je done ; выход, если конец цепочки
mov ax,es ; вычисление следующего адреса
add ax,es:[di].SizeMCB ; добавление размера блока
inc ax ; плюс 1 для себя
mov es,ax ; начало нового блока
cmp byte ptr es:[di].TypeMCB,LastMCB
je show_mem ; продолжение, если правильный тип
cmp byte ptr es:[di].TypeMCB,BlocMCB
je show_mem ; продолжение, если правильный тип
;
bad_chain: ; ошибка в MCB "chains"
@DisStr $MCBad ; завершающее сообщение
@DisStr $Crlf
mov al,1 ; завершение без ошибки
@ExitToDOS ; завершение программы
;
done: @DisStr $End ; завершающее сообщение
@DisStr $Crlf
mov al,0 ; нормальное завершение
@ExitToDOS ; завершение программы
;
main ENDP
;
; **** ShowMCBInfo *********************************************
; ShowMCBInfo отображает блоки, адресуемые с помощью ES:DI как
; блоки управления памятью MS-DOS. Формат отображения показан
; выше.
;
ShowMCBInfo PROC NEAR
mov ch,04 ; отображение числовых данных
mov ax,es ; адрес MCB
call bin2hex
@DisStr $Space
mov ax,es:[DI].SizeMCB ; связанный блок
call bin2hex
@DisStr $Space
mov ax,es:[DI].OwnerMCB ; владелец
push ax ; сохранение владельца
call bin2hex
@DisStr $Space
pop ax
cmp ax,FreeMCB ; блок освобожден?
je is_free ; да, выполнять имя не надо
call ShowMCBOwner ; нет, отображение владельца
jmp Info_Exit
;
- 3-10 -
is_free:
@DisStr $Free ; отметить блок как свободный
Info_exit:
@DisStr $Crlf
ret
ShowMCBInfo ENDP
;
; **** ShowMCBOwner ********************************************
; ShowMCBOwner извлекает и отображает владельца MCB DOS из
; соответствующей строки среды. ES:DI указывает на допустимый
; MCB с ненулевым полем владельца.
;
ShowMCBOwner PROC NEAR
push es ; сохранение адреса MCB
push di ; сохранение для приборки
;
; Получение PID (адреса PSP), который владеет этим блоком памяти
mov ax,es:[di].OwnerMCB ; адрес PSP владельца
mov es,ax
cmp es:[di].PSPExitInt,PSPSignature ; допустимый PSP?
je Owner_PID ; да, владелец имеет PID
;
; Без PSP владелец должен быть ядром DOS
Owner_DOS:
@DisStr $DOS ; владелец MS-DOS
jmp Owner_Exit ; все выполнено
;
; Извлечение сегмента среды процесса из PSP
Owner_PID:
mov ax,es:[di].PSPEnvironment ; да, получ. адр.среды
push ax ; сохранение сегмента среды
;
; Получение размера сегмента среды
dec ax ; MCB среды
mov es,ax
mov cx,es:[di].SizeMCB ; получение размера среды
shl cx,1 ; преобразование параграфов
shl cx,1 ; в байты
shl cx,1
shl cx,1
;
; Продолжение поиска имени процесса по ES:DI, длине CX
; Каждая переменная среды завершается нулевым байтом.
; Список переменных завершается другим нулевым байтом
cld ; поиск вперед
pop es ; восстановление среды
xor al,al ; поиск значения
search:
repne scasb ; поиск для ASCIIZ
jne Owner_DOS ; останов, если выход за границу
scasb ; конец списка строк
jne search ; продолжить, если больше
;
; Проверка наличия "Сигнатуры", продолжающей (возможные) имена
mov si,di ; передача в SI
push ds ; сохранение сегмента строк
push es ; передача ES в DS
pop ds
- 3-11 -
lodsw ; чтение предшествующего слова
cmp al,NameSig ; проверка действительного имени
je show_name ; имя допустимое
;
; Без действительного имени владелец должен иметь имя SHELL
pop ds
@DisStr $Shell ; владелец имеет имя shell
jmp Owner_Exit
; ES:DI указывает на допустимое (0 завершенное) имя процесса
show_name:
lodsb ; чтение символа,
cmp al,0 ; одновременно проверка
je Owner_POP ; окончания, и
@DisStr al ; отображение
loop show_name
Owner_Pop:
pop ds
Owner_Exit:
pop di
pop es
ret
ShowMCBOwner ENDP
; ***** КОНЕЦ ПРОГРАММЫ : КОНЕЦ ФАЙЛА **************************
;
_TEXT ENDS
END main
; PSP.INC
;***************************************************************
; ФАЙЛ ВКЛЮЧЕНИЯ ОПИСАНИЙ PSP
;***************************************************************
;
PSPSignature EQU 020cdh ; слово, начинающее все PSP
;
ProgramSegmentPrefix STRUC
PSPExitInt dw ? ; прерывание выход int 20h
PSPMemTot dw ? ; вершина памяти
PSPResvr1 db ?
PSODOSCall db 5 dup (?) ; вызов MS-DOS
PSPTerminate db ? ; адрес завершения
PSPControlC dd ? ; адрес control-C
PSPCritical dd ? ; адрес критической ошибки
PSPParent dw ? ; владелец PSP
PSPHandleTable db 20 dup (?);таблица описателей по умолчанию
PSPEnvironment dw ? ; адрес среды
PSPStack dd ? ; начальные значения стека
PSPHandleSize dw ? ; размер таблицы описателей
PSPHandlePntr dd ? ; адрес таблицы описателей
PSPResvr2 db 24 dup (?)
PSPDOSInt db 3 dup (?) ; прерывание 21h или возврат
PSPResvr3 db 9 dup (?)
PSPFCB1 db 16 dup (?) ; блок управления файлом
PSPFCB2 db 16 dup (?) ; блок управления файлом
PSPResvr4 db 4 dup (?)
PSPCommandLen db 1 ; длина командной строки
PSPCommandBuf db 127 dup (?) ; текст командной строки
ProgramSegmentPrefix ENDS
________________________________________________________________
- 3-12 -
Блок операционной среды программы
При загрузке программы в память MS-DOS всегда присоединяет в
начало программы блок операционной среды (далее просто "среда"),
запоминая в нем владельца блока памяти. На Рис.3-4 он показан как
первый небольшой блок, который соотносится с каждой программой.
Блок среды программы содержит собственную копию операционной сре-
ды MS-DOS. Среда MS-DOS представляет собой область, в которой
запоминаются PATH (путь), COMSPEC (спецификация файла
COMMAND.COM) и PROMPT (приглашения) вместе с любыми переменными,
назначаемыми по команде SET (установить). Общей формой переменной
среды является: NAME = строка . Формат блока среды приведен в
примере, показанном на Рис.3-5.
Из Рис.3-5 можно видеть, что каждый элемент в блоке среды
представляет собой строку в коде ASCII (American Standard Code
for Information Interchange - Американский стандартный код для
обмена информацией), завершаемую нулевым байтом. (Этот стандарт
фирмой "Майкрософт" назван ASCIIZ). Весь список элементов закан-
чивается еще одним нулевым байтом, показанным на Рис.3-5 в ка-
честве седьмого элемента. Элементы, предшествующие этому маркеру
"конец списка", отображаются всякий раз при использовании команды
SET. А что из себя представляют два элемента, следующие после
маркера "конец списка"?
Недокументируемая возможность MS-DOS версии 3 и последующих
версий состоит в том, что всякий раз при запуске процесса с по-
мощью COMMAND.COM либо непосредственно, либо в ответ на функцию
EXEC (выполнить), имя процесса помещается в блок среды процесса.
На Рис.3-5 последние два элемента перед частью "не используемая"
являются этим недокументируемым именем процесса. Имени процесса
предшествует слово 0001h. Имя содержит собственное имя и путь
процесса и запоминается в формате ASCIIZ. Из Рис.3-5 можно уви-
деть, что этот блок среды относится к процессу SHOWMEM.
Одним из элементов, не представленных на Рис.3-5, является
общий размер блока среды. В отличие от основной среды MS-DOS, чей
размер может управляться с помощью параметра, устанавливаемого в
файле CONFIG.SYS, размер блока среды процесса определяется во
время загрузки программы путем помещения в него только текущей
части среды.
Сравните в примере отображения SHOWMEM на Рис.3-4 800-байтный
размер среды DOS (второй элемент с именем "SHELL") со средами
RETRIEVE и SHOWMEM в 64 и 272 байта, соответственно. Несмотря на
то, что DOS должна резервировать 800 байт, при загрузке RETRIEVE
впереди файла AUTOEXEC.BAT среда содержит меньше, чем 64 байта.
После завершения файлом AUTOEXEC.BAT установок PATH и PROMPT и из-
менения других переменных, среда должна увеличиться приблизитель-
но до 200 байт.
Имеются две причины получения собственного блока среды каждым
процессом при его создании. Первая - это уменьшение вероятности
того, что процесс будет испорчен средой его владельца - критичес-
кое требование, если владелец процесса - файл COMMAND.COM. Вторая
связана с тем, что процесс-владелец должен управлять средой, пе-
редаваемой порождаемому процессу, что, в свою очередь, позволяет
процессу-владельцу управлять поведением порожденного процесса. К
этой теме мы вернемся снова, когда будем рассматривать загрузку и
выполнение программы. Мы также вернемся к нерешенному вопросу
большого размера блока памяти SHOWMEM. Не забудьте эту проблему.
- 3-13 -
.------------------------------.
| COMSPEC = C:\COMMAND.COM\* | 0 |
|----------------------------------------------.
| INCLUDE = C:\msc\*include;c:\masm\*include\* | 0 |
|-----------------------------------------------
| LIB = c:\msc\*lib | 0 |
|-----------------------
| ECHO = OFF | 0 |
|-------------------.
| PROMPT = $p$g | 0 |
|-------------------------------------------------------------.
|PATH = C:\DOS;C:\BIN;C:\BAT;C:\UTILS;C:\MASM\*;C:\WIN;C:\WS| 0 |
|--------------------------------------------------------------
| 0 |
|-------.
| 0001h |
|-----------------------------------.
| C:\GUIDE\EXAMPLES|SHOWMEM.EXE | 0 |
|------------------------------------
| Не используемая |
| |
|/\/\/\/\/\/\/\/\/\|
Рис.3-5. Блок среды
когда мы вернемся к ней после накопления основных сведений по
рассматриваемым вопросам.
Процессы MS-DOS
Мы начали эту главу с описания того, как все пространство па-
мяти системы MS-DOS форматируется на разделы для MS-DOS, BIOS и
системных функций аппаратных средств. Затем мы видели, как раз-
дел, управляемый MS-DOS, организуется в различные области, вклю-
чая нерезидентную область программ или TPA (transient program
area). Мы также увидели, как TPA управляется посредством использо-
вания блоков управления памятью, и что каждый процесс включает
два блока памяти: блока среды и блока, который мы будем называть
"блок процесса". Теперь мы можем перейти к обзору блока процесса
и рассмотреть отдельные компоненты, которые включает в себя про-
цесс MS-DOS.
Контекст процесса MS-DOS
Рис.2-3 в главе 2 дает нам представление о внутренней струк-
туре процесса MS-DOS для процессов типа .EXE и .COM. Теперь мы
можем объединить это с тем, что уже изучили, для представления в
памяти более детального образа процесса MS-DOS. Это новое предс-
тавление показано на Рис. 3-6.
На Рис.3-6 имеется много деталей, которые нам необходимо
рассмотреть. Мы начнем с сегмента программного префикса или PSP
(program segment prefix).
Сегмент программного префикса
Сегмент программного префикса (PSP), введенный в главе 2, яв-
ляется в некотором смысле "краеугольным камнем" процесса MS-DOS.
- 3-14 -
Адрес сегмента PSP обеспечивает идентификатор процесса и служит в
качестве идентификатора блока памяти процесса. Устанавливаемый
всегда в начале блока процесса PSP также служит в качестве "хра-
нилища" для большого количества ценной информации.
В данном документе PSP представлен в трех представлениях: как
графическое изображение на Рис.3-7; как подробное описание в таб-
лице 3-1 и, наконец, как описание структуры макроассемблера MASM
STRUC в PSP.INC, приведенной в листинге 3-1. Рисунок дает возмож-
ность быстрого размещения информации, таблица обеспечивает глуби-
ну информации, а листинг показывает смещения, необходимые при ис-
пользовании в программах пользователя.
Даже беглый взгляд на Рис.3-7 и таблицу 3-1 открывает обилие
информации, которая может быть полезна программисту. Однако, не-
которые элементы PSP требуют гораздо большего пояснения.
Процесс .COM Нижние адреса памяти Процесс .EXE
|\/\/\/\/\/\/\/\/\/| |\/\/\/\/\/\/\/\/\/|
|COMMAND или преды-| Предыдущий блок |COMMAND или преды-|
| дущей программы | | дущей программы |
------------------ ------------------
.------------------. .------------------.
|| 4D/PSP/size/ || MCB среды || 4D/PSP/size/ ||
||----------------|| ||----------------||
|| имя = строка || блок среды || имя = строка ||
------------------ ------------------
.------------------. .------------------.
|| 5A/PSP/size/ || MCB процесса || 4D/PSP/size/ ||
||----------------|| ||----------------||
|| PSP программы || блок процесса || PSP программы ||
|| - - - - - - - -|| || - - - - - - - -||
|| Код программы || || Код программы ||
|| и данные || || - - - - - - - -||
|| - - - - - - - -|| || Стек или данные||
|| || ------------------
|| || .------------------.
||"Ни себе, ни лю-|| свободный MCB | 5A/0000/size/ |
|| дям" || |------------------|
|| || | |
|| - - - - - - - -|| | |
|| Стек || | Доступна для |
||----------------|| | использования |
|| Недоступна для || Неиспользуемая память | |
|| использования || | |
|/\/\/\/\/\/\/\/\/\| |/\/\/\/\/\/\/\/\/\|
Верхние адреса памяти
-------
|| || - Память, распределяемая/владеющая процессом
-------
Рис.3-6. Контекст процесса MS-DOS в памяти
Адреса завершения PSP
Таблица 3-1 показывает три "адреса завершения", хранимые в
байтах от 0Ah до 15h PSP. Как уже объяснялось выше, эти копии:
- 3-15 -
адреса завершения программы, адреса выхода по нажатию клавиш
Control-Break и адреса выхода по критической ошибке выбираются из
действительных векторов прерываний, размещаемых в int 22h,int 23h
и int 24h. Чтобы воздействовать на поведение системы во время си-
туации завершения (такой как, например, выход по внутреннему пре-
рыванию CONTROL-BREAK/CONTROL-C) программисту требуется изменить
основные векторы прерываний. Это можно сделать, используя для по-
лучения и изменения этих адресов функции "Установить вектор" (Set
Vector - код 25h) и "Получить вектор" (Get Vector - код 35h).
Таблица описателей файлов PSP
С обработкой файлов в сегменте программного префикса связаны
три "недокументируемых" элемента: адрес таблицы описателей, указа-
тель описателей и счетчик описателей. Как увидим потом, эти эле-
менты являются относительными.
Адрес таблицы описателей содержит указатель длины на таблицу,
шириной в несколько байт в памяти, размер которой задается счетчи-
ком описателей. Каждый байтовый элемент этой таблицы является ин-
дикатором описателей, которая может быть открыта для файла или
устройства.
Таблица 3-1
Содержимое сегмента программного префикса
________________________________________________________________
Шестнадцатиричные|
-----------------| Содержимое
смещение| размер |
________|________|______________________________________________
00 | 2 | Прерывание int 20h. Содержит инструкцию пре-
| | рывания int 20h (байты CD 20 - шестнадцати-
| | ричные значения). Устаревшее использование.
| | Программист вместо завершения должен исполь-
| | зовать функцию 4Ch, прерывание int 21h.
________|________|______________________________________________
02 | 2 | Вершина памяти. Содержит адрес сегмента, сле-
| | дующего за памятью программы. Это может быть
| | адрес старой памяти DOS (такой как А000) или
| | адрес следующего доступного блока управления
| | памятью.
________|________|______________________________________________
04 | 1 | Зарезервирован.
________|________|______________________________________________
05 | 5 | Длинный вызов диспетчера функций MS-DOS. Со-
| | держит длинный переход к диспетчеру функций
| | MS-DOS для использования с программами типа
| | CP/M. Устаревшее использование. Программы
| | должны вместо вызова MS-DOS использовать пре-
| | рывание int 21h.
________|________|______________________________________________
06 | 2 | Доступная память. Часть смещения длинного вы-
| | зова также содержит количество байтов, дос-
| | тупных в кодовом сегменте программы.
________|________|______________________________________________
0A | 4 | Адрес завершения программы. Копия адреса пре-
| | рывания int 22h (IP,CS), по которому переда-
- 3-16 -
________________________________________________________________
Шестнадцатиричные|
-----------------| Содержимое
смещение| размер |
________|________|______________________________________________
| | ется управление, когда вводятся Control-Break
| | или Control-C.
________|________|______________________________________________
0E | 4 | Адрес выхода Control-Break. Копия адреса пре-
| | рывания int 23h (IP,CS), по которому переда-
| | ется управление, когда вводятся Control-Break
| | или Control-C.
12 | 4 | Адрес выхода по критической ошибке. Копия ад-
| | реса прерывания int 24h (IP,CS), по которому
| | передается управление, когда во время обра-
| | ботки обнаруживается критическая ошибка.
________|________|______________________________________________
16 | 2 | Префикс программного сегмента владельца. Это
| | сегментный адрес сегмента программного префи-
| | кса владельца. Для процессов, не имеющих вла-
| | дельца, это адрес текущего PSP.
________|________|______________________________________________
18 | 14 | Таблица описателей файла. Содержит 20 одиноч-
| | ных байтов "обработки" (индикаторов) в табли-
| | це файлов системы. Первыми 5 из них являются:
| | STDIN, STDOUT, STDERR, AUXIO и LSTOUT. Смотри
| | текст для более подробного объяснения.
________|________|______________________________________________
2C | 2 | Адрес среды. Адрес сегмента блока среды про-
| | цесса.
________|________|______________________________________________
2E | 4 | Память переключателя стека. Используется для
| | хранения стекового сегмента процесса и указа-
| | теля (SS:SP), когда процесс выполняет опера-
| | ции в стеке MS-DOS.
________|________|______________________________________________
32 | 2 | Счетчик описателей. Максимальное количество
| | элементов, допускаемое в таблице описателей
| | файла. По умолчанию принимается значение 20.
________|________|______________________________________________
34 | 4 | Адрес таблицы описателей.Длинный указатель на
| | таблицу описателей файла.По умолчанию в теку-
| | щем PSP принимается значение смещения 18 (ше-
| | стнадцатиричное значение).
_______|_______________________________________________________
38 | 18 | Зарезервировано.
________|________|______________________________________________
50 | 3 | Прерывание диспетчера функций. Содержит код
| | для прерывания int 21h вызова диспетчера фун-
| | кций MS-DOS, следующего по выходу far RET.
________|________|______________________________________________
53 | 2 | Зарезервировано.
________|________|______________________________________________
55 | 7 | Расширение блока управления файлом. Поля рас-
| | ширения для блока #1 управления файлом. Уста-
| | ревшее использование. Программы должны ис-
| | пользовать вместо описателя файла. Для полу-
| | чения более подробной информации по FCB (File
- 3-17 -
________________________________________________________________
Шестнадцатиричные|
-----------------| Содержимое
смещение| размер |
________|________|______________________________________________
| | control block - блок управления файлом) обра-
| | титесь к руководствам по MS-DOS).
________|________|______________________________________________
5C | 10 | Блок управления файлом номер 1. Содержит не-
| | открытый блок FCB #1. Устаревшее использова-
| | ние и в результате может привести к разруше-
| | нию FCB #2 и длины командной строки. Пути
| | имен файлов не поддерживаются. Вместо этого
| | программы должны использовать описатели фай-
| | лов. Для получения более подробной информации
| | по FCB, обратитесь к руководствам по MS-DOS.
________|________|______________________________________________
6C | 10 | Блок управления файлом номер 2. Содержит не-
| | открытый блок FCB #2. Устаревшее использова-
| | ние и в результате может привести к разруше-
| | нию параметров командной строки. Вместо это-
| | го программы должны использовать описатели
| | файлов. Для получения более подробной инфор-
| | мации по FCB, обратитесь к руководствам по
| | MS-DOS.
________|________|______________________________________________
7C | 4 | Зарезервировано.
________|________|______________________________________________
80 | 80 | Дисковая область передачи, назначаемая по
| | умолчанию. Перекрывает при использовании
| | строку текста командной строки.
________|________|______________________________________________
80 | 1 | Длина командной строки. Длина текстовой стро-
| | ки, которая была набрана следом за именем
| | программы, минус любые переназначенные симво-
| | лы или параметры.
________|________|______________________________________________
81 | 7F | Буфер командной строки. Текстовая строка, ко-
| | торая была введена следом за именем программы.
| | Символы переназначения (< и >) и их соответст-
| | вующие имена файлов в этой области не появля-
| | ются, т.к. переназначение прозрачно для прик-
| | ладной программы.
________|________|______________________________________________
Открытые описатели сохраняют свои показания в таблице файлов
системы. Неиспользуемые элементы в таблице помечаются шестнадцати-
ричным значением 0FF. Первые пять обработок в таблице описателей
файлов зарезервированы за стандартными устройствами: STDIN (стан-
дартный ввод), STDOUT (стандартный вывод), STDERR (стандартная
ошибка), AUXIO (вспомогательный ввод-вывод) и LSTOUT (стандартный
вывод на печать) и открываются при запуске процесса. Все показания
отсчитываются от первоначального нулевого значения.
Рис.3-8 показывает состояние таблицы описателя файла, приня-
той по умолчанию, сразу же после успешного открытия файла myfile
(мой файл). Таблица описателя файла, принимаемая по умолчанию,
является двадцатибайтовой таблицей, размещенной в PSP по смеще-
нию 18 (шестнадцатиричное значение). Этот адрес запоминается в
- 3-18 -
адресе таблицы описателя при запуске процесса. В связи с тем,
что первые пять обработок зарезервированы за стандартными уст-
ройствами, остается только 15 обработок, доступных для файлов или
других устройств.
.00h-----------02h-------------------05h-----------------------.
|int 20h |Вершина памяти | 00 | Далекий вызов MS-DOS |
-------------|0Ah----------------------0Eh--------------------|
|Адрес завершения |Адрес выхода Ctrl-Break|
|12h---------------------|16h--------------------
|Адр.вых. по крит. ошибке|PSP владельца|
.18h-------------------------------------------------|
|Таблица описателя файла |
|----------------------------------------------------|
|Таблица описателя файла (продолжение) |
|-------------------------------2Ch-----2Eh--------------------.
|Таблица описателя файла(конец)|Среда |Начальный адрес стека |
--------------32h-------------|34h----------------------------
|Счетчик описат. |Указатель таб.описат.|
.38h-------------------------------------------------|
|Зарезервированная область (длиной 40 байт) |
----------------------------------------------------
.50h-----------------53h----------55h----------------.
|Функция int 21h |Зарезерв. |Расширение FCB |
|----------------------------5Ch---------------------|
|Расширение FCB(продолжение)|Блок управл-я файлом #1 |
|----------------------------------------------------|
|Блок управления файлом #1 (продолжение) |
|----------------------------6Ch---------------------|
|Блок управления файлом #1 |Блок управл-я файлом #2 |
|----------------------------------------------------|
|Блок управления файлом #2 (продолжение) |
|----------------------------7Ch---------------------|
|Блок управления файлом #2 |Зарезервированная обл. |
|80Ch-81Ch-------------------------------------------|
|Дл. |Буфер команд (длиной 127 байт) |
----------------------------------------------------
Рис.3-7. Структура PSP
На Рис.3-8 значение описателя, возвращаемое при успешном вы-
полнении функции OPEN, равно 0005, которое означает, что файлу с
именем myfile назначен шестой элемент (вход) в таблице описате-
ля файлов процесса. При обращении шестой вход содержит значение
03, которое означает, что файлу myfile был назначен четвертый
вход в таблице файлов системы. Рис.3-8 также демонстрирует ис-
пользование первых трех описателей с целью показа назначения
для одного и того же входа в системной таблице файлов нескольких
обработок. Максимальное количество входов в системную таблицу
файлов устанавливается с помощью предложения FILES = в файле кон-
фигурации системы CONFIG.SYS.
- 3-19 -
.34h----------------------------. .--------------.
| Указатель таблицы описателя | | Описатель AX |
| PS Segment:0018 (шестнадц.) | | OPEN = 0005 |
------------------------------- --------------
| |
-------------------------------
|
0 1 2 3 4 5 v 6 7
.-------------------------------------------------------.
|18h | | | | |Таб.описател.файлов |
| STDIN|STDOUT|STDERR| AUXIO|LSTOUT|myfile|(не использ.)|
| 01 | 01 | 01 | 00 | 02 | 03 | FF | FF |
-------------------------------------------------------
| | | | | |
|------------- | | |
|Табл. файлов системы| | |
| .--------------. | | |
| | AUX 0 |<- | |
| |--------------| | |
->| CON 1 | | |
|--------------| | |
| PRN 2 |<-------- |
|--------------| |
| MYFILE 3 |<---------------
|--------------|
|Не использ.4 |
--------------
Рис.3-8. Таблица описателей файлов PSP
В большинстве ситуаций пользователю никогда нет необходимости
быть осведомленным об этих устройствах, однако, существуют две
ситуации, когда эти знания полезны.
Первая ситуация возникает, когда программа пользователя тре-
бует больше описателей, чем может быть открыто в данное время.
Так как по умолчанию таблица описателей файлов поддерживает толь-
ко 20 описателей и т.к. 5 описателей уже присвоены, то практичес-
ки невозможно так далеко все предугадать. Тем не менее, чтобы
обойти это ограничение, программа должна установить свои собс-
твенные расширенные таблицы описателей файлов, как показано во
фрагменте программы в листинге 3-2.
При второй ситуации листинг 3-2 предполагает, что для прог-
раммы использовано размещение новой таблицы и, кроме того, пред-
полагает, что таблица была предварительно загружена с кодами 0FF
(коды неиспользуемых описателей). Программа сначала определяет
ячейки PSP, используя функцию 62h. Из PSP находится размер и
ячейки существующей таблицы описателей файлов, и старая таблица
копируется в новую таблицу. Новый адрес таблицы и ее размер сох-
раняются в соответствующих полях PSP и обмен завершается.
Другой возможностью, предоставляемой этим механизмом, являет-
ся то, что программист теперь управляет переназначением ввода и
вывода программы. В MS-DOS переназначение выполняется простым из-
менением драйвера, связанного с конкретным устройством. Этот спо-
соб даже работает для переназначения ввода и вывода, выполняемого
со старыми, необрабатываемыми вызовами ввода и вывода (такими как
функция 09h "Отобразить строку").
Листинг 3-3 демонстрирует как устройство stdout (стандартный
- 3-20 -
вывод) переназначается в файл или устройство myfile (мой файл).
Программа сначала открывает имя myfile и сохраняет описатель. За-
тем она получает адрес PSP и из PSP получает адрес таблицы описа-
телей. Используя myfile в качестве индекса таблицы описателей,
программа получает индекс таблицы файлов системы myfile и запоми-
нает его в индексе, назначенном для stdout (стандартный вывод),
выполняя переназначение. Оставшаяся часть программы "поворачива-
ет" процесс и заканчивает работу закрытием описателя myfile.
Листинг 3-2. Фрагмент программы для переключения таблицы
описателей файла
----------------------------------------------------------------
; Этот листинг передает таблицу описателей файла, назначенную
; по умолчанию, в область, адрес которой указывается в ES:DI.
; Размер новой таблицы подразумевается в CX. Подразумевается
; MS-DOS версии 3.xx (для функции "Get PSP Address" - получить
; адрес PSP). Регистры AX и BX не сохраняются.
;
push ds ; сохранение DS
push si ; сохранение SI
push di ; сохранение смещения новой таблицы
push cx ; сохранение размера новой таблицы
mov ah,62h ; получение PSP
int 21h ; возврат PSP в BX
mov ds,bx ; адрес PSP
;
; Получение размера и адреса текущей таблицы
mov bx,032h ; адрес размера таблицы
mov cx,[bx] ; получение размера таблицы
push ds ; сохранение адреса PSP
lds si,[bx]2 ; получение адреса текущей таблицы
; Копирование старой таблицы из DS:DI на новое место по ES:DI
cld ; пересылка в прямом направлении
rep movsb ; пересылка таблицы на новое место
;
; Восстановление размера и положения новой таблицы и обновление
; PSP
pop ds ; восстановление адреса PSP
pop cx ; восстановление размера новой таблицы
pop di ; восстановление смещения новой таблицы
mov [bx]2,di ; запоминание смещения новой таблицы
mov [bx]4,es ; запоминание сегмента новой таблицы
mov [bx],cx ; запоминание размера новой таблицы
pop si ; восстановление первоначального SI
pop ds ; восстановление первоначального DS
----------------------------------------------------------------
Листинг 3-3. Фрагмент программы для переназначения
StdOut в файл
----------------------------------------------------------------
; Этот листинг открывает описатель файла или устройства с
; именем "myfile" и заменяет описатель StdOut вновь открытым
; описателем. Вход подразумевается с DS и ES, указывающих на
; сегмент данных. Переменные следующих данных предполагаются
; определенными:
;
StdOut equ 1 ; код для описателя StdOut
- 3-21 -
Handle dw ? ; новая переменная описателя
Outhand db ? ; переменная описателя StdOut
MyFile db 'filename.ext',0
;
; Открытие описателя для файла/устройства, находящегося в
; myfile
lea dx,MyFile ; имя
mov al,2 ; доступ чтение/запись
mov ah,03dh ; функция OPEN - открыть
int 21h
jc OpenError
mov Handle,ax ; сохранение описателя
;
; Передача описателя файла/устройства в описателю StdOut.
push es ; сохранение ES
mov ah,62h ; получение PSP
int 21h
mov es,bx ; ES указывает на PSP
les bx,es:[bx].PSPHandlePntr
;
; ES:BX теперь указывает на таблицу описателя файла
mov al,es:[bx].StdOut ; чтение описателя StdOut и
mov Outhand,al ; сохранение
mov di,Handle ; считанного индекса описателя
mov al,es:[bx+di] ; считывание входа описателя
mov es:[bx].StdOut,al ; запом-е как описателя StdOut
pop es
;
; Восстановление первоначальной описателя StdOut
push es ; сохранение ES
mov ah,62h ; получение PSP
int 21h
mov es,bx ; ES указывает на PSP
les bx,es:[bx].PSPHandlePntr
;
; ES:BX указывает теперь на таблицу описателя файла
mov al,Outhand ; считывание описателя StdOut
mov es:[bx].StdOut,al ; запоминание описателя StdOut
pop es
;
; Закрытие переназначенного файла
mov bx,Handle ; описателя для файла или устройства
mov ah,03eh ; функция CLOSE - закрыть
int 21h
----------------------------------------------------------------
SHOWMEM и указатель адреса среды PSP
Другим полезным значением, сохраняемым в PSP, является адрес
сегмента блока среды процесса. Мы не возвращались к этому входу в
связи с тем, что он требовал последующего разъяснения, но так как
теперь мы обладаем полной информацией, необходимой для понимания
всей программы SHOWMEM, включая подпрограмму ShowMCBOwner, то:
- найдите начальный блок управления памятью, используя преры-
вание int 52h;
- используйте поле владельца в блоке MCB в качестве адреса
PSP;
- проверьте PSP путем проверки первых двух байтов для преры-
вания int 20h;
- если владельцем MCB является PSP, то извлеките адрес среды.
Если PSP не является владельцем, то владельцем должна быть
MS-DOS;
- вычтите единицу из адреса сегмента среды для получения MCB
среды, и извлеките из него размер среды;
- проверьте среду на наличие двойного нуля, который сигнали-
зирует о конце строк ASCIIZ;
- проверьте процесс пользователя на наличие сигнатуры 0001.
Если сигнатура 0001 найдена, то печатайте следующее имя. Если
сигнатура 0001 отсутствует, то процесс должен быть COMMAND.COM
или эквивалентным командным процессом;
- если текущий MCB не является последним, то найдите следую-
щий MCB путем добавления размера блока (плюс 1) к адресу MCB;
- повторите выполнение со второго шага.
Программа SHOWMEM демонстрирует внутренние взаимосвязи, су-
ществующие внутри DOS и показывает как можно перейти от блока уп-
равления памятью к PSP, к блоку среды и обратно к MCB среды, вы-
бирая необходимые данные.
Функции для манипулирования PSP
MS-DOS содержит функции, относящиеся непосредственно к пре-
фиксу программного сегмента. Эти функции перечислены в таб. 3-2.
Для этих функций, которые получают и устанавливают PSP, текущий
PSP определяется MS-DOS не по программному сегменту, выполняемому
в данное время.
Например, предположим, что выполняется программа MYPROG, ког-
да получает управление установленная подпрограмма (TSR, если
угодно) и выдает вызов функции GET PSP (получить PSP - функция с
кодом 62h). В этом случае MS-DOS возвращает значение PSP для
прерванной программы MYPROG. Это происходит потому, что после то-
го как подпрограмма резидентной памяти выполнит функцию Keep
Process (сохранить процесс) или завершить и оставить резидентной,
она еще некоторое время будет считаться активной. MS-DOS считает
последнюю загруженную программу текущей активной программой.
Если важно, чтобы TSR имела доступ к своему собственному PSP,
то для этого может быть использована недокументированная функция
SET PSP (установить PSP - функция с кодом 50h). Когда TSR загру-
жается в первый раз, она должна сохранить значение своего PSP.
Затем , когда TSR позднее получит управление, PSP прерванной
программы может быть определен с помощью функции 62h (GET PSP-по-
лучить PSP). Это значение должно быть сохранено и активизирован
собственный PSP TSR с помощью функции 50h (SET PSP - установить
PSP). После выполнения TSR, она должна восстановить первоначаль-
ный PSP с помощью функции SET PSP (установить PSP).
- 3-23 -
Таблица 3-2
Функции прерываний int 21h для сегмента программого префикса
________________________________________________________________
|
Функция| Назначение
________|_______________________________________________________
26h | Создание блока PSP. Устаревшее использование
________|_______________________________________________________
50h | Установка текущего PSP. Недокументированная. BX содер-
| жит адрес сегмента действительного PSP. Эта функция
| заставляет новый PSP (BX) стать активным PSP для
| MS-DOS. Последовательные обращения к MS-DOS, ссылающи-
| еся к данным PSP, также как таблица описателя файла,
| будут использовать новый PSP.
________|_______________________________________________________
51h | Получить сегмент PSP. Недокументированная. Возвращает
| адрес текущего сегмента PSP в регистре BX. Это тоже
| самое, что и функция 62h, но она также доступна и в
| более ранних версиях MS-DOS 3.00. Ненадежна для вызова
| из TSR. Вместо этой функции рекомендуется использовать
| функцию 62h.
________|_______________________________________________________
55h | Получить копию PSP. Недокументированная. Функция почти
| идентична функции 26h. DX содержит адрес сегмента но-
| вого PSP. Однако, эта функция будет также устанавли-
| вать поле владельца нового PSP для адреса сегмента те-
| кущего PSP. Т.к. это недокументированная функция и по-
| лезна только при загрузке новой программы, то вместо
| нее рекомендуется использовать функцию EXEC (выпол-
| нить) с кодом 4Bh.
________|_______________________________________________________
62h | Получить текущий PSP. MS-DOS версии 3.00 и последующие
| версии. Возвращает адрес сегмента текущего PSP в ре-
| гистре BX.
________|_______________________________________________________
Файлы процессов MS-DOS: .EXE в сравнении с .COM
Как известно, в MS-DOS файлы исполнимой программы могут быть
в двух вариантах: файлы типа .COM и файлы типа .EXE. Рисунки 2-3
(в главе 2) и 3-6 иллюстрируют некоторые различия между этими
двумя типами файлов. Для MS-DOS различия проявляются в других
формах.
Тип файла .EXE - это в действительности "естественный" режим
файла в MS-DOS. Средства языков программирования и системы MS-DOS
предназначены для работы с этим типом файлов. Тип файлов .COM
первоначально был создан для совместимости с процессами операци-
онной системы CP/M, но этот тип не похож на вымирающий. Даже и
сегодня в версии MS-DOS файлы типа .COM являются упрощенной узкой
разновидностью файлов типа .EXE. С некоторой гибкостью файлы типа
.EXE заменяются назначаемым по умолчанию форматом.COM. В резуль-
тате такого упрощения файлы типа .COM загружаются гораздо быст-
рее, но различие скоростей тривиально для современных машин.
После образования процесса макроассемблер MASM не знает и не
заботится о том, чтобы знать, какой тип файла ассемблируется. Во
время компоновки компоновщик LINK обнаружит, что файлы формата
.COM не имеют стекового сегмента, но при этом компоновщик не бу-
дет выражать свое неудовольствие. Вот, когда выполняется функция
- 3-24 -
EXE2BIN для преобразования файлов типа .EXE в файлы типа .COM,
различия в файлах начинают обнаруживаться.
Все объектные файлы, вырабатываемые макроассемблером MASM, и
файлы типа .EXE, создаваемые компоновщиком LINK, могут содержать
настраиваемые (переместимые) ссылки сегмента. Эти файлы содержат
таблицы, которые включают списки, где в программе делаются явные
ссылки на программу или кодовый сегмент по его адресу. В связи с
тем, что адрес сегмента в программе будет зависеть от того, где
он загружен в памяти, когда загружается программа .EXE, MS-DOS
должна каким-либо образом обновить ячейки в программе, где дела-
ется ссылка этого сегмента, изменяя значения для указания на те-
кущий сегмент. Этот процесс называется relocating (настройкой).
Перед рассмотрением выполнения настройки посмотрим чем этот про-
цесс отличается от загрузки файлов типа .COM.
Когда EXE2BIN выполняет преобразование файла типа .EXE в
файл типа .COM, она просматривает файл типа .EXE для нахождения
этих ссылок на сегменты. Если она находит явную ссылку на сег-
мент в программе, или неявную ссылку на другой не базовый сег-
мент, она вырабатывает сообщение об ошибке, указывающее на то,
что файл не может быть преобразован. Кроме этого, EXE2BIN выпол-
няет проверку того, чтобы программа начиналась с адреса 100h
относительно базового сегмента. Если все эти условия удовлетворя-
ются, то EXE2BIN удаляет из файла всю настраиваемую информацию и
вырабатывает файл типа .COM. Различия между этими двумя форматами
программ кратко излагаются в таблице 3-3.
Таблица 3-3
Различия между форматами .COM и .EXE
________________________________________________________________
| |
Атрибуты | тип .COM | тип .EXE
_______________________________|________________|_______________
Количество допустимых сегментов|Только 1 |Несколько сег-
| |ментов
_______________________________|________________|_______________
Ссылка на сегменты |Нет |Ссылки допуска-
| |ются
_______________________________|________________|_______________
Стековый сегмент |Не указывается |Должен быть оп-
| |ределен
_______________________________|________________|_______________
Начало программного кода |ORG в 100h |ORG не требует-
| |ся
_______________________________|________________|_______________
Размер программы |Менее 64 кбайт |Может быть лю-
| |бого размера
_______________________________|________________|_______________
Адрес PSP находится |Во всех регист- |В регистрах ES
|рах |и DS
_______________________________|________________|_______________
Блок начального распределения |Вся память |Размер может
| |быть изменен
_______________________________|________________|_______________
- 3-25 -
Загрузка файла типа .COM
Начальные шаги, предпринимаемые при загрузке и выполнении
программного файла типа .COM, идентичны шагам, предпринимаемым
при загрузке программного файла типа .EXE. При установке "кон-
текста" процесса MS-DOS сначала инициализирует блок среды, выби-
рая информацию либо из текущей среды системы (случай, принимаемый
по умолчанию), или из среды, указываемой порождающим процессом.
После установки среды, MS-DOS распределяет блок памяти для
программы. Для программ типа .COM этот блок памяти занимает всю
оставшуюся память. Минимально требуемый размер равен размеру фай-
ла программы типа.COM плюс память для PSP. После получения блока
памяти MS-DOS продолжает строить сегмент программого префикса
для программы в начале блока памяти. В этой точке используемый
процесс загрузки заметно отличается от того, который используется
с программой типа .EXE.
Файл типа .COM читается в память непосредственно выше PSP по
смещению 100 (шестнадцатиричное значение) в блоке памяти и без
настройки. Все регистры сегмента инициализируются для адреса сег-
мента PSP, указатель инструкции устанавливается в 100 (шестнадца-
тиричное значение), а указатель стека устанавливается в значение
0FFFE (шестнадцатиричное значение) или ниже, если имеется менее
64 Кбайт памяти, доступной процессу. (Минимальное значение указа-
теля стека равно 0100 - шестнадцатиричное значение). Управление
возвращается в процесс и программа .COM начинает выполнение.
Некоторые программы .COM имеют неприятности при функциониро-
вании из-за минимального стека, обеспечиваемого MS-DOS. Если
программа выполняется при слишком маленьком стеке, то в результа-
те это может привести к росту стека вниз в раздел программы или
данных, что непременно приведет к фатальному окончанию програм-
мы. Если программа .COM требует стек, больше минимального размера
в 256 байтов, то программист может построить свой минимальный
стек памяти в образе программы путем резервирования большего
пространства памяти в конце программы. (Запомните, что MS-DOS при
загрузке программы типа .COM автоматически добавит для стека не
менее 256 байт памяти). Этот способ при недостатке памяти для
требуемого стека не даст возможность MS-DOS загрузить программу.
Формат программного файла типа .EXE
В отличие от программного файла типа .COM, который содержит
только образ программы, файл программы типа .EXE должен содержать
всю необходимую информацию для настройки ссылок внутреннего сег-
мента. Также в связи с тем, что в программе типа .EXE не запре-
щено иметь свой особый стек или особую начальную точку, програм-
мный файл типа .EXE должен содержать информацию для загрузчика с
целью надлежащей инициализации программы.
Файл программы типа .EXE состоит из трех разделов: заголовка
файла .EXE, таблицы настройки и образа программы. Заголовок файла
.EXE показан в таблице 3-4. Некоторые элементы в заголовке обес-
печивают начальное состояние образа программы. Это: MinAlloc (ми-
нимальное распределение), MaxAlloc (максимальное распределение) и
начальные значения SS:SP и CS:IP. Другие элементы: настраиваемые
элементы и смещение таблицы настройки позволяют загрузчику обес-
печивать доступ к таблице настройки процесса.
Каждый элемент в таблице настройки позволяет загрузчику раз-
- 3-26 -
решать ссылку одного сегмента внутри образа программы. Каждый
элемент содержит указатель длины (сегмента или смещения) для
ссылки сегмента внутри образа загрузки. Указатель самого сегмента
является относительным по отношению к началу образа загрузки.
Таблица 3-4
Заголовок программного файла типа .EXE
________________________________________________________________
Шестнад-|
цатирич-|
ное сме-| Содержимое
щение |
_________|______________________________________________________
00 |Сигнатура. Маркер типа файла программы .EXE: 4D5H
|(шестнадцатиричное значение)
_________|______________________________________________________
02 |Остаток. Количество байтов на последней странице файла
|(размер образа загрузки модуля 512 байт)
_________|______________________________________________________
04 |Страницы. Количество 512-байтных страниц в файле,
|включая заголовок.
_________|______________________________________________________
06 |Элементы настройки. Количество элементов в таблице
|настройки.
_________|______________________________________________________
08 |Размер заголовка. Размер заголовка в 16-байтовых па-
|раграфах.
_________|______________________________________________________
0A |Минимальное распределение (MinAlloc). Минимальное ко-
|личество параграфов памяти, требуемое после конца
|программы.
_________|______________________________________________________
0C |Максимальное распределение (MaxAlloc). Максимальное
|количество параграфов памяти, требуемое после конца
|программы.
_________|______________________________________________________
0E |Стековый сегмент. Начальное значение для стекового
|сегмента (относительно начала образа загрузки програм-
|мы.
_________|______________________________________________________
10 |Указатель стека. Начальное значение указателя стека.
_________|______________________________________________________
12 |Контрольная сумма. Двоичное дополнение контрольной
|суммы программного файла.
_________|______________________________________________________
14 |Указатель инструкции. Начальное значение указателя
|инструкции.
_________|______________________________________________________
16 |Кодовый сегмент. Начальное значение кодового сегмента
|(относительно начала образа загрузки программы).
_________|______________________________________________________
18 |Смещение таблицы настройки. Относительное смещение
|байтов от начала программного файла в таблице настрой-
|ки.
_________|______________________________________________________
1A |Номер перекрытия. Номер перекрытия, сгенерированный
|компоновщиком LINK.
_________|______________________________________________________
- 3-27 -
программы. Во время настройки начальная ссылка сегмента, включаю-
щая образ загрузки, обновляется для включения действительных зна-
чений сегмента. Этот процесс мы рассмотрим более подробно только
после того, как разберем один большой аспект файлов .EXE - на-
чальные значения распределения.
Блок начального распределения памяти .EXE
В примерах, представленных до сих пор, считалось само собой
разумеющимся, что MS-DOS при загрузке программы в память распре-
деляет всю оставшуюся память для этой программы. Так, в примере
SHOUMEM, показанном на Рис. 3-4, для программы SHOWMEM назначен
последний и наибольший блок памяти. Это явление было рассмотрено
в главе 2, в которой для программ, приведенных в листингах 2 -12
и 2-13, была использована функция модификации блока распределения
памяти (функция 4Ah). Но мы намекали и на другие способы получе-
ния свободной памяти для программ типа .EXE. Рис. 3-6 показывает
программу типа .EXE, которая имеет большой блок доступной памяти,
а последний элемент таблицы 3-3 говорит о том, что размер блока
начального распределения программы .EXE может быть изменен. Как
это получается?
Заголовок файла типа .EXE содержит два элемента, которые уп-
равляют точным предоставлением памяти программе при ее загрузке.
Этими двумя элементами являются MinAlloc - минимальное распреде-
ление памяти (по смещению 0Ah) и MaxAlloc - максимальное распре-
деление памяти (по смещению 0Сh). Элемент MinAlloc сообщает заг-
рузчику о том, какой объем памяти (в 16-байтовых параграфах)
должна иметь программа для выполнения, т.е. сколько байтов ис-
пользует программа на самом деле. Элемент MaxAlloc, с другой сто-
роны, сообщает загрузчику количество параграфов памяти, которое
программа требует распределить для нее.
Компоновщик MS-DOS обычно устанавливает значение элемента
MaxAlloc в 0FFFFh, указывающее на то, что программа желает почти
1 Мбайт памяти. Т.к. MS-DOS не может может иметь мегабайт памяти,
то она выделяет программе всю оставшуюся память. Однако, если бы
мы указали значение элемента MaxAlloc, равное значению элемента
MinAlloc, то программа получила бы требуемую ей память, а остав-
шаяся часть была бы доступна для распределения. Для этого имеется
два очень простых способа.
Языки программирования фирмы "Майкрософт", включая MASM, пос-
тавляются с утилитой, называемой EXEMOD. Эта утилита может быть
использована для отображения и модификации заголовка программы
типа .EXE. Рис.3-9 показывает, как необходимо выполнять использо-
вание утилиты EXEMOD для получения дампа и затем модификации па-
раметра MaxAlloc. Можно удивиться, увидев, что в примере значение
параметра MaxAlloc изменяется на значение 1, но из рассмотрения
Рис.3-10 можно видеть, как на самом деле выполняется модификация
размера памяти, требуемого для программы SHOWMEM, и как выполня-
ется освобождение памяти. Модифицированный образ программы
SHOWMEM в памяти очень похож на образ программы типа .EXE, приве-
денный на Рис.3-6, включая свободный блок.
C> exemod c:\guide\examples\showmem.exe
- 3-28 -
1 Microsoft R EXE File Header Utility Version 4.02
2 Copyright c Microsoft Corp 1985-1987. All rights reserved.
c:\guide\examples\showmem.exe (hex) (dec)
3 EXE size (bytes) CC5 3269
4 Minimum Load size (bytes) AC5 2757
5 Overlay number 0 0
6 Initial CS:IP 0093:0000
7 Initial SS:SP 0013:0800 2048
8 Minimum allocation (para) 0 0
9 Maximum allocation (para) FFFF 65535
10 Header size (para) 20 32
11 Relocation table offset 1E 30
12 Relocations entries 1 1
C> exemod c:\guide\examples\showmem.exe /max 1
9 Maximum allocation (para) FFFF 65535
Рис.3-9. Использование утилиты EXEMOD
для программных файлов типа .EXE:
1 - версия 4.02 утилиты заголовка файла типа EXE фирмы "Майкро-
софт"; 2 - авторское право фирмы "Майкрософт карпорэйшн" 1985-
1987 гг. все права зарезервированы; 3 - размер EXE (в байтах); 4
- минимальный размер для загрузки (в байтах); 5 - номер перекры-
тия; 6 - начальное значение CS:IP; 7- начальное значение SS:SP; 8
- минимальное распределение; 9 - максимальное распределение; 10 -
размер заголовка; 11 - смещение таблицы настройки; 12 - количест-
во настраиваемых элементов.
Увидев, что значения MinAlloc и MaxAlloc равны нулю, Вы уди-
витесь. Если это имеет место, то действительный размер минималь-
ного распределения для программы будет равен размеру самой прог-
раммы, и дополнительное пространство памяти не распределяется. 1
SM-ShowMem, Version 1.00 c Copyright 1988 2 MCB Size Owner
Command Line
------------------------------------------------------------
0A01 08D7 0008 DOS
12D9 00D3 12DA [ SHELL ]
13AD 0003 0000 [ available ]
13B1 0032 12DA [ SHELL ]
13E4 0004 13EA c:\bin\RETRIEVE.COM
13E9 00A9 13EA c:\bin\RETEIEVE.COM
1493 000F 14A4 s:\MODE.COM\*
14A3 0017 14A4 s:\MODE.COM\*
14BB 0010 14CD c:\ws2000\SWITCH.COM
14CC 0018 14CD c:\ws2000\SWITCH.COM
14E5 0011 14F8 c:\GUIDE\EXAMPLES\SHOWMEM.EXE
14F7 00D1 14F8 c:\GUIDE\EXAMPLES\SHOWMEM.EXE
15C9 8A36 0000 [ available ]
<<<------------- End of Memory Block List ------------->>> 3
Рис.3-10. Пример отображения из SHOWMEM с параметром MaxAlloc,
равным значению параметра MinAlloc:
1 - программа показа памяти - ShowMem, версия 1.00, авторское
право 1988; 2 - блок управления памятью, размер, владелец, ко-
мандная строка; 3 - конец списка блоков памяти.
- 3-29 -
Таким способом необходимо определять размер всех программных
файлов .EXE, и даже учитывать размер EXEMOD при создании команд-
ных файлов. Однако, при создании файлов .EXE имеется другой спо-
соб управления параметром MaxAlloc - способ использования перек-
лючателя "/CPARMAXALLOC:nnn" (сокращенно: "/CP:nnn") компоновщика
LINK, где nnn - значение параметра MaxAlloc, выраженное в параг-
рафах. Например, программа SHOWMEM может быть образована со зна-
чением параметра максимального распределения, равным 1, путем ис-
пользования следующей команды:
C> link /cp:1 showmem,,,stdlib.lib;
Загрузчик процесса .EXE MS-DOS
Теперь нам известны все составные части, входящие в програм-
мные файлы типа .EXE, и можно начать рассмотрение загрузки и вы-
полнения программ типа .EXE.Так же,как и для процессов типа .COM,
первый шаг состоит в установке контекста процесса, начиная с бло-
ка среды.
После установки среды либо из системных таблиц, либо из таб-
лиц владельца, в рабочую область считывается заголовок програм-
много файла .EXE. Используя значения MinAlloc и MaxAlloc и размер
образа программы (из размера страницы и размера заголовка),
MS-DOS определяет требуемый размер блока памяти и распределяет
его. Если значение параметра MaxAlloc равно 0FFFFh, то при этом
будет распределена вся память.
После распределения блока памяти, в начале блока процесса
создается PSP (сегмент программого префикса). PSP для программ
типа .EXE не отличается от программ типа .COM. Затем MS-DOS чита-
ет образ программы в память непосредственно выше PSP, считывает
таблицу настройки и продолжает настраивать образ программы.
Рис.3-11 показывает, как элементы в таблице настройки ссылаются к
образу программы. Все числа на рисунке и арифметические действия
выполняются в шестнадцатиричной системе счисления.
Первым шагом при настройке является вычисление адреса начала
сегмента. Он является адресом реальной памяти, который соответс-
твует адресу начала образа программы в файле. На Рис.3-11 блок
памяти процесса размещен по адресу сегмента, равному 1000. PSP
занимает 100 байтов или 10 сегментов. Адрес начала программного
сегмента в памяти равен тогда сегменту 1010:0000, и это есть ад-
рес, по которому загрузчик поместит образ программы.
После загрузки образа программы загрузчик должен обновить
или настроить каждую ссылку сегмента. Когда компоновщик LINK на-
чинает строить образ программы, он использует предполагаемый ба-
зовый сегмент 0000. На самом деле, программа загружается в сег-
мент 1010, так что к каждой ссылке сегмента необходимо добавить
1010. Загрузчик находит все эти ссылки путем использования таб-
лицы настройки, которая содержит указатель на каждую ссылку сег-
мента в программе.
Рис.3-11 содержит две ссылки на значения сегментов. Просле-
дим процесс настройки для далекого (far) вызова, размещенного по
0003:1234. Действительная ссылка сегмента находится в четвертом
и пятом байтах этой инструкции по адресу 0003:1237.
- 3-30 -
Программный файл
типа .EXE
--------------- Начальные CS:IP = 0000:0010
|Заголовок прог-| Начальный сегмент = +1010
|раммного файла | ----------
--------------- Действительные
значения = 1010:0010
--------------- |
Добавление| Таблица наст- | | ОБРАЗ ПРОГРАММЫ
адреса | ройки | | В ПАМЯТИ
начала | 0003:1237 --------.| --------------- 1000:0000
сегмента | 0005:ABCE -------.|| | Префикс прог- |
1010 --------------- ||| | раммного сег- |
||| | мента |
0000:0000 --------------- ||| |---------------| 1010:0000
|Образ программы| ||| |Образ программы|
| | ||| | |
0000:0010 | START | || ->| START | 1010:0010
0003:1234 | CALL 0005:ABCD| | -->| CALL 1015:ABCD| 1013:1234
0005:ABCD | MOV AX,0007 | --->| MOV AX,1017 | 1015:ABCD
0007:0000 | Data Segment | | Data Segment | 1017:0000
--------------- ---------------
Рис.3-11. Процесс настройки для загрузки программы типа .EXE
Однако, этот адрес является относительным для мнимого нулевого
базового сегмента, а не для действительного образа программы в
памяти. Для нахождения действительной ссылки сегмента в памяти
указатель таблицы настройки сам должен быть обновлен с помощью
адреса начала сегмента. Действительная ссылка на сегмент является
адресом 1013:1237.
Слова, указываемые в памяти, увеличиваются затем на адрес
начала сегмента. Вызов far (далекий) для сегмента 0005 теперь
станет вызовом far для сегмента 1015 - действительное размещение
подпрограммы.
После завершения настройки, регистры ES и DS процесса уста-
навливаются на адрес сегмента PSP, а регистры CS:IP и SS:SP ини-
циализируются значениями, данными в заголовке программного файла
типа .EXE. Оба регистра CS и SS увеличиваются на адрес начала
сегмента образа программы. Например, на Рис.3-11 адрес начала
(START) 0000:0010 является смещением действительного адреса нача-
ла сегмента 1010, для формирования действительных значений CS:IP
1010:0010, используемых при запуске программы.
Перекрытия
Рано или поздно Вам придется писать программу, которая явля-
ется слишком большой, чтобы разместиться в отведенном для нее
месте памяти. Когда это произойдет, одним из возможных способов
выполнения таких программ является создание перекрытий (оверлеев
-overlays). Перекрытие является разделом программы, которому не
нужно все время находиться в памяти. Он загружается в память тог-
да, когда это необходимо, но после того, как он станет ненужным,
пространство памяти, занимаемое им, может быть использовано неко-
торым другим перекрытием. Остаток программы, который не может
быть помещен в перекрытие, называется корнем (root). Все данные
программы должны помещаться в корень, т.к. данные в перекрытии
теряются при загрузке в него очередной части программы. Перекры-
тия, в конце концов, являются только читаемыми.
- 3-31 -
Перекрытия являются очень полезными объектами, и MS-DOS под-
держивает их достаточно эффективно. Одним из назначений функции
EXEC (выполнить) является загрузка перекрытий в память. Но перед
рассмотрением этой опции, необходимо отметить, что компоновщик
LINK MS-DOS имеет возможность создавать перекрытия и автоматичес-
ки управлять ими!
Правила использования управления перекрытиями в MS-DOS прос-
ты. Оверлейные (перекрываемые) модули не могут содержать глобаль-
ные или статические данные, хотя постоянные данные допустимы.
Другое правило заключается в том, что перекрытие может быть выз-
вано только с помощью вызова far (далекий) либо корня, либо дру-
гого перекрытия. Перекрытие может вызвать корень через вызов near
(близкий).
Способ создания перекрытия (оверлея) очень прост: при вызове
команды LINK, объектные файлы, составляющие перекрытие, должны
заключаться в круглые скобки. Это все, что для них имеется. Сле-
дующая командная строка создает программный файл, использующий
три перекрытия:
C> link root + (init + read) + (work) + (save + exit) ,myprog ;
Этот пример использует один набор подпрограмм для чтения не-
которых данных и инициализации программы, другой набор для обра-
ботки данных и еще один набор подпрограмм для сохранения обрабо-
танных данных и выхода. Поскольку ни одна из этих операций не
выполняется одновременно, каждая их них выполняется в перекрытии,
и, таким образом, решается проблема гипотетической памяти.
Резидентные программы
При типовом использовании операционная система MS-DOS пред-
ставляет собой операционную систему с одной задачей. В любой мо-
мент времени в памяти выполняется только одна программа. Факти-
чески же, MS-DOS имеет возможность в любое время поддерживать
несколько программ в памяти. В действительности, в любое конкрет-
ное время выполняется только одна программа, потому что процессор
может выполнять в любой конкретный момент времени только одну ин-
струкцию, но программы могут быть сконфигурированы таким образом,
что создается видимость их одновременного выполнения. Эти не-
сколько программ создаются путем загрузки программы в память с
помощью MS-DOS и затем возврата управления к MS-DOS без удаления
программы из памяти. Поскольку программа не покидает память при
возврате управления операционной системе, то программа называется
резидентной. Первым шагом при выполнении резидентной программы
является установка программы в памяти. Одним из простейших типов
резидентных программ являются библиотеки исполняющей системы (RTL
- run-time library), которые будут использованы в качестве перво-
го примера.
Описание библиотеки исполняющей системы
Что такое библиотека исполняющей системы? Как известно, биб-
лиотека представляет из себя собрание полезных подпрограмм, ко-
торые могут быть вызваны из программы. Большинство библиотек яв-
- 3-32 -
ляются скомпонованными (отредактированными) библиотеками, в кото-
рых требуемые подпрограммы включаются в программный файл (.EXE
или .COM) во время компоновки. Т.к. они являются частью програм-
много файла, подпрограммы скомпонованной библиотеки загружаются
вместе с программой при загрузке программного файла. RTL непос-
редственно не компонуется с программой, но подключается во время
выполнения. RTL должна уже находиться в памяти, или она должна
быть занесена в память, когда это необходимо, но, в любом случае
RTL не является частью самого программного файла.
RTL непосредственно не объединяется с программой, так как же
программа выполняет ее вызов? Программа должна каким-либо образом
поставить в известность либо операционную систему, либо RTL о
поддержке процесса, с помощью которого запрашивается библиотека.
Это может быть выполнено через вызовы, внутренние прерывания, ис-
ключительные ситуации или прерывания, зависящие от комплекса ап-
паратных средств и операционной системы. В среде операционной
системы MS-DOS/8086 наиболее подходящим способом является способ
оповещения через прерывание.
Почему же используют библиотеки RTL, если они требуют допол-
нительных усилий: предварительной загрузки, вызова и т.д.?
Во-первых, библиотеки RTL часто используются при разработке при-
кладных программ, которые имеют большое количество программ, со-
вместно использующих общие подпрограммы или для обеспечения общи-
ми ресурсами всех пользователей отдельного языка программирова-
ния. При использовании библиотек RTL, разработчикам необходимо
сохранять только одну копию библиотеки, вместо того, чтобы каждая
программа содержала такую копию. Пока интерфейс между программами
и RTL остается неизменным, подпрограммы в RTL могут обновляться
без модификации или перекомпоновки программ, которые их вызывают.
Поэтому RTL может выглядеть как расширение операционной системы,
т.к. она обеспечивает такие средства, которые необходимы разра-
ботчикам, но которые не поддерживает операционная система.
Во-вторых, библиотеки RTL имеют дополнительные преимущества по
уменьшению дисковой памяти и ускорению времени загрузки програм-
мы, т.к. RTL не загружается с каждой программой в отдельности.
Загрузка резидентных подпрограмм из командной строки
В MS-DOS имеется несколько способов, которые могут быть ис-
пользованы для загрузки образа программы в память. Диапазон этих
способов простирается от загрузки программы из командной строки
до подпрограмм начальной загрузки нижнего уровня, передающих
программный код из абсолютного места на диске в фиксированные
ячейки памяти. Наиболее простым способом является способ исполь-
зования загрузчика командной строки MS-DOS, представляющий собой
простой запрос для выполнения программы. Резидентные программы,
такие как, например, RTL, загружаются в память подобно любой
другой программе. Однако, после того, как резидентная программа
загружена и начала выполняться посредством предложения ее инициа-
лизации, она завершается использованием специального выхода:
функции с кодом 31h ("сохранить процесс") или вектора прерывания
27h ("завершить, но оставить резидентной"). Рекомендуемой проце-
дурой является использование функции с кодом 31h прерывания 21h,
которая демонстрируется в листинге 3-4.
Функция с кодом 31h имеет два параметра: необязательный па-
раметр код возврата, используемый для указания состояния при вы-
- 3-33 -
ходе из подпрограммы, и обязательный параметр, представляющий со-
бой значение размера блока памяти в параграфах, которое остается
распределенным за процессом. При вызове функции MS-DOS резервиру-
ет запрошенное количество памяти, начиная с адреса PSP (сегмента
программого префикса). Это происходит почти также как и при вызо-
ве функции "Модифицировать блок распределенной памяти" с адресом
PSP и требуемым размером. В случае функции "сохранить процесс"
MS-DOS знает, что блок, размер которого должен быть модифициро-
ван, начинается с адреса PSP, так что параметр не требуется.
Листинг 3-4. Функция с кодом 31h - "Сохранить процесс"
----------------------------------------------------------------
; используемый тип .COM
program segment
ORG 0
seg_org equ $
ORG 0100h
start:
...
mov dx,(offset last_byte - seg_org + 15) shr 4
mov ah,31h ; сохранить процесс
int 21h ; вызов MS-DOS
...
last_byte:
program ends
end start
; используемый тип .EXE
...
mov ax,es ; получение адреса PSP
mov dx,seg end_addr ; получение адреса посл.сегм.
sub dx,ax ; получение размера прогр-мы
mov ah,31h ; сохранить процесс
int 21h ; вызов MS-DOS
...
program ends
end_addr segment
end_addr ends
end start
----------------------------------------------------------------
В главе 2 был представлен набор формул для вычисления размера
программы в параграфах. Эти формулы могут быть использованы с
функцией "Сохранить процесс" также как и с функцией "Модифициро-
вать блок распределенной памяти". При использовании этих формул в
резидентных программах, появилось соответствующее уравнение, как
показано в листинге 3-4. Заметим, что хотя функция "Сохранить
процесс" и не требует адреса PSP, программам типа .EXE необходимо
сохранять адрес PSP при выходе с целью вычисления размера прог-
раммы.
Т.к. память резервируется в начале PSP, резидентные подпрог-
раммы не должны загружаться в верхнюю часть блока памяти (напри-
мер, путем использования переключателя /high компоновщика
MS-LINK). Если подпрограмма загружается в верхнюю часть памяти,
то она станет незащищенной при завершении резидентной подпрограм-
мы, т.к. сохраняемый блок памяти размещается в начале блока памя-
ти. Подпрограмма сама будет размещаться выше пространства резер-
вируемой памяти. Когда подпрограмма станет таким образом
незащищенной, MS-DOS может загрузить на то же самое место памяти
другую программу или нерезидентную часть файла COMMAND.COM, зати-
рая резидентную подпрограмму.
В любом случае, переключатель /high компоновщика MS-LINK за-
трагивает только программы типа .EXE. Когда конвертирующая прог-
рамма EXE2BIN для файла .COM удалит маркер "загрузка высокая",
MS-DOS будет загружать программу с начала PSP.
Другим способом инстоляции резидентных программ является пре-
рывание "завершить и оставить резидентной" int 27h, оставленное
из ранних версий MS-DOS. Способ использования прерывания int 27h
имеет ряд недостатков, которые сводят на нет использование этого
способа. В отличие от функции "Сохранить процесс" (Keep Process),
прерывание int 27h не требует адреса блока памяти (задаваемого
адресом PSP), а требует этот адрес в регистре CS. Только файлы
типа .COM имеют адрес PSP в регистре программного сегмента, за-
трудняя использование этой функции в программах типа.EXE. (Как
выполнить изменение регистра CS и еще выполнить программу?) Кроме
того, параметр размер указывается в байтах, а не в параграфах,
что ограничивает размер программы, который может быть сохраненным
до 64 Кбайт (максимальный размер программы типа .COM). Единствен-
ным достоинством этой функции является то, что в качестве пара-
метра может быть использовано без преобразования смещение послед-
него адреса, как показано ниже:
...
mov dx,offset last_byte ; получение количества байтов
int 27h ; завершение и оставить резидентной
...
last_byte:
program ends
end start
Фирма "Майкрософт" рекомендует для всех вновь разрабатываемых
и для всех существующих модернизированных программ преобразовать
это прерывание в функцию с кодом 31h. При выполнении преобразова-
ния не забудьте модифицировать параметр размер (Size) из байтов в
параграфы.
Доступ к резидентным подпрограммам через прерывания
В результате выполнения программы, показанной в листинге
3-4, в памяти системы будет установлена резидентная программа.
После размещения необязательно вся программа должна находиться в
памяти. Для включения этой программы в RTL необходимо передавать
ей намерение и сделать ее доступной для других программ.
RTL может содержать любую функцию и сделать любой вызов
MS-DOS (например, прерывание int 21h), пока библиотека вызывается
только текущей выполняющейся программой. Это ограничение предназ-
начено для предотвращения неумышленного повторного вызова MS-DOS,
который приведет к сбою системы. Следующая программа, показанная
в листинге 3-5, содержит пример интерфейса для RTL, который может
поддерживать много отдельных функций и очень похож на обработчик
прерывания MS-DOS int 21h.
- 3-35 -
Как показано в листинге 3-5, этот пример структуры может быть
расширен путем добавления необходимого программного кода для под-
держки подпрограмм сравнения, получения справок о таблицах, пре-
образования ввода-вывода, или, даже, общей области для нескольких
программ. Мы попытались включить некоторые примеры технических
приемов, рассмотренных в главе 2, такие как использование пара-
метров стека, отчеты об ошибках и т.д. Если эта подпрограмма ис-
пользуется для поддержки большого количества функций, то можно
заменить модель макроса таблицей переходов, как демонстрируется в
драйвере дискового запоминающего устройства с произвольной выбор-
кой RDISK в главе 6.
Библиотека MACRO, упоминаемая в программе EXRTL, содержит мо-
дель макроса, введенного в главе 1, а также макросы dis_chr отоб-
разить символ) и dis_str (отобразить строку), представленные в
документе "Technical Reference Manual. @DosCall" (Справочное ру-
ководство по техническому обслуживанию. Вызовы DOS), и, конечно
же, макрос для прерывания int 21h.
Листинг 3-5. Пример установки RTL
(подпрограмма EXRTL - Example Run-Time Library)
----------------------------------------------------------------
;====== RTL.ASM - этот файл вырабатывает файл типа .COM ======
V_NUM EQU 40h ; эта RTL использует вектор 40h
;
INCLUDE STDMAC.INC ; включение файла макробиблиотеки
;====== СЕКЦИЯ ПРОГРАММНОГО КОДА ==============================
;
frame STRUC ; схема структуры стека вызывающей программы
old_bp dw ? ; запомненный указатель базы
ret_IP dw ? ; адрес возврата (IP)
ret_CS dw ? ; адрес возврата (CS)
flags dw ? ; флажки вызывающей программы
funct dw ? ; номер выполняемой функции
frame ENDS
;
code_seg SEGMENT
ASSUME cs:code_seg
ASSUME ds:code_seg
main PROC FAR
ORG 0
seg_org EQU $
ORG 2Ch
env_adr LABEL WORD ; смещение среды в PSP
ORG 0100h
start: jmp install
entry: push bp ; сохранение указателя базы
mov bp,sp ; получение адреса стека
push ds ; сохранение сегмента данных
push ax ; сохранение регистра
push bx
mov ax,cs ; установка сегмента данных
mov ds,ax
mov ax,[bp].flags ; передача флажков вызывающ.пр-мы
sahf ; в AX и в мои флажки
clc ; очистка переноса (нет ошибки)
- 3-36 -
pushf ; и сохранение копии флажков
mov bx,[bp].funct ; получение кода функции
@Case bl,<1,2>,
© KOAP Open Portal 2000 |