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

MS-DOS для программиста

© Александр Фролов, Григорий Фролов
Том 18, М.: Диалог-МИФИ, 1995, 254 стр.

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

2.5. Список управляющих блоков устройств

Поле dev_cb векторной таблицы связи содержит дальний адрес списка блоков управления устройствами MS-DOS (MS-DOS Device Control Block), который мы назвали DDCB . Блок DDCB строится операционной системой для каждого дискового устройства и содержит информацию о характеристиках этого устройства, указатель на заголовок драйвера, обслуживающего данное устройство и многое другое.

Как мы уже говорили, блок DDCB может быть использован программами, которые выполняют доступ к диску на уровне секторов. Подробнее назначение и использование полей блока DDCB будет описано в разделе, посвященном файловой системе, так как эта информация требуется в основном для организации работы с диском на низком уровне.

Формат блока управления устройствами DDCB

Ниже мы привели формат блока управления устройствами DDCB , адрес которого доступен через векторную таблицу связи MS-DOS.

Смещение, байт Размер, байт Имя поля Описание
0 1 drv_num Номер устройства (0 соответствует устройству А:, 1 - В: и т. д.)
1 1 drv_numd Дополнительный номер устройства (используется, если драйвер обслуживает несколько устройств)
2 2 sec_size Размер сектора в байтах
4 1 clu_size Число, на единицу меньшее количества секторов в кластере
5 1 clu_base Если содержимое этого поля не равно нулю, то для получения общего числа секторов в кластере надо возвести 2 в степень clu_base и получившееся число прибавить к clu_size
6 2 boot_siz Количество зарезервированных секторов (загрузочные секторы, начало корневого каталога)
8 1 fat_num Количество копий таблицы размещения файлов FAT
9 2 max_dir Максимальное количество дескрипторов файлов в корневом каталоге (т. е. максимальное количество файлов, которое может содержать корневой каталог на этом устройстве)
11 2 data_sec Номер первого сектора данных на диске (номер сектора, соответствующего кластеру с номером 2)
13 2 hi_clust Максимальное количество кластеров (равно количеству кластеров данных, увеличенному на 1)
15 1 fat_size Количество секторов, занимаемых одной копией FAT
16 1 reserv1 Зарезервировано
17 2 root_sec Номер первого сектора корневого каталога
19 4 drv_addr Дальний адрес заголовка драйвера, обслуживающего данное устройство
23 1 media Байт описания среды носителя данных
24 1 acc_flag Флаг доступа, 0 означает, что к устройству был доступ
25 4 next Адрес следующего блока DDCB .Для последнего блока в поле смещения находится число FFFFh
29 2 reserv2 Зарезервировано
31 2 built Значение 52EEh в этом поле означает, что блок DDCB был сформирован

Мы приведем также описание формата блока DDCB в следующем виде:

typedef struct 
{
  unsigned char drv_num;
  unsigned char drv_numd;
  unsigned sec_size;
  unsigned char clu_size;
  unsigned char clu_base;
  unsigned boot_siz;
  unsigned char fat_num;
  unsigned max_dir;
  unsigned data_sec;
  unsigned hi_clust;
  unsigned char fat_size;
  char reserv1;
  unsigned root_sec;
  void far *drv_addr;
  unsigned char media;
  unsigned char acc_flag;
  struct _DDCB _ far *next;
  unsigned reserv2;
  unsigned built;
} DDCB ;

Еще раз уместно заметить, что формат этого блока не описан в документации по MS-DOS, поэтому он может отличаться в различных версиях операционных систем.

Программа DDCBLIST

Программа DDCBLIST (листинг 2.3) выводит на консоль содержимое всех блоков DDCB в расшифрованном виде. Так как при большом количестве дисков выводится очень много информации, следует использовать средство переназначения стандартного устройства вывода MS-DOS, запустив программу следующим образом:

ddcblist > ddcb.txt

Затем вы сможете просмотреть полученный файл при помощи любого текстового редактора.


Листинг 2.3. Файл ddcblist\ddcblist.cpp


#include <dos.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
  unsigned mcb_seg;
  void far *dev_cb;
  void far *file_tab;
  void far *clock_dr;
  void far *con_dr;
  unsigned max_btbl;
  void far *disk_buf;
  void far *drv_info;
  void far *fcb_tabl;
  unsigned fcb_size;
  unsigned char num_bdev;
  unsigned char lastdriv;
} CVT ;
typedef CVT  far* LPCVT ;

typedef struct _DDCB _
{
  unsigned char drv_num;
  unsigned char drv_numd;
  unsigned sec_size;
  unsigned char clu_size;
  unsigned char clu_base;
  unsigned boot_siz;
  unsigned char fat_num;
  unsigned max_dir;
  unsigned data_sec;
  unsigned hi_clust;
  unsigned char fat_size;
  char reserv1;
  unsigned root_sec;
  void far *drv_addr;
  unsigned char media;
  unsigned char acc_flag;
  struct _DDCB _ far *next;
  unsigned reserv2;
  unsigned built;
} DDCB ;
typedef DDCB  far* LPDDCB;

void main(void);
LPDDCB get_fddcb(LPCVT  cvt);
LPDDCB get_nddcb(LPDDCB ddcb);

void main(void)
{
  union REGS  regs;
  struct SREGS  sregs;
  LPCVT      lpCVT;
  LPDDCB    lpDDCB;

  printf("\nБлоки управления дисковыми устройствами DDCB "
    "\n(C) Фролов А.В., 1995\n\n");

  // Получаем адрес векторной таблицы связи
  regs.h.ah = 0x52;
  intdosx (&regs, &regs, &sregs);

  // Передвигаем указатель на поле msb_seg
  lpCVT = (LPCVT )MK_FP (sregs.es, regs.x.bx - 2);

  // Получаем адрес первого блока DDCB 
  lpDDCB = get_fddcb(lpCVT);

  for(;;)
  {
    // Если это последний блок, завершаем цикл
    if(lpDDCB == NULL) break;
      printf("Адрес DDCB :                         %Fp\n"
	"Номер устройства:                   %d\n"
	"Дополнительный номер:               %d\n"
	"Размер сектора:                     %d\n"
	"Размер кластера в секторах:         %d\n"
	"База размера кластера:              %d\n"
	"Зарезервировано секторов:           %d\n"
	"Число копий FAT :                    %d\n"
	"Макс. файлов в корневом каталоге :  %d\n"
	"Первый кластер данных:              %d\n"
	"Всего кластеров:                    %ld\n"
	"Размер FAT  в секторах:              %d\n"
	"Первый сектор корневого каталога:   %d\n"
	"Поле reserv1:                       %01X\n"
	"Адрес драйвера:                     %Fp\n"
	"Байт описателя среды носителя:      %01X\n"
	"Флаг доступа:                       %01X\n"
	"Адрес следующего DDCB :              %Fp\n"
	"Поле reserv2:                       %04X\n"
	"Блок заполнен:                      %04X\n"
	"-------------------------------------\n\n",
	 lpDDCB, lpDDCB->drv_num,  lpDDCB->drv_numd,
	 lpDDCB->sec_size, lpDDCB->clu_size,
	 lpDDCB->clu_base, lpDDCB->boot_siz,
	 lpDDCB->fat_num,  lpDDCB->max_dir,
	 lpDDCB->data_sec, lpDDCB->hi_clust,
	 lpDDCB->fat_size, lpDDCB->root_sec,
	 lpDDCB->reserv1,  lpDDCB->drv_addr,
	 lpDDCB->media,    lpDDCB->acc_flag,
	 lpDDCB->next,     lpDDCB->reserv2,
	 lpDDCB->built);

      // Получаем адрес следующего блока DDCB 
      lpDDCB = get_nddcb(lpDDCB);
  }
}

// ---------------------------------------------
// get_fddcb
// Функция возвращает адрес первого блока DDCB .
// В качестве параметра ей следует передать
// адрес векторной таблицы связи
// ---------------------------------------------
LPDDCB get_fddcb(LPCVT  cvt)
{
  LPDDCB ddcb;
  ddcb = (LPDDCB)cvt->dev_cb;
  return(ddcb);
}

// ---------------------------------------------
// get_nddcb
// Функция возвращает адрес следующего блока DDCB 
// ---------------------------------------------
LPDDCB get_nddcb(LPDDCB ddcb)
{
  LPDDCB ddcb_n;

  ddcb_n = (LPDDCB)ddcb->next;
  if(FP_OFF (ddcb_n) == 0xffff)
    return((LPDDCB)NULL);
  return(ddcb_n);
}

Программа DDCBLST1

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

Функция 32h получает в регистре DL номер устройства (0 - текущий диск , 1 - А: и т. д.) и возвращает в регистрах DS:BX адрес соответствующего DDCB . Если номер устройства был задан неправильно, после выполнения функции регистр AL будет содержать значение FFh.

Если требуется получить адрес DDCB для НГМД , необходимо установить дискету в приемный карман накопителя.

Программа, исходный текст который приведен в листинге 2.4, выводит адреса всех DDCB в виде списка. Можете запустить ее (она есть на дискете, которая продается вместе с книгой) и посмотреть, что получится. Перед запуском не забудьте вставить дискеты во все НГМД .


Листинг 2.4. Файл ddcblst1\ddcblst1.cpp


#include <dos.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct _DDCB _
{
  unsigned char drv_num;
  unsigned char drv_numd;
  unsigned sec_size;
  unsigned char clu_size;
  unsigned char clu_base;
  unsigned boot_siz;
  unsigned char fat_num;
  unsigned max_dir;
  unsigned data_sec;
  unsigned hi_clust;
  unsigned char fat_size;
  char reserv1;
  unsigned root_sec;
  void far *drv_addr;
  unsigned char media;
  unsigned char acc_flag;
  struct _DDCB _ far *next;
  unsigned reserv2;
  unsigned built;
} DDCB ;
typedef DDCB  far* LPDDCB;

void main(void);
LPDDCB get_ddcb(unsigned char device_number);

void main(void)
{
 LPDDCB lpDDCB;
 unsigned char dr;

 for(dr=1;; dr++)
 {
   lpDDCB = get_ddcb(dr);
   if(lpDDCB == NULL) break;
     printf("%Fp\n", lpDDCB);
 }
}

LPDDCB get_ddcb(unsigned char device_number)
{
  union  REGS   regs;
  struct SREGS  sregs;

  regs.h.ah = 0x32;
  regs.h.al = 0;
  regs.h.dl = device_number;
  intdosx ( &regs, &regs, &sregs );
  if(regs.h.al == 0xff)
    return(LPDDCB)NULL;

  return((DDCB  far*)MK_FP (sregs.ds, regs.x.bx));
}

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