КаталогИндекс раздела
НазадОглавлениеВперед


Лабораторная работа 12
УПРАВЛЕНИЕ ПАМЯТЬЮ

1. Цель работы

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

2. Темы для предварительной проработки

3. Постановка задачи

Вывести на экран карту распределяемой памяти на своем рабочем месте.

4. Порядок выполнения работы

5. Пример решения задачи

5.1. Разработка алгоритма решения

5.1.1. Структура программы Программа состоит из основной программы и двух функций.

5.1.2. Описание переменных Переменные, глобальные для всей программы:

5.1.3. Описание алгоритма программы

В начале выполнения программы определим номер версии DOS, установленной на данной ПЭВМ. Затем с помощью функции 52h DOS (прерывание 21h) определим адрес системных управляющих блоков:

     Вход:   AH = 52h
     Выход:  ES:BX - адрес 1-го блока параметров диска. Вычтя из этого адреса 2, получим адрес того слова памяти в котором DOS хранит сегментный адрес первого управляющего блока памяти.

Каждый блок памяти пpедваpяется Упpавляющим Блоком Памяти (MCB - Memory Control Block). MCB имеет фиксиpованный pазмеp 1 паpагpаф и фоpмат, описываемый следующей стpуктуpой:

       struct MCB {
         byte type;        /* тип */
         word owner;       /* владелец */
         word size;        /* размер */
         byte reserved[3]; /* не используется */
         char pgmname[8];  /* имя (только DOS 4.0 и выше) */
         };

Поле type содеpжит код, показывающий, является ли этот MCB последним (код буквы Z - 5Ah.) или непоследним (код буквы M - 4Dh). Поле owner содеpжит PID (сегментный адpес пpефикса пpогpаммного сегмента) пpогpаммы, котоpой данный блок памяти пpинадлежит. Если значение этого поля нулевое, то блок свободен. Поле size содеpжит pазмеp блока памяти в паpагpафах (в это число не включен 1 паpагpаф, занимаемый самим MCB). Следующие 3 байта (поле reserved) заpезеpвиpованы во всех веpсиях. Поле pgmname заpезеpвиpовано (не используется) в веpсиях DOS ниже 4.0. Начиная с веpсии 4.0, в MCB, пpедваpяющем пpогpаммный сегмент, здесь записано имя (без pасшиpения) пpогpаммы, находящейся в этом сегменте (если длина имени меньше 8 символов, оно заканчивается нулевым байтом).

Все MCB увязаны в цепочку. Получив при помощи функции DOS 52h сегментный адрес начала цепочки MCB csegm, движемся по цепочке. Переход к следующему блоку производится прибавлением к адресу текущего MCB его поля size и еще 1. Перебор заканчивается при достижении MCB со значением 'Z' в поле type.

Для каждого MCB выводится на экран:

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

Класс блока определяется по следующим правилам. Если PID владельца блока нулевой, блок является свободным (класс Free). Для занятых блоков класс уточняется. Если PID (сегментный адрес PSP) владельца содержит адрес, лежащий до вершины распределяемой памяти memtop (обычно это число 8), то блок получает класс Dos. Для блоков, не принадлежащих DOS, прежде всего проверяется сегментный адрес владельца. Если этот адрес является адресом сегмента, следующего за текущим MCB, это означает, что блок памяти содержит программный сегмент и получает класс Pgm. В противном случае программа "заглядывает" в PSP владельца. Со смещением 2Ch в PSP содержится адрес сегмента окружения; если этот адрес является адресом сегмента, следующего за текущим MCB, то блок получает класс Env. Если класс блока не удается определить ни одним из вышеописанных способов, считаем, что блок содержит данные и помечаем его классом Data.

Для DOS 4.0 и выше определяем символьный идентификатор владельца, для этого "заглядываем" в MCB того блока, на который указывает поле owner текущего блока, и выводим его поле pgmname.

Только DOS 4.0 позволяют получить идентификатор командного процессора DOS вышеописанным образом. Однако, в ранних версиях DOS можно опознать блок памяти, принадлежащий командному процессору вот каким способом. В PSP владельца со смещением 16h находится сегментный адрес "родителя" - программы, запустившей данную программу. Для программ, запущенных из командной строки DOS, родителем является COMMAND.COM. Родителем же COMMAND.COM является он сам. По этому признаку (сам себе родитель) он и может быть опознан.

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

Функция get_DOS_ver_h() определяет старшее число номера версии DOS, используя для этого функцию 30h DOS (прерывание 21h), которая возвращает в регистре AL старшее число номера версии, а в регистре AH - младшее число. Нас интересует только значение регистра AL.

Функция print_head() выводит заголовок лабораторной работы (номер лабораторной работы, тема и т.д.).

5.2. Текст программы

/*----------------Лабораторная работа N12-----------------*/
/*------------------"Управление памятью"------------------*/

/* Типы данных */
#define byte unsigned char
#define word unsigned int


/* Подключение стандартных заголовков */
#include <dos.h>
#include <string.h>

void get_DOS_ver_h(void);  /* получить номер версии DOS   */
void print_head(void);     /* вывод заголовка лаб. работы */

struct MCB { /* блок упpавления памятью */
  char type; /* тип */
  word owner,/* владелец */
       size; /* размер */
  byte reserved[3]; /* зарезервировано */
  char pgmname[8];  /* имя программы, находящейся в данном блоке
                              ( для  DOS 4.0 и выше )         */
   };

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,n;
char *s;

void main()
{
  print_head();
  n=0; /* Число выведенных на экран блоков */
  get_DOS_ver_h();
  /* получить адрес системных блоков */
  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);
    textattr(14);
    cprintf("Addr=%04X:0000  ",csegm);
    textattr(13);
    cprintf("PID=%04X  ",cmcb->owner);
    textattr(11);
    cprintf("Size=%-6u ",cmcb->size*16);
    n++;/* Счетчик выведенных блоков увеличить на 1 */
    if (cmcb->owner==0)
    {
     textattr(15);
     cprintf(" Free"); /* блок свободен */
    }
    else {  /* блок занят */
      /* блок принадлежит DOS ? */
     if (cmcb->owner<memtop)
     {
      textattr(13);
      cprintf(" Dos ");
     }
     else {  /* блок не принадлежит DOS */
        /* если PID хозяина указывает на текущий блок,
        то это программный сегмент */
       if (csegm==cmcb->owner-1)
       {
         textattr(10);
         cprintf(" Pgm   ");
       }
       else {
          /* адpес блока сpеды для хозяина этого блока памя-
          ти находится в PSP хозяина со смещением 0x2C */
         othersegm=peek(cmcb->owner,0x2c);
          /* адpес родителя для программы-хозяина этого бло-
          ка находится в PSP хозяина со смещением 0x16 */
         fathersegm=peek(cmcb->owner,0x16);
          /* если на текущий блок указывает адрес окружения
            хозяина, то это блок окружения */
         if (csegm==othersegm-1)
         {
          textattr(11);
          cprintf(" Env   ");
         }
          /* иначе - это блок данных */
         else
         {
          textattr(14);
          cprintf(" Data  ");
         }
       }
        /* если хозяин сам себе родитель, то это COMMAND */
          if (cmcb->owner==fathersegm)
          {
            textattr(13);
            cprintf("COMMAND.COM");
          }
          else { /* для другой программы узнаем ее имя */
           textattr(15);
           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)
                {
                 textattr(11);
                 cprintf("%s",envstr);
                }
           }
          }
        }
      }
    printf("\n");
    csegm+=(cmcb->size+1); /* переход к следующему блоку */
    if(n==20)
    { /* Весь экран занят информацией о блоках */
     textattr(0x0a);
     gotoxy(30,24);
     cprintf("Нажмите любую клавишу");
     getch();
     n=0;
     print_head();
    }
  }
  while (cmcb->type!='Z');  /* выход по последн.блоку */
  textattr(0x0a);
  gotoxy(20,24);
  cprintf("Нажмите любую клавишу для возврата в DOS.");
  textattr(0x07);
  getch();
  clrscr();
}

/* получить номер версии DOS */
void get_DOS_ver_h(void)
{
  rr.h.ah=0x30;
  intdos(&rr,&rr);
  dos=rr.h.al;
}

/* вывод заголовка лаб. работы */
void print_head(void)
{
  textbackground(0);
  clrscr();
  textattr(0x0a);
  cprintf("---------------");
  cprintf("              Лабораторная работа N12              ");
  cprintf("---------------");
  cprintf("---------------");
  cprintf("                 Управление памятью               ");
  cprintf("---------------");
  gotoxy(28,3);
  textattr(13);
  cprintf("Карта распределяемой памяти.\n\r");
}

5.3. Результаты работы программы

В процессе работы программы на экран была выведена следующая информация о распределении памяти:

--------------------------------------------------------------------------------
---------------              Лабораторная работа N12             ---------------
---------------                 Управление памятью               ---------------
                           Карта распределяемой памяти.
Addr=025B:0000  PID=0008  Size=9130    Dos
Addr=0496:0000  PID=0008  Size=64      Dos
Addr=049B:0000  PID=04A0  Size=48      Data  COMMAND.COM
Addr=049F:0000  PID=04A0  Size=2640    Pgm   COMMAND.COM
Addr=0545:0000  PID=04A0  Size=64      Data  COMMAND.COM
Addr=054A:0000  PID=04A0  Size=513     Env   COMMAND.COM
Addr=056B:0000  PID=0008  Size=32      Dos
Addr=056E:0000  PID=06CB  Size=160     Env   NC       C:\NC\NC.EXE
Addr=0579:0000  PID=057A  Size=5376    Pgm   KEYRUS   C:\NC\NC.EXE
Addr=06CA:0000  PID=06CB  Size=13152   Pgm   NC       C:\NC\NC.EXE
Addr=0A01:0000  PID=0AFA  Size=160     Data  COMMAND.COM
Addr=0A0C:0000  PID=0AFA  Size=256     Env   COMMAND.COM
Addr=0A1D:0000  PID=0BA0  Size=160     Env   TC_LAB12 D:\TC\TC_LAB12.EXE
Addr=0A28:0000  PID=0000  Size=2464    Free
Addr=0AC3:0000  PID=0ACF  Size=160     Env   COPY_ECR D:\TC\COPY_ECR.COM
Addr=0ACE:0000  PID=0ACF  Size=672     Pgm   COPY_ECR D:\TC\COPY_ECR.COM
Addr=0AF9:0000  PID=0AFA  Size=2640    Pgm   COMMAND  D:\TC\COPY_ECR.COM
Addr=0B9F:0000  PID=0BA0  Size=17408   Pgm   TC_LAB12 D:\TC\COPY_ECR.COM
Addr=0FE0:0000  PID=0000  Size=65008   Free

                   Нажмите любую клавишу для возврата в DOS.
--------------------------------------------------------------------------------


КаталогИндекс раздела
НазадОглавлениеВперед