Untitled
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,"nr"
"nrКод ошибки устройтсва: %04.4X"
"nrКод ошибки DOS: %d"
"nrnr"
"nrВыполняемые действия:"
"nr 0 - повторить"
"nr 1 - отменить"
"nr 2 - завершить"
"nr----> ?",
deverr,
doserr);
_bios_str(buf);
// Вводим ответ с клавиатуры
ch = _bios_keybrd(_KEYBRD_READ) & 0x00ff;
_bios_str("nr");
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);
}
}
|