Операционная система MS-DOS© Александр Фролов, Григорий ФроловТом 1, книги 1-2, М.: Диалог-МИФИ, 1991. 2.3. Список управляющих блоков устройствПоле dev_cb векторной таблицы связи содержит FAR-адрес цепочки блоков управления устройствами DOS (DOS Device Control Block) - DDCB. Блок DDCB строится операционной системой для каждого дискового устройства и содержит информацию о характеристиках этого устройства и указатель на заголовок драйвера, обслуживающего данное устройство. Этот блок может быть использован программами, которые выполняют доступ к диску на уровне секторов. Подробнее назначение и использование полей блока DDCB будет описано в разделе, посвященном файловой системе, так как эта информация требуется в основном для организации работы с диском на низком уровне. Приведем формат блока DDCB для DOS версий 2.х и 3.х:
Для DOS версии 4.х формат этого блока другой. Кроме того, изменилась его длина:
Файл sysp.h содержит определение типа DDCB для MS-DOS версии 4.х и 5.0: /* Блок управления устройством DOS */ #pragma pack(1) 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; #pragma pack() Еще раз уместно заметить, что формат этого блока не описан в документации по MS-DOS, поэтому он может отличаться в различных версиях операционных систем. Приведем тексты программ для получения адресов первого и последующих блоков DDCB: /** *.Name get_fddcb * *.Title Получить адрес первого DDCB * *.Descr Функция возвращает адрес первого блока DDCB * *.Params DDCB far *get_fddcb(CVT far *cvt) * * cvt - адрес векторной таблицы связи * *.Return Указатель на первый блок DDCB **/ #include <stdlib.h> #include <stdio.h> #include "sysp.h" DDCB far *get_fddcb(CVT far *cvt) { DDCB far * ddcb; ddcb = cvt->dev_cb; return(ddcb); } /** *.Name get_nddcb * *.Title Получить адрес следующего DDCB * *.Descr Функция возвращает адрес следующего блока DDCB * или 0, если это последний блок в цепочке * *.Params DDCB far *get_nddcb(DDCB far *ddcb) * * ddcb - адрес предыдущего DDCB * *.Return Указатель на следующий блок DDCB * или 0, если это последний блок в цепочке **/ #include <dos.h> #include "sysp.h" DDCB far *get_nddcb(DDCB far *ddcb) { DDCB far *ddcb_n; ddcb_n = ddcb->next; if(FP_OFF(ddcb_n) == 0xffff) return((DDCB far *)0); return(ddcb_n); } С помощью приведенной ниже программы можно просмотреть содержимое всех блоков DDCB. Так как при большом количестве дисков выводится очень много информации, следует использовать средство переназначения стандартного устройства вывода DOS: show_ddc > drives.lst Эта программа проверена для версии MS/DOS 4.01. #include <dos.h> #include <stdio.h> #include <stdlib.h> #include "sysp.h" void main(void); void main(void) { CVT far *cvt; DDCB far *ddcb; printf("\nБлоки управления дисковыми устройствами (DDCB)" "\nCopyright (C)Frolov A., 1990\n" "\n"); cvt=get_mcvt(); ddcb=get_fddcb(cvt); for(;;) { if(ddcb == (DDCB far *)0) break; printf("Адрес DDCB: %Fp\n" "Номер устройства: %d\n" "Дополнительный номер: %d\n" "Размер сектора: %d\n" "Размер кластера в секторах: %d\n" "База размера кластера: %d\n" "Зарезервировано секторов: %d\n" "Число копий FAT: %d\n" "Макс. файлов в корневом каталоге : %d\n" "Первый кластер данных: %d\n" "Всего кластеров: %d\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", ddcb, ddcb->drv_num, ddcb->drv_numd, ddcb->sec_size, ddcb->clu_size, ddcb->clu_base, ddcb->boot_siz, ddcb->fat_num, ddcb->max_dir, ddcb->data_sec, ddcb->hi_clust, ddcb->fat_size, ddcb->root_sec, ddcb->reserv1, ddcb->drv_addr, ddcb->media, ddcb->acc_flag, ddcb->next, ddcb->reserv2, ddcb->built); ddcb=get_nddcb(ddcb); } exit(0); } Приведенный выше способ получения доступа к блокам DDCB больше всего подходит для просмотра блоков управления всеми дисковыми устройствами. Если вам требуется получить DDCB для какого-нибудь конкретного устройства, можно воспользоваться недокументированной функцией 32H прерывания INT 21H (со всеми ограничениями, связанными с использованием недокументированных возможностей). Функция 32H получает в регистре DL номер устройства (0 - текущий диск, 1 - А: и т.д.) и возвращает в регистровой паре DS:BX адрес соответствующего DDCB. Если номер устройства был задан неправильно, регистр AL после выполнения функции будет содержать значение FF. Если требуется получить адрес DDCB флоппи-диска, необходимо установить диск в приемный карман дисковода. Приведем текст программы, возвращающей указатель на DDCB диска с заданным номером: /** *.Name get_ddcb * *.Title Получить адрес DDCB заданного диска * *.Descr Функция возвращает адрес блока управления * устройством DOS DDCB * *.Params DDCB far *get_ddcb(int device_number) * * device_number - номер диска, для которого * требуется получить DDCB * Номер задается так: * 0 - текущий диск, 1 - В и т.д. * *.Return Указатель на DDCB заданного диска **/ #include <dos.h> #include <stdio.h> #include "sysp.h" DDCB far *get_ddcb(unsigned char device_number) { union REGS inregs, outregs; struct SREGS segregs; inregs.h.ah = 0x32; inregs.h.al = 0; inregs.h.dl = device_number; intdosx( &inregs, &outregs, &segregs ); if(outregs.h.al == 0xff) return(DDCB far *)0; return((DDCB far*)FP_MAKE(segregs.ds,outregs.x.bx)); } Программа, приведенная ниже, выводит адреса всех DDCB. Можете запустить ее (она есть на дискете, прилагающейся к книге) и посмотреть, что получится. Не забудьте вставить флоппи-диски во все дисководы. #include <dos.h> #include <stdio.h> #include <stdlib.h> #include "sysp.h" void main(void); void main(void) { DDCB far *ddcb; unsigned char dr; for(dr=1;;dr++) { ddcb=get_ddcb(dr); if(ddcb == (DDCB far *)0) break; printf("%Fp\n",ddcb); } exit(0); } |