4. Использование функций IOCTL
В главе предыдущего тома "Библиотеки системного программиста", посвященной драйверам, мы рассказывали о функции управления вводом/выводом IOCTL - функции 44h прерывания INT 21h . Эта функция предоставляет широкие возможности по управлению устройствами посредством обмена управляющей информацией с драйверами устройств. В этой главе мы вновь вернемся к функциям IOCTL для того чтобы рассказать об их использовании при работе с дисковой системой компьютера. Мы покажем, как использовать функцию 44h прерывания INT 21h для извлечения разнообразной информации об открытых файлах по их идентификатору, для определения момента достижения конца файла, для получения информации о НГМД и НМД и для выполнения таких низкоуровневых операций, как форматирование дорожки диска, чтение или запись секторов диска и т. д. Напомним, при вызове функции 44h регистр AL содержит код выполняемой подфункции. Для подфункции 0Dh (Generic IO Control) в регистре CL должен находиться код выполняемой операции. 4.1. Получение различной информацииС помощью описанных ниже подфункций программа может получить разнообразную информацию об открытом файле или устройстве. Состояние открытого файла и конфигурация устройстваС помощью подфункции 00h, описанной в предыдущем томе, можно получить информацию об открытом файле или о конфигурации устройства по его идентификатору. Функция возвращает в регистре DX слово
конфигурации устройства, которое имеет
следующий формат:
Если при вызове этой подфункции регистр BX
содержит идентификатор файла, формат информации,
получаемой в регистре DX, будет следующий:
Определение момента достижения конца файлаПодфункция 06h функции 44h прерывания INT 21h поможет вам определить момент достижения конца файла или готовность устройства посимвольной обработки. Для проверки условия "Конец файла" или готовности устройства можно использовать следующую функцию: /** * heof * * Проверить условие "Конец файла" * * Функция позволяет проверить факт достижения * конца файла или готовность устройства * * int heof(int handle); * * handle - идентификатор файла или устройства, * для которого необходимо получить * информацию о состоянии * * Возвращаемо значение; * 0 - конец файла не достигнут (для файла), * устройство готово (для устройства); * * 1 - достигнут конец файла (для файла), * устройство не готово (для устройства); * * -1 - произошла ошибка. **/ int heof(int handle) { union REGS reg; // Заполняем регистровые структуры для вызова // прерывания DOS INT 21h. Код используемой // подфункции - 06h reg.x.ax = 0x4406; reg.x.bx = handle; // Вызываем прерывание intdos(®, ®); // Проверяем флаг переноса if(reg.x.cflag == 0) { // Если флаг переноса сброшен в 0, ошибок нет. if(reg.h.al == 0) return(1); else return(0); } // Если флаг переноса установлен в 1, возвращаем // признак ошибки else return(-1); } Вы можете использовать эту функцию аналогично функции eof(). Расположение открытого файла или устройстваПодфункция 0Ah функции 44h прерывания INT 21h поможет программе, работающей в сети, определить расположение открытого файла или устройства - на рабочей станции или на сервере. Перед вызовом запишите в регистр BX идентификатор проверяемого файла или устройства. После возврата из подфункции регистр DX содержит слово атрибутов для файла или устройства. Если самый старший бит в этом слове равен 0, то файл или устройство является локальным и расположено на рабочей станции. Если же этот бит равен 1, то файл или устройство удаленное и находится на сервере (подключено к серверу, если проверяется устройство). Обычно программы составляют таким образом, чтобы их работа не зависела от расположения файлов и дисков. Но если такая информация вам когда-либо понадобится, вы можете воспользоваться подфункцией 0Ah. Аналогично для проверки расположения дискового устройства можно использовать подфункцию 09h. Перед вызовом запишите в регистр BL код устройства (0 - текущий диск, 1 - А:, 2 - В:, и т. д.). Двенадцатый бит регистра DX после вызова этой функции покажет вам расположение устройства: 0 - локальное, 1 - удаленное. Возможность замены носителя данныхДля проверки возможности замены носителя данных в устройстве вы можете воспользоваться подфункцией 08h. Используя эту подфункцию, вы сможете отличить НГМД от НМД. Это может вам пригодиться, например, при выполнении операции форматирования, так как форматирование НГМД и НМД выполняется по-разному. Перед вызовом подфункции 08h запишите код устройства в регистр BL (0 - текущий диск, 1 - А:, 2 - В:, и т. д.). Если носитель данных сменный, то после выполнения подфункции регистр AL будет содержать 0, в противном случае - 1. Эта подфункция не предназначена для работы с сетевыми устройствами. 4.2. Общее управление устройством GENERIC IOCTLПодфункция 0Dh функции 44h прерывания INT 21h обеспечивает механизм взаимодействия между прикладным программным обеспечением и драйверами блочных устройств. Эта подфункция позволяет программам читать и изменять параметры устройств, предоставляет возможность выполнять чтение, запись, форматирование и проверку дорожек диска на низком уровне с помощью драйвера устройства. При вызове функции вы должны загрузить
регистры процессора следующим образом:
Если функция выполнилась без ошибки, флаг переноса CF сброшен. В противном случае этот флаг установлен, при этом регистр AX содержит код ошибки. Через регистры DS:DX функции необходимо передать адрес подготовленного блока параметров, формат которого зависит от выполняемой операции. Опишем этот формат для наиболее важных операций.
Таблица разметки дорожки начинается с двухбайтового слова, содержащего общее количество секторов на дорожке. Затем для каждого сектора в таблице находится по два двухбайтовых слова, содержащих номер сектора (1, 2 и т. д.) и размер сектора. То есть для каждого сектора в таблице содержится два слова. Если в поле "специальные функции" бит 2 установлен в 1, размеры всех секторов должны быть одинаковыми.
Перед началом выполнения операции программа должна получить и сохранить текущие параметры устройства. Для получения текущих параметров устройства необходимо выполнить операцию с кодом 60h. Затем программа должна установить новые параметры устройства, которые будут использованы в операциях чтения, записи, проверки или форматирования. Для установки параметров программа должна выполнить операцию с кодом 40h. После выполнения операции программа должна восстановить первоначальные параметры устройства, выполнив операцию с кодом 40h. 4.3. Примеры использования функций GENERIC IOCTLВ этом разделе мы приведем исходные тексты двух программ. Первая из них форматирует дорожку дискеты с помощью функции GENERIC IOCTL . Вторая демонстрирует способы чтения и записи секторов дискеты. Для этого она также пользуется функциями GENERIC IOCTL. Программа FMTIOCTLПриведем пример программы FMTIOCTL (листинг 4.1), иллюстрирующей применение функций общего управления GENERIC IOCTL . Эта программа выполняет стандартное форматирование двадцатой дорожки диска А:. Листинг 4.1. Файл fmtioctl\fmtioctl.cpp #include <dos.h> #include <stdio.h> #include <conio.h> #include <malloc.h> #include <errno.h> typedef struct _EBPB_ { unsigned sectsize; char clustsize; unsigned ressecs; char fatcnt; unsigned rootsize; unsigned totsecs; char media; unsigned fatsize; unsigned seccnt; unsigned headcnt; unsigned hiddensec_low; unsigned hiddensec_hi; unsigned long drvsecs; } EBPB; typedef struct _TRK_LY_ { unsigned no; unsigned size; } TRK_LY; typedef struct _DPB_ { char spec; char devtype; unsigned devattr; unsigned numofcyl; char media_type; EBPB bpb; char reserved[6]; unsigned trkcnt; TRK_LY trk[100]; } DPB; typedef struct _DPB_FORMAT_ { char spec; unsigned head; unsigned track; } DPB_FORMAT; int main(void) { union REGS reg; struct SREGS segreg; DPB far *dbp; DPB_FORMAT far *dbp_f; int sectors, i; printf("\nПрограмма уничтожит содержимое" "\n20-й дорожки диска А:." "\nЖелаете продолжить? (Y,N)\n"); // Ожидаем ответ и анализируем его i = getch(); if((i != 'y') && (i != 'Y')) return(-1); // Заказываем память для блока // параметров устройства dbp = (DPB far*)farmalloc(sizeof(DPB)); // Заказываем память для блока параметров // устройства, который будет // использован для форматирования dbp_f = (DPB_FORMAT far*) farmalloc(sizeof(DPB_FORMAT)); if(dbp == NULL || dbp_f == NULL) { printf("\nМало памяти"); return(-1); } // Получаем текущие параметры диска А: dbp->spec = 0; // Вызываем подфункцию 0Dh для выполнения // операции чтения текущих параметров диска А: reg.x.ax = 0x440d; reg.h.bl = 1; reg.x.cx = 0x0860; reg.x.dx = FP_OFF(dbp); segreg.ds = FP_SEG(dbp); intdosx(®, ®, &segreg); // Проверяем результат выполнения операции if(reg.x.cflag != 0) { printf("\nОшибка: %d",reg.x.ax); return(-1); } // Заполняем блок парметров для форматирования. // Байт специальных функций содержит значение, // равное 5. Это означает, что: // - используется текущий блок параметров BIOS BPB ; // - используются все поля в блоке парметров устройства; // - все секторы на дорожке имеют одинаковый размер dbp->spec = 5; // Считываем из BPB количество секторов на дорожке sectors = dbp->bpb.seccnt; // Подготавливаем таблицу, описывающую формат дорожки // Записываем количество секторов на дорожке dbp->trkcnt = sectors; // Для каждого сектора на дорожке в таблицу // записываем его номер и размер. // Заметьте, что записывается размер сектора // в байтах, а не код размера, как это делается // при форматировании с помощью // функции 05h прерывания INT 13h for(i = 0; i < sectors; i++) { dbp->trk[i].no = i + 1; dbp->trk[i].size = 512; } // Устанавливаем новые параметры для диска А: reg.x.ax = 0x440d; reg.h.bl = 1; reg.x.cx = 0x0840; reg.x.dx = FP_OFF(dbp); segreg.ds = FP_SEG(dbp); intdosx(®, ®, &segreg); // Проверяем результат выполнения операции if(reg.x.cflag != 0) { printf("\nОшибка: %d",reg.x.ax); return(-1); } // Подготавливаем блок параметров устройства, // который будет использован при вызове // операции проверки возможности форматирования // дорожки. // В поле специальных функций записываем 1, // это означает, что будет выполняться проверка // возможности использования // указанного формата дорожки dbp_f->spec = 1; dbp_f->head = 0; dbp_f->track = 20; reg.x.ax = 0x440d; reg.h.bl = 1; reg.x.cx = 0x0842; reg.x.dx = FP_OFF(dbp_f); segreg.ds = FP_SEG(dbp_f); intdosx(®, ®, &segreg); // Проверяем результат выполнения операции if(reg.x.cflag != 0) { printf("\nОшибка: %d", reg.x.ax); return(-1); } // Если указанный формат дорожки поддерживается, // поле специальных функций будет содержать 0. // Проверяем это if(dbp_f->spec != 0) { printf("\nФормат дорожки не поддерживается"); return(-1); } // Заполняем блок параметров для выполнения // операции форматирования dbp_f->spec = 0; dbp_f->head = 0; dbp_f->track = 20; // Форматируем дорожку с номером 20, головка 0 reg.x.ax = 0x440d; reg.h.bl = 1; reg.x.cx = 0x0842; reg.x.dx = FP_OFF(dbp_f); segreg.ds = FP_SEG(dbp_f); intdosx(®, ®, &segreg); // Проверяем резултат выполнения операции if(reg.x.cflag != 0) { printf("\nОшибка: %d", reg.x.ax); return(-1); } // Освобождаем память farfree(dbp); farfree(dbp_f); return(0); } После запуска программа форматирования читает текущие параметры для диска А:, формирует структуру дорожки и устанавливает параметры для выполнения операции форматирования. Затем программа проверяет возможность использования указанной структуры дорожки и выполняет форматирование. Программа CPYIOCTLТеперь приведем программу CPYIOCTL (листинг 4.2), копирующую содержимое двух первых секторов нулевой дорожки (головка 0) в первые два сектора двадцатой дорожки. Листинг 4.2. Файл cpyioctl\cpyioctl.cpp #include <dos.h> #include <stdio.h> #include <conio.h> #include <malloc.h> #include <errno.h> typedef struct _DPB_WR_ { char spec; unsigned head; unsigned track; unsigned sector; unsigned sectcnt; void _far *buffer; } DPB_WR; char buf[2000]; int main(void) { union REGS reg; struct SREGS segreg; DPB_WR far *dbp_wr; int sectors, i; printf("\nПрограмма уничтожит содержимое" "\n20-й дорожки диска А:." "\nЖелаете продолжить? (Y,N)\n"); // Ожидаем ответ и анализируем его i = getch(); if((i != 'y') && (i != 'Y')) return(-1); // Заказываем память для блока параметров // устройства, который будет // использован для чтения и записи dbp_wr = (DPB_WR far*)farmalloc(sizeof(DPB_WR)); if(dbp_wr == NULL) { printf("\nМало памяти"); return(-1); } // Заполняем блок параметров для выполнения // операции чтения. // Мы будем читать первые два сектора // на нулевой дорожке, головка 0 dbp_wr->spec = 0; dbp_wr->head = 0; dbp_wr->track = 0; dbp_wr->sector = 0; dbp_wr->sectcnt = 2; dbp_wr->buffer = buf; // Выполняем операцию чтения дорожки reg.x.ax = 0x440d; reg.h.bl = 1; reg.x.cx = 0x0861; reg.x.dx = FP_OFF(dbp_wr); segreg.ds = FP_SEG(dbp_wr); intdosx(®, ®, &segreg); // Проверяем результат выполнения операции if(reg.x.cflag != 0) { printf("\nОшибка: %d", reg.x.ax); return(-1); } // Заполняем блок параметров для выполнения // операции записи. // Только что прочитанные два сектора нулевой // дорожки будут записаны на 20-ю дорожку dbp_wr->spec = 0; dbp_wr->head = 0; dbp_wr->track = 20; dbp_wr->sector = 0; dbp_wr->sectcnt = 2; dbp_wr->buffer = buf; // Выполняем операцию записи reg.x.ax = 0x440d; reg.h.bl = 1; reg.x.cx = 0x0841; reg.x.dx = FP_OFF(dbp_wr); segreg.ds = FP_SEG(dbp_wr); intdosx(®, ®, &segreg); // Проверяем результат выполнения операции if(reg.x.cflag != 0) { printf("\nОшибка: %d", reg.x.ax); return(-1); } // Освобождаем память farfree(dbp_wr); return(0); } Программа пользуется текущими параметрами диска А:, поэтому операции чтения текущих параметров и записи новых параметров не используются. Обратите внимание на то, что эта и предыдущая программа разрушают содержимое двадцатой дорожки, поэтому для экспериментов с этими программами надо подготовить чистую отформатированную дискету. |