Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система MS-DOS

© Александр Фролов, Григорий Фролов
Том 1, книги 1-2, М.: Диалог-МИФИ, 1991.

[Назад] [Содержание] [Дальше]

2.2. Блоки управления памятью в MS-DOS

Первое поле векторной таблицы связи mcb_seg содержит сегментный адрес первого блока управления памятью (MCB). Блок MCB всегда начинается на границе параграфа, поэтому полный адрес первого блока будет равен mcb_seg:0.

Для лучшего понимания механизма управления памятью в MS-DOS вспомним карту распределения памяти:

Диапазон адресов Содержимое
0000:0000 Векторы прерываний
0000:0400 Область данных BIOS
0000:0500 Область данных DOS
xxxx:0000 Область программ DOS (расширение BIOS, обработчики прерываний DOS, буфера, области данных, загружаемые драйверы устройств)
xxxx:0000 Резидентная порция COMMAND.COM
xxxx:0000 TSR-программы (остающиеся резидентными после запуска)
xxxx:0000 Выполняющиеся прикладные программы типа COM или EXE
xxxx:0000 Транзитная порция COMMAND.COM
A000:0000 Память EGA, используемая некоторыми видеорежимами
B000:0000 Память монохромного дисплейного адаптера
B800:0000 Память видеоадаптера CGA
C800:0000 Внешнее ПЗУ
F600:0000 ПЗУ интерпретатора BASIC
FE00:0000 ПЗУ BIOS

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

Адреса 0000:0400 - 0000:04FF (или от 0040:0000 до 0050:0000) занимает область данных BIOS. Это внутренние переменные BIOS. К ним можно обращаться для получения различной информации, но необходимо только помнить, что формат этой области может быть различным для различных версий BIOS. Мы будем при необходимости ссылаться на отдельные поля этой области.

Начиная с адреса 0000:0500 (или с адреса 0050:0000, что одно и то же) следует область данных DOS. Здесь DOS хранит свои внутренние таблицы и переменные. Формат этой области (и ее размер) зависит от версии операционной системы.

Далее следует большая область памяти, используемая DOS. Здесь располагаются:

система ввода-вывода DOS (содержимое файла IO.SYS);

обработчики прерываний DOS, в частности, обработчик прерывания INT 21H (эти обработчики входят в состав файла MSDOS.SYS);

внутренние буфера DOS и области данных;

загружаемые драйверы (описанные в файле CONFIG.SYS).

После драйверов располагается резидентная порция COMMAND.COM (командный интерпретатор). Она, в частности, обрабатывает прерывания INT 22H, INT 23H и INT 24H.

Следующая область памяти занимается программами, остающимися резидентными после запуска (TSR-программы).

После резидентных программ находится выполняющаяся в настоящий момент программа (COM или EXE). Она может занимать всю оставшуюся память до адреса A000:0000 или только часть этой памяти (для EXE-программ). Для EXE-программ можно при редактировании указать требуемый объем памяти.

Нижнюю часть 640-килобайтного адресного пространства (до адреса A000:0000) занимает транзитная часть COMMAND.COM. Она может перекрываться выполняющейся программой. Если программа перекроет транзитную часть COMMAND.COM, то после завершения выполнения программы эта часть командного интерпретатора будет загружена заново.

Область адресов от A000:0000 до C800:0000 используется дисплейными адаптерами. Каждый адаптер использует эту часть памяти по-своему.

Далее и до конца мегабайтной границы идет область ПЗУ. Там расположено ПЗУ BIOS, ПЗУ интерпретатора BASIC, расширение BIOS (например EGA BIOS - для дисплейных адаптеров EGA).

Диапазон адресов свыше мегабайта используется для машин класса не ниже AT. Это так называемая расширенная память (Extended Memory). Она обслуживается BIOS и используется операционной системой MS-DOS для организации "электронного" диска, кэш-памяти для дисков. Некоторые прикладные программы (например, отладчик Microsoft CodeView) хранят в этой области свои данные. Полностью управлять расширенной памятью способны операционные системы типа OS/2 и UNIX в защищенном режиме работы (процессоры Intel 80286, 80386, 80486), а также оболочка Microsoft Windows версий 3.0 и 3.1.

С расширенной памятью не следует путать дополнительную память (Expanded Memory). Эта память с помощью специальной аппаратуры и драйверов отображается в область адресного пространства, лежащую до границы 1 мегабайт. MS-DOS может использовать эту память аналогично расширенной памяти.

Зона памяти, начиная с области программ DOS и до видеопамяти дисплейных адаптеров, разбита на блоки. Перед каждым блоком находится блок управления памятью - Memory Control Block (MCB).

Сегментный адрес первого блока MCB находится в векторной таблице связи, в поле mcb_seg (смещение блока равно 0). Внутри блока MCB содержится длина описываемого данным MCB блока памяти. Следующий MCB начинается сразу за предыдущим. Таким образом, все блоки управления памятью связаны в список.

Блоки MCB бывают двух типов - M и Z. M-блоки ('middle') - это промежуточные блоки. Блок типа Z является последним блоком в списке и может быть только один.

Приведем формат блока MCB (описание этого блока отсутствует в документации по MS-DOS версии 4.01, поэтому его формат может измениться в последующих версиях операционной системы):

(0) 1 type тип блока MCB (M или Z)
(+1) 2 owner параграф владельца блока (если 0, то блок описывает сам себя)
(+3) 2 size число параграфов в этом блоке (один параграф имеет размер 16 байт)
(+5) 11 reserve зарезервировано

Для удобства работы с блоком MCB файл sysp.h содержит определение типа MCB:

#pragma pack(1)

typedef struct _MCB_ {
        unsigned char type;
        unsigned owner;
        unsigned size;
        char reserve[11];
} MCB;

#pragma pack()

Существует несколько удобных программ для просмотра списка блоков MCB. Вот какую информацию выдает программа MI.COM из пакета PCSHELL при запуске с параметром /A:

Memory Info v5.8
Copyright 1989 Central Point Software, Inc.  All rights reserved.

Conventional memory.  Total:  640k
Largest executable program:   485k

Type  Paragraphs  Bytes     Owner
----  ----------  -----  -------------
Sys   0BA4-18C5h  53792  0008h < DOS >
Free  18C7-18CFh    144  0000h < DOS >
Env   18D1-18D2h     32  18D4h JYRKEYB
Prog  18D4-1904h    784  18D4h JYRKEYB C:\DOS\JYRKEYB.COM C
Prog  1906-1A69h   5696  1906h COMMAND
Env   1A6B-1A7Dh    304  1906h COMMAND
      1A7F-1A82h     64  1906h COMMAND
Free  1A84-1A93h    256  0000h < DOS >
Prog  1A95-1DD8h  13376  1A95h MOUSE
Env   1DDA-1DEDh    320  1ED8h NS
Prog  1DEF-1ED6h   3712  1DEFh SHELLB  DOSSHELL
Prog  1ED8-21EBh  12608  1ED8h NS      f:\norton\NS.EXE
Env   21ED-2200h    320  2202h NC
Prog  2202-2527h  12896  2202h NC      f:\norton\NC.EXESocha
      2529-253Ch    320  253Eh COMMAND
Prog  253E-26A1h   5696  253Eh COMMAND /a
Env   26A3-26B5h    304  253Eh COMMAND
Env   26B7-26CAh    320  26CCh MI
Prog  26CC-9FFFh    485k 26CCh MI      c:\dos\MI.COM /a

Программа сообщает размер оперативной памяти (640 К), максимальный размер выполняемой программы (485 К), затем выдает список блоков памяти с указанием типа, занимаемых параграфов памяти, размера в байтах и владельца блока памяти. Программа MI различает четыре типа блоков памяти:

системный (Sys), его владельцем является MS-DOS;

свободный (Free), обычно тоже принадлежит MS-DOS;

программный (Prog) - его занимает запущенная программа;

среда (Env) - содержит переменные среды MS-DOS.

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

Видно, что для каждой запущенной программы создается два блока памяти - блок среды и программный блок. Среда формируется при загрузке операционной системы с помощью команд SET и содержит строки вида:

LIB=D:\C600\LIB

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

Блок памяти типа Prog (программный) независимо от формата загрузочного модуля (COM или EXE) начинается с префикса программного сегмента PSP, за которым следует сама программа.

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

MCB для блока памяти переменных среды;

блок памяти переменных среды;

MCB программного блока памяти;

префикс программного сегмента PSP;

программный модуль.

Приведем фрагмент дампа оперативной памяти, полученные при отладке программы MI.COM с помощью отладчика Microsoft CodeView. Дамп памяти начинается с адреса 6D47:0000.

Первая строка дампа (смещение 000-00F) - это MCB блока памяти переменных среды. Это не последний блок памяти, поэтому MCB имеет тип M. В поле владельца блока памяти находятся нули, следовательно, MCB принадлежит сам себе. Поле длины содержит значение 0014 - это количество параграфов в блоке памяти переменных среды.

Со смещением 010 начинается блок переменных среды. Из дампа видно, что строки определения переменных среды закрыты двоичным нулем. Таблица переменных среды также закрыта нулем (смещение 13B). Со смещением 13C записано слово 0001 (количество слов в последующей строке), после которого расположен полный путь для файла запущенной программы.

000 4D00 0014 0000 0000 0000 0000 0000 0000 M...............
010 434F 4D53 5045 433D 433A 5C43 4F4D 4D41 COMSPEC=C:\COMMA
020 4E44 2E43 4F4D 0054 4D50 3D67 3A5C 7465 ND.COM.TMP=g:\te
030 6D70 0050 4154 483D 673A 5C3B 643A 5C71 mp.PATH=g:\;d:\q
040 6332 5C62 696E 3B65 3A5C 6336 3030 5C62 c2\bin;e:\c600\b
050 696E 623B 653A 5C63 3630 305C 6269 6E3B inb;e:\c600\bin;
060 633A 5C64 6F73 3B63 3A5C 6172 633B 663A c:\dos;c:\arc;f:
070 5C6E 6F72 746F 6E3B 653A 5C77 6F72 643B \norton;e:\word;
080 004C 4942 3D65 3A5C 7163 325C 4C49 4200 .LIB=e:\qc2\LIB.
090 494E 434C 5544 453D 673A 5C69 6E63 6C75 INCLUDE=g:\inclu
0A0 6465 3B65 3A5C 7163 325C 494E 434C 5544 de;e:\qc2\INCLUD
0B0 453B 653A 5C63 746F 6F6C 735C 696E 636C E;e:\ctools\incl
0C0 7564 653B 0048 454C 5046 494C 4553 3D65 ude;.HELPFILES=e
0D0 3A5C 4336 3030 5C48 454C 505C 2A2E 484C :\C600\HELP\*.HL
0E0 503B 0049 4E49 543D 653A 5C43 3630 305C P;.INIT=e:\C600\
0F0 494E 4954 0044 4D41 4B45 3D67 3A5C 646D INIT.DMAKE=g:\dm
100 616B 653B 0056 4547 413D 653A 5C76 6567 ake;.VEGA=e:\veg
110 613B 004E 4152 4348 454C 503D 663A 5C61 a;.NARCHELP=f:\a
120 7263 5C6E 6172 633B 0048 454C 5050 4154 rc\narc;.HELPPAT
130 483D 643A 5C68 656C 703B 0000 0100 433A H=d:\help;....C:
140 5C44 4F53 5C6D 692E 636F 6D00 6D00 7016 \DOS\mi.com.m.p.

Далее начинается MCB прогаммного блока памяти (смещение 150). Это последний блок памяти, поэтому MCB имеет тип Z. Непосредственно за MCB располагается префикс программного сегмента PSP (смещение 160). Размер PSP - 256 байт, его формат будет описан ниже.

И, наконец, со смещением 260 расположена сама программа MI.COM.

150 5A00 00A3 3216 0000 6D69 0000 0000 0000 Z...2...mi......
160 CD20 00A0 009A F0FE 1DF0 3D09 B22E 340A . ........=...4.
170 B22E 850E B22E CD26 FFFF FFFF FFFF FFFF .......&........
180 FFFF FFFF FFFF FFFF FFFF FFFF 486D 92B3 ............Hm..
190 FD4B 1400 1800 5D6D FFFF FFFF 0000 0000 .K....]m........
1A0 0000 0000 0000 0000 0000 0000 0000 0000 ................
1B0 CD21 CB00 0000 0000 0000 0000 0020 2020 .!........... 
1C0 2020 2020 2020 2020 0000 0000 0020 2020 ..... 
1D0 2020 2020 2020 2020 0000 0000 0000 0000 ........
1E0 0320 2F61 0038 0F00 2C09 0001 0000 4005 . /a.8..,.....@.
1F0 7808 0000 9000 0100 04B3 0D07 ED2F 14B3 x............/..
200 7501 4A36 0000 0000 0000 4003 A308 3F00 u.J6......@...?.
210 FD4B FD4B 0100 2ABA 0000 0000 026F 5718 .K.K..*......oW.
220 3800 0500 3800 CF08 DD26 2D09 FD4B FD4B 8...8....&-..K.K
230 E408 0000 4200 A308 3F00 FD4B FD4B E408 ....B...?..K.K..
240 1304 0000 5718 C000 0500 C000 CE02 CE03 ....W...........
250 5718 8400 0500 8400 CE02 CE03 973A 92B3 W............:..
260 E9BB 000D 0A4D 656D 6F72 7920 496E 666F .....Memory Info
270 2076 352E 380D 0A43 6F70 7972 6967 6874 v5.8..Copyright
280 2031 3938 3920 4365 6E74 7261 6C20 506F 1989 Central Po
290 696E 7420 536F 6674 7761 7265 2C20 496E int Software, In
2A0 632E 2020 416C 6C20 7269 6768 7473 2072 c. All rights r
2B0 6573 6572 7665 642E 0D0A 0A00 4279 2047 eserved.....By G
2C0 5744 2030 312F 3036 2F38 391A 2D2D 2D2D WD 01/06/89.----
2D0 5041 5443 4820 4152 4541 2D2D 2D2D 2D2D PATCH AREA------
2E0 2D2D FF90 5601 4102 0000 0290 5D6D 5C6D --..V.A.....]m\m

Префикс программного сегмента всегда создается при загрузке программы COM или EXE в память и имеет одинаковый формат для COM и EXE файлов:

(0) 2 int20h двоичный код команды int 20h (программы могут использовать эту команду для завершения своей работы)
(+2) 2 mem_top нижняя граница доступной памяти в системе в параграфах
(+4) 1 reserv1 зарезервировано
(+5) 5 call_dsp команда вызова FAR CALL диспетчера MS-DOS
(+10) 4 term_adr адрес завершения (Terminate Address)
(+14) 4 cbrk_adr адрес обработчика Ctrl-Break
(+18) 4 crit_err адрес обработчика критической ошибки
(+22) 2 parn_psp сегмент PSP программы, запустившей данную программу (программы-родителя)
(+24) 20 file_tab таблица открытых файлов, если здесь находятся байты 0FFH, то таблица не используется
(+44) 2 env_seg сегмент блока памяти, содержащего переменные среды
(+46) 4 ss_sp адрес стека SS:SP программы
(+50) 2 max_open максимальное число открытых файлов
(+52) 4 file_tba адрес таблицы открытых файлов
(+56) 24 reserv2 зарезервировано
(+80) 3 disp диспетчер функций DOS
(+83) 9 reserv3 зарезервировано
(+92) 16 fcb1 форматируется как стандартный FCB, если первый аргумент командной строки содержит правильное имя файла
(+108) 20 fcb2 заполняется для второго аргумента командной строки аналогично fcb1
(+128) 1 p_size число значащих символов в неформатированной области параметров, либо буфер обмена с диском DTA, назначенный по умолчанию
(+129) 127 parm неформатированная область параметров, заполняется при запуске программы из командной строки

Для обращения к полям PSP файл sysp.h содержит следующее определение:

#pragma pack(1)

typedef struct _PSP_ {
        unsigned char int20h[2];
        unsigned mem_top;
        unsigned char reserv1;
        unsigned char call_dsp[5];
        void far *term_adr;
        void far *cbrk_adr;
        void far *crit_err;
        unsigned parn_psp;
        unsigned char file_tab[20];
        unsigned env_seg;
        void far *ss_sp;
        unsigned max_open;
        void far *file_tba;
        unsigned char reserv2[24];
        unsigned char disp[3];
        unsigned char reserv3[9];
        unsigned char fcb1[16];
        unsigned char fcb2[20];
        unsigned char p_size;
        unsigned char parm[127];
} PSP;
#pragma pack()

Приведем программу, определяющую адрес первого блока MCB:

/**
*.Name      get_fmcb
*
*.Title     Получить адрес первого MCB
*
*.Descr     Функция возвращает адрес первого блока MCB
*
*.Params    MCB far *get_fmcb(CVT far *cvt)
*
*              cvt - адрес векторной таблицы связи
*
*.Return    Указатель на первый блок MCB
**/

#include "sysp.h"

MCB far *get_fmcb(CVT far *cvt)
{
        return((MCB far *)FP_MAKE(cvt->mcb_seg,0));
}

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

Для получения адреса следующего MCB можно использовать функцию get_nmcb:

/**
*.Name      get_nmcb
*
*.Title     Получить адрес следующего MCB
*
*.Descr     Функция возвращает адрес следующего блока MCB
*           или 0, если это последний блок. В качестве
*           параметра используется указатель на предыдущий
*           блок MCB
*
*.Params    MCB far *get_fmcb(MCB far *mcb)
*
*              mcb - адрес предыдущего MCB
*
*.Return    Указатель на следующий блок MCB или 0, если
*           это последний MCB
**/

#include <dos.h>
#include "sysp.h"

MCB far *get_nmcb(MCB far *mcb)
{
unsigned seg, off;

        if(mcb->type == 'M') {
           seg = FP_SEG(mcb) + mcb->size + 1;
           off = FP_OFF(mcb);
           return((MCB far *) FP_MAKE(seg,off));
        }
        else return ((MCB far *)0);
}

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

#include <dos.h>
#include <stdio.h>
#include "sysp.h"

void main(void);

void main(void)
{
         CVT far *cvt;
         MCB far *mcb;
         printf("\nБлоки управления памятью (MCB)"
                "\nCopyright (C)Frolov A., 1990\n"
                "\nАдрес MCB Тип Владелец Размер"
                "\n--------- --- -------- ------"
                "\n");
        cvt=get_mcvt();
        mcb=get_fmcb(cvt);
        for(;;) {
           if(mcb == (MCB far *)0) break;
           printf("%Fp %c   %04X     %04X\n",
           mcb,
           mcb->type,
           mcb->owner,
           mcb->size);
           mcb=get_nmcb(mcb);
        }
        exit(0);
}
[Назад] [Содеожание] [Дальше]