Операционная система MS-DOS
© Александр Фролов, Григорий Фролов
Том 1, книга 3, М.: Диалог-МИФИ, 1992.
5. Обработка критических
ошибок
Операционная система MS-DOS позволяет программам
устанавливать собственный обработчик
критических ошибок аппаратуры. Мы уже говорили о
том, что вектор 0000:0090, соответствующий
прерыванию INT 24h, содержит адрес
обработчика критических ошибок. Этот обработчик
получает управление от операционной системы,
когда драйвер какого-либо устройства
обнаруживает ошибку аппаратуры.
Обратите внимание на то, что обработчик
критических ошибок не вызывается при работе с
диском через прерывания MS-DOS INT 25h/26h, и, тем
более, при работе с диском на уровне прерывания INT 13h
BIOSBIOS.
При запуске программы MS-DOS копирует адрес
обработчика в префикс сегмента программы PSP,
а после завершения работы программы -
восстанавливает его из PSP.
Стандартный обработчик MS-DOS выводит на экран
сообщение:
Abort, Retry, Ignore, Fail?
Если ваша программа должна сама обрабатывать
ошибки аппаратуры, она может установить свой
собственный обработчик критических ошибок.
Когда обработчик получает управление, регистры
процессора содержат информацию, необходимую для
определения причины и места появления ошибки:
AH |
информация об ошибке:
Биты:
0 - тип операции:
0 - чтение, 1 - запись
1...2 область диска, где произошла ошибка:
00 - системные файлы;
01 - область FAT
10 - область каталога;
11 - область данных
3 - 1 - возможен выход с кодом FAIL
4 - 1 - возможен выход с кодом RETRY
5 - 1 - возможен выход с кодом IGNORE
6 зарезервирован, равен 0
7 тип устройства: 0 - диск; 1 - символьное
устройство |
AL |
номер диска (если бит 7 регистра AH
равен 0) |
DI |
код ошибки (биты 0...7, остальные биты не
определены) |
BP:SI |
адрес заголовка драйвера устройства, на
котором произошла ошибка |
Биты 3, 4, 5 определены только для DOS версии 3.0
и для более поздних версий.
Обработчик критических ошибок не должен
пользоваться функциями MS-DOS с кодами, большими
чем 0Ch (из-за того, что функции MS-DOS не
реентерабельны).
Программа может вывести на экран сообщение об
ошибке и запросить оператора о необходимых
действиях. Ей разрешено также получить
дополнительную уточняющую информацию об ошибке
с помощью функции 59h прерывания INT 21h
или узнать версию DOS с помощью функции 30h
этого же прерывания. Дополнительная информация
об устройстве, в котором произошла ошибка, может
быть получена с использованием адреса заголовка
драйвера устройства, который передается
операционной системой при вызове обработчика в
регистрах BP:SI.
Для определения номера функции DOS, в которой
произошла критическая ошибка,
программа-обработчик может выполнить анализ
стека. Когда обработчик получает управление,
стек имеет следующую структуру:
Адрес возврата в DOS для команды IRET
IP
CS
FLAGS
Содержимое регистров программы перед вызовом INT_21h
AX, BX, CX, DX, SI, DI, BP, DS, ES
Адрес возврата в программу, вызвавшую функцию DOS
IP
CS
FLAGS
Выполнив анализ регистра AH, можно
определить номер функции DOS, при вызове которой
произошла ошибка, а зная содержимое остальных
регистров - и все параметры этой функции.
После выполнения всех необходимых действий,
программа обработки критических ошибок должна
возвратить в регистре AL код действия, которое
должна выполнить операционная система для
обработки данной ошибки:
0 |
игнорировать ошибку; |
1 |
повторить операцию; |
2 |
аварийно закончить задачу, используя
адрес завершения, записанный в векторе
прерывания INT 23h; |
3 |
вернуть программе управление с
соответствующим кодом ошибки (этот код можно
задавать только для DOS версии 3.0 и для более
поздних версий). |
Если вы пользуетесь операционной системой MS-DOS
версии 4.0, то при открытии файлов с помощью
функции 6Ch программа может заблокировать
вызов обработчика критических ошибок.
Для составления программы обработки
критических ошибок вы можете воспользоваться
языком ассемблера или функциями стандартных
библиотек трансляторов Microsoft QC 2.5 и C 6.0 _dos_getvect(),
_dos_setvect(), _chain_intr(). Однако лучше всего
использовать специально предназначенные для
этого (и входящие в состав стандартных библиотек
указанных трансляторов) функции _harderr(), _hardresume()
и _hardretn().
Функция _harderr() предназначена для установки
нового обработчика критических ошибок, она имеет
следующий прототип:
void _harderr(void (_far *handler)());
Параметр handler - указатель на новую функцию
обработки критических ошибок.
Функции _hardresume() и _hardretn() должны быть
использованы в обработчике критичеких ошибок,
установленном функцией _harderr().
Функция _hardresume() возвращает управление
операционной системе, она имеет прототип:
_hardresume(int result);
Парметр result может иметь следующие значения (в
соответствии с необходимыми действиями):
_HARDERR_ABORT |
аварийно завершить программу; |
_HARDERR_FAIL |
вернуть код ошибки; |
_HARDERR_IGNORE |
игнорировать ошибку; |
_HARDERR_RETRY |
повторить операцию. |
Эти параметры описаны в файле dos.h.
Функция _hardretn() возвращает управление
непосредственно программе, передавая ей код
ошибки, определяемый параметром функции error:
void _hardretn(int error);
При этом программа получает после возврата из
вызванной ей функции DOS код ошибки error. Если
ошибка произошла при выполнении функции с
номером, большим чем 38h, дополнительно
устанавливается в 1 флаг переноса. Если номер
функции был меньше указанного значения, в
регистр AL записывается величина FFh.
Функция обработки критических ошибок handler
имеет следующие параметры:
void _far handler(unsigned deverror, unsigned errcode,
unsigned _far *devhdr);
Первый параметр - код ошибки устройства. Он
равен содержимому регистра AX при вызове
обработчика прерывания INT 24h. Аналогично,
параметр errcode соответствует содержимому
регистра DI - код ошибки. Третий параметр - devhdr
- это указатель на заголовок драйвера устройства
(передаваемый в регистрах BP:SI).
Для демонстрации использования функций
установки обработчика критических ошибок
приведем программу, которая пытается создать
каталог на диске А:. Эта программа сама
обрабатывает критические ошибки, запрашивая у
оператора информацию о необходимых действиях.
// Эту программу можно запускать только из командной
// строки. При запуске из интегрированной среды QC
// или PWB возможен конфликт с используемым в этих
// средах обработчиком критических ошибок.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <direct.h>
#include <string.h>
#include <dos.h>
#include <bios.h>
void main(void);
void _far hhandler(unsigned deverr,
unsigned doserr, unsigned _far *hdr);
void _bios_str(char *p);
void main() {
// Устанавливаем обработчик критических ошибок
_harderr(hhandler);
// Моделируем критическую ошибку. Выполняем попытку создать
// каталог на диске А:. Если мы "забудем" вставить
// в дисковод дискету, будет вызван обработчик
// критической ошибки
printf("\nВставьте (или не вставляйте) дискету в дисковод A:"
"\nи нажмите любую клавишу..."
"\n");
getch();
// Создаем каталог
if(mkdir("a:\test_ctl")) {
printf("\nОшибка при создании каталога" );
exit(-1);
}
else {
printf("\nУспешное создание каталога");
// Удаляем только что созданный каталог
rmdir("a:test_ctl");
exit(0);
}
}
// Новый обработчик критических ошибок
void _far hhandler(unsigned deverr,
unsigned doserr, unsigned _far *hdr) {
int ch;
static char buf[200], tmpbuf[10];
// Выводим сообщение о критической ошибке
sprintf(buf,"\n\r"
"\n\rКод ошибки устройтсва: %04.4X"
"\n\rКод ошибки DOS: %d"
"\n\r\n\r"
"\n\rВыполняемые действия:"
"\n\r 0 - повторить"
"\n\r 1 - отменить"
"\n\r 2 - завершить"
"\n\r----> ?",
deverr,
doserr);
_bios_str(buf);
// Вводим ответ с клавиатуры
ch = _bios_keybrd(_KEYBRD_READ) & 0x00ff;
_bios_str("\n\r");
switch(ch) {
case '0': // Пытаемся повторить операцию
default:
_hardresume(_HARDERR_RETRY);
case '2': // Завершаем работу программы
_hardresume(_HARDERR_ABORT);
case '1': // Возврат в DOS с кодом ошибки
_hardretn(doserr);
}
}
// Программа для вывода строки символов на экран
// с помощью функции BIOS 0Eh
void _bios_str(char *ptr) {
union REGS inregs, outregs;
char *start = ptr;
inregs.h.ah = 0x0e;
for(; *ptr; ptr++) {
inregs.h.al = *ptr;
int86(0x10, &inregs, &outregs);
}
}
|