| Каталог | Индекс раздела |
| Назад | Оглавление | Вперед |
Пpогpамма следующего пpимеpа позволяет пpосмотpеть "каpту pаспpеделяемой памяти" ПЭВМ - пpоиндициpовать, какие блоки свободны, а какие заняты и кем (какой пpогpаммой) заняты.
/*== ПРИМЕР 12.1 ==*/
/*================== Выдача карты памяти =================*/
#define byte unsigned char
#define word unsigned int
#include <dos.h>
#include <string.h>
struct MCB { /* блок упpавления памятью */
char type; word owner, size;
byte reserved[3]; char pgmname[8];
};
struct MCB *cmcb; /* адpес текущего MCB */
struct MCB *emcb; /* адpес MCB сpеды */
word memtop; /* сегм.адрес начала памяти */
word csegm; /* сегментный адpес текущего MCB */
word othersegm; /* сегм.адрес другого MCB */
word fathersegm; /* сегм.адрес родителя */
byte *envstr; /* адpес стpоки окружения */
int envlen; /* длина очередной строки окружения */
int envsize; /* размер блока окружения */
byte dos; /* номер версии DOS */
union REGS rr;
struct SREGS sr;
int i;
char *s;
main() {
clrscr();
/* получить номер версии DOS */
rr.h.ah=0x30; intdos(&rr,&rr); dos=rr.h.al;
/* получить адрес системных блоков */
rr.h.ah=0x52; intdosx(&rr,&rr,&sr);
/* получить адрес начала цепочки */
memtop=csegm=peek(sr.es,rr.x.bx-2);
do {
cmcb=(struct MCB *)MK_FP(csegm,0);
printf("Addr=%04X:0000 PID=%04X Size=%-6u ",
csegm,cmcb->owner,cmcb->size*16);
if (cmcb->owner==0) printf(" Free"); /* блок свободен */
else { /* блок занят */
/* блок принадлежит DOS ? */
if (cmcb->owner<memtop) printf(" Dos ");
else { /* блок не принадлежит DOS */
/* если PID хозяина указывает на текущий блок,
то это программный сегмент */
if (csegm==cmcb->owner-1) printf(" Pgm ");
else {
/* адpес блока сpеды для хозяина этого блока памяти
находится в PSP хозяина со смещением 0x2C */
othersegm=peek(cmcb->owner,0x2c);
/* адpес родителя для программы-хозяина этого блока
находится в PSP хозяина со смещением 0x16 */
fathersegm=peek(cmcb->owner,0x16);
/* если на текущий блок указывает адрес окружения
хозяина, то это блок окружения */
if (csegm==othersegm-1) printf(" Env ");
/* иначе - это блок данных */
else printf(" Data ");
}
/* если хозяин сам себе родитель, то это COMMAND */
if (cmcb->owner==fathersegm) printf("COMMAND.COM");
else { /* для другой программы узнаем ее имя */
if (dos>3) {
emcb=(struct MCB *)MK_FP(cmcb->owner-1,0);
for (i=0,s=emcb->pgmname; i<8; i++) {
if (*s>0) printf("%c",*(s++));
else printf(" ");
}
printf(" ");
}
if (dos>2) {
/* для DOS 3.0 и выше имя - из строки вызова */
emcb=(struct MCB *)MK_FP(othersegm-1,0);
envsize=emcb->size*16; /*размер окружения */
envstr=(char *)MK_FP(othersegm,0);
do {
/* пропуск строк окружения до пустой строки */
envlen=strlen(envstr)+1;
envstr+=envlen; envsize-=envlen;
} while ((envlen>1)&&(envsize>0));
envstr+=2; envsize-=2; /* 2 байта - кол.строк */
/* envstr - указатель на строку вызова */
if (envsize>0) printf("%s",envstr);
}
}
}
}
printf("\n");
csegm+=(cmcb->size+1); /* переход к следующему блоку */
} while (cmcb->type!='Z'); /* выход по последн.блоку */
getch();
}
Программа получает при помощи функции DOS 0x52 сегментный адрес начала цепочки MCB csegm и движется по цепочке.
Переход к следующему блоку производится прибавлением к адресу текущего MCB его поля size и еще 1. Перебор заканчивается
при достижении MCB со значением 'Z' в поле type.
Для каждого MCB выводится на экран:
12.2. Функции pаспpеделения памяти DOS
При обычной работе MS DOS сама занимается распределением памяти и предоставляет пользователю три функции выделения/освобождения памяти:
Следующий программный пример иллюстрирует работу системы
по выделению/освобождению памяти.
Функция init производит сканирование цепочки MCB и определяет адрес последнего блока памяти freetop. При принятой в
системе по умолчанию дисциплине распределения памяти старшие
адреса памяти представляют собой большой свободный блок, и
все последующие выделения памяти будут вестись за счет этого
свободного блока.
Функция memmap перебирает цепочку MCB, начиная с адреса
freetop и выдает карту распределения памяти от freetop до
конца памяти.
Функции memget, memfree, memnew обеспечивают обращения к
функциям DOS 0x48, 0x49, 0x4A соответственно.
При шагах 1-5 программы происходит выделение нескольких
блоков памяти по 64 параграфа каждый. По изменению карты памяти при этих шагах можно видеть, что новый блок памяти выделяется в начале свободного большого блока. Для оставшейся
части большого блока система строит новый MCB, в котором
этот блок помечается как свободный.
На шаге 5 изменяется (уменьшается) размер блока a2. По
карте памяти мы увидим, что блок a2 будет разбит на два блока: первый - занятый блок требуемого размера, а освободившаяся часть блока образует новый, свободный блок.
На шаге 6 выдается запрос на увеличение блока a2 до размера большего 64 параграфов. Этот запрос не может быть удовлетворен, так как размер блока может быть увеличен только в
том случае, если за ним следует свободный блок достаточного
размера. В нашем случае блок a2 будет увеличен до возможного
размера (64 параграфов), и будет индицирована ошибка.
Из карты, выдающейся на шагах 7, 8 видно, что при освобождении блока, в его MCB появляется отметка "свободен", при
освобождении возможно появление фрагментации памяти.
При поступлении запроса на блок большего размера (шаг
9), этот блок выделяется за счет последнего свободного блока, как и на шагах 1 - 5.
При поступлении запроса на блок меньшего размера (шаг
10), этот блок выделяется за счет свободного блока внутри
цепочки (в нашем случае - за счет блока a1. При этом, если
затребованный размер меньше размера свободного блока, выделяется затребованный объем памяти, а остаток образует свободный блок.
/*== ПРИМЕР 12.2 ==*/
/*============= Функции упpавления памятью DOS ===========*/
#include <dos.h>
#define byte unsigned char
#define word unsigned int
struct MCB { char type; word owner, size;
byte reserved[11]; } *cmcb; /* указатель на текущий MCB */
void init(void);
void memfree(byte *a);
byte *memget(word blksize);
byte *memnew(word blksize,byte *a);
void memmap(char *s);
word freetop; /* сегментный адpес начала свободной памяти */
word csegm; /* сегм.адрес текущего MCB */
byte *a[7]; /* указатели на блоки памяти */
union REGS rr;
struct SREGS sr;
main() {
clrscr();
init(); /* инициализация */
/* Шаги 1 - 5 - выделение новых блоков */
a[0]=memget(64); memmap("a[0]=memget(64)");
a[1]=memget(64); memmap("a[1]=memget(64)");
a[2]=memget(64); memmap("a[2]=memget(64)");
a[3]=memget(64); memmap("a[3]=memget(64)");
a[4]=memget(64); memmap("a[4]=memget(64)");
/* шаг 6 - уменьшение блока a2 */
memnew(50,a[1]); memmap("memnew(50,a[1])");
/* шаг 7 - попытка увеличения блока a2 */
memnew(80,a[1]); memmap("memnew(80,a[1])");
/* шаги 8,9 - освобождение блоков */
memfree(a[1]); memmap("memfree(a[1])");
memfree(a[3]); memmap("memfree(a[3])");
/* шаг 10 - выделение блока большего размера */
a[5]=memget(80); memmap("a[5]=memget(80)");
/* шаг 11 - выделение блока меньшего размера */
a[6]=memget(50); memmap("a[6]=memget(50)");
}
/*==== Выделение нового блока размером blksize ====*/
byte *memget(word blksize) {
rr.h.ah=0x48; /* функция 48 */
rr.x.bx=blksize; /* требуемый размер */
intdos(&rr,&rr);
if (rr.x.cflag)
printf("\7Неудовл.запрос mem=%d\n",rr.x.bx);
/* сегм. адрес блока - в AX */
else return(MK_FP(rr.x.ax,0));
}
/*==== Освобождение блока a ====*/
void memfree(byte *a) {
rr.h.ah=0x49; /* функция 49 */
sr.es=FP_SEG(a); /* сегм. адрес блока */
intdosx(&rr,&rr,&sr);
if (rr.x.flags&1) printf("\7Некорректный free\n");
}
/*==== Изменение размера блока a до blksize ====*/
byte *memnew(word blksize,byte *a) {
rr.h.ah=0x4a; /* функция 4A */
rr.x.bx=blksize; /* требуемый размер */
sr.es=FP_SEG(a); /* сегм. адрес блока */
intdosx(&rr,&rr,&sr);
if (rr.x.cflag)
printf("\7Неудовл.запрос на расширение mem=%d\n",rr.x.bx);
else return(MK_FP(rr.x.ax,0));
}
/*==== Определение адреса начала последнего (большого)
свободного блока ====*/
void init(void) {
/* получение адреса системных блоков */
rr.h.ah=0x52; intdosx(&rr,&rr,&sr);
csegm=peek(sr.es,rr.x.bx-2);
do { /* движение по цепочке MCB */
freetop=csegm;
cmcb=(struct MCB *)MK_FP(csegm,0);
/* переход к следующему блоку */
csegm+=(cmcb->size+1);
} while(cmcb->type!='Z');
memmap("Исходный блок");
}
/*==== Выдача карты памяти ====*/
void memmap(char *s) {
printf("\n***** %s *****\n",s);
csegm=freetop;
do { /* движение по цепочке MCB */
cmcb=(struct MCB *)MK_FP(csegm,0);
printf("Адрес=%04X Размер=%-5u ",csegm,cmcb->size);
if (cmcb->owner==0) printf("свободен\n");
else printf("занят\n");
/* переход к следующему блоку */
csegm+=(cmcb->size+1);
} while(cmcb->type!='Z');
getch();
}
Из работы программы предыдущего примера видно, что MS
DOS при выделении памяти использует дисциплину "первый подходящий" - память выделяется из первого в цепочке свободного
блока, размер которого больше или равен затребованному. В
DOS 3.0 и выше имеется возможность управлять дисциплиной
распределения памяти. Для этого служит функция DOS 0x58.
Подфункция 0 (AL=0) этой функции позволяет прочитать установленную дисциплину (результат - в AX). Подфункция 1 (AL=1)
позволяет задать (в BX) дисциплину. Возможны три дисциплины:
0 - первый подходящий (установлена по умолчанию), 1 - последний подходящий, 2 - самый подходящий.
В программном примере функции getorder и setorder обеспечивают обращение к функции DOS 0x58 для чтения/установки
дисциплины.
Функции init, memmap, memget и memfree работают аналогично одноименным функциям предыдущего примера.
/*== ПРИМЕР 12.3 ==*/
/*=========== Дисциплины распределения памяти ===========*/
#include <dos.h>
#define byte unsigned char
#define word unsigned int
void memfree(byte *a);
byte *memget(int blksize);
void setorder(byte d);
byte getorder();
void init(void);
void memmap(int x);
struct MCB { byte type; word owner, size;
byte reserved[11]; } *c;
word freetop, cs;
union REGS rr;
struct SREGS sr;
main()
{
byte *a[10] ,*b, order;
word sz, i;
init(); /* составление списка исходных блоков */
/* Формирование фрагментированной памяти */
for (sz=84,i=0; i<10; i++) {
a[i]=memget(sz); b=memget(8);
if (i>=5) sz+=4; else sz-=4;
}
for (i=0; i<10; i++) memfree(a[i]);
order=getorder();
clrscr();
printf(" Исходное |Дисциплина_0");
printf("|Дисциплина_1|Дисциплина_2|\n");
printf("------------|------------");
printf("|------------|------------|");
memmap(1); /* отображение памяти */
setorder(0); /* установка дисциплины */
b=memget(70); /* выделение памяти */
memmap(14); /* отображение памяти */
memfree(b); /* возврат к исходному распределению */
setorder(2); b=memget(70); memmap(27); memfree(b);
setorder(1); b=memget(70); memmap(40); memfree(b);
setorder(order); /* восстановление исходной дисциплины */
}
/*==== Установка дисциплины распределения ====*/
void setorder(byte d) {
rr.h.ah=0x58; /* Ф-ция 58 */
rr.h.al=1; /* Установить */
rr.x.bx=d; /* Дисциплина */
intdos(&rr,&rr);
}
/*==== Чтение дисциплины распределения ====*/
byte getorder() {
rr.h.ah=0x58; /* Ф-ция 58 */
rr.h.al=0; /* Прочитать */
intdos(&rr,&rr);
return (rr.x.ax); /* Дисциплина - в AX */
}
/*==== Выделение памяти ====*/
byte *memget(int blksize) {
rr.h.ah=0x48; rr.x.bx=blksize; intdos(&rr,&rr);
if (rr.x.cflag)
printf("\7Неудовл.запрос mem=%d\n",rr.x.bx);
else return(MK_FP(rr.x.ax,0));
}
/*==== Освобождение памяти ====*/
void memfree(byte *a) {
rr.h.ah=0x49; sr.es=FP_SEG(a); intdosx(&rr,&rr,&sr);
if (rr.x.cflag) printf("\7Некорректный free\n");
}
/*==== Определение адреса последнего свободного блока ====*/
void init(void) {
rr.h.ah=0x52; intdosx(&rr,&rr,&sr);
cs=peek(sr.es,rr.x.bx-2);
do {
freetop=cs; c=(struct MCB *)MK_FP(cs,0);
cs+=(c->size+1);
} while(c->type!='Z');
}
/*==== Выдача карты памяти ====*/
void memmap(int x) {
int y;
cs=freetop; y=3;
do {
c=(struct MCB *)MK_FP(cs,0); cs+=(c->size+1);
gotoxy(x,y++); printf("%04X %-4u",cs,c->size);
if (c->owner==0) printf("...|\n");
else printf("***|\n");
} while(c->type!='Z');
}
Для наглядности эксперимента программа формирует фрагментированную память. Карта памяти в исходном состоянии выглядит (рис.
12.1 ) следующим образом. В памяти имеется набор свободных участков, размер которых вначале убывает от 84 до 64 параграфов, а затем возрастает до 84 параграфов. Свободные участки перемежаются
занятыми по 8 параграфов каждый (размер 8 параграфов выбран для
того, чтобы на картину распределения не повлияли маленькие свободные участки, которые, иногда имеются в начале распределяемой
памяти).
Затем программа устанавливает дисциплину распределения,
запрашивает блок размером 70 параграфов и выводит новое
распределение памяти. После вывода запрошенная память освобождается, то есть восстанавливается исходное распределение.
Проведем анализ результатов выполнения этой программы,
сведенных в таблицу (см. рис. 12.1).
При дисциплине распределения 0 первым подходящим оказывается самый первый свободный блок, его размер - 84 параграфа. В начале его образуется занятый блок размером 70 параграфов, а остаток образует свободный блок размером 13
параграфов (учтите, что один параграф потребовался для нового MCB).
| Исходное распределение | Дисциплина 0 | Дисциплина 1 | Дисциплина 2 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| a | v | s | a | v | s | a | v | s | a | v | s | |
| 7433 | 84 | - | 7433 | 70 | + | 7433 | 84 | - | 7433 | 84 | - | |
| 7488 | 8 | + | 747A | 13 | - | 7488 | 8 | + | 7488 | 8 | + | |
| 7491 | 80 | - | 7488 | 8 | + | 7491 | 80 | - | 7491 | 80 | - | |
| 74E2 | 8 | + | 7491 | 80 | - | 74E2 | 8 | + | 74E2 | 8 | + | |
| 74EB | 76 | - | 74E2 | 8 | + | 74EB | 76 | - | 74EB | 76 | - | |
| 7538 | 8 | + | 74EB | 76 | - | 7538 | 8 | + | 7538 | 8 | + | |
| 7541 | 72 | - | 7538 | 8 | + | 7541 | 72 | - | 7541 | 70 | + | |
| 758A | 8 | + | 7541 | 72 | - | 758A | 8 | + | 7588 | 1 | - | |
| 7593 | 68 | - | 758A | 8 | + | 7593 | 68 | - | 758A | 8 | + | |
| 75D8 | 8 | + | 7593 | 68 | - | 75D8 | 8 | + | 7593 | 68 | - | |
| 75E1 | 64 | - | 75D8 | 8 | + | 75E1 | 64 | - | 75D8 | 8 | + | |
| 7622 | 8 | + | 75E1 | 64 | - | 7622 | 8 | + | 75E1 | 64 | - | |
| 762B | 68 | - | 7622 | 8 | + | 762B | 68 | - | 7622 | 8 | + | |
| 7670 | 8 | + | 762B | 68 | - | 7670 | 8 | + | 762B | 68 | - | |
| 7679 | 72 | - | 7670 | 8 | + | 7679 | 72 | - | 7670 | 8 | + | |
| 76C2 | 8 | + | 7679 | 72 | - | 76C2 | 8 | + | 7679 | 72 | - | |
| 76CB | 76 | - | 76C2 | 8 | + | 76CB | 76 | - | 76C2 | 8 | + | |
| 7718 | 8 | + | 76CB | 76 | - | 7718 | 8 | + | 76CB | 76 | - | |
| 7721 | 80 | - | 7718 | 8 | + | 7721 | 80 | - | 7718 | 8 | + | |
| 7772 | 8 | + | 7721 | 80 | - | 7772 | 8 | + | 7721 | 80 | - | |
| 777B | 10372 | - | 7772 | 8 | + | 777B | 10301 | - | 7772 | 8 | + | |
| . | . | . | 777B | 10372 | - | 9FB9 | 70 | + | 777B | 10372 | - | . |
| Рис. 12.1. Дисциплины распределения памяти: | a - сегментный адрес блока; |
| v - объем блока в параграфах; | |
| s - состояние блока: "+" - занят, "-" - свободен. |
При дисциплине распределения 1 последним подходящим оказывается самый последний блок, включающий в себя весь остаток памяти. Причем память под новый занятый блок при этой
дисциплине выделяется не из начальной, а из конечной части
этого блока, то есть распределение памяти здесь ведется от
конца к началу.
При дисциплине распределения 2 самым подходящим оказывается свободный блок размером 72 параграфа (предыдущие блоки
имели больший размер, последующие - меньший). Память выделяется в начале этого блока, остаток образует свободный блок
размером всего 1 параграф.
Специалистам хорошо известоно, что наилучшие показатели в большинстве применений обеспечивает дисциплина "первый подходящий".
| Каталог | Индекс раздела |
| Назад | Оглавление | Вперед |