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

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

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

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

3.12. Обработка критических ошибок

Операционная система MS-DOS позволяет программам устанавливать собственный обработчик критических ошибок аппаратуры. Мы уже говорили о том, что вектор 0000h:0090h, соответствующий прерыванию INT 24h , содержит адрес обработчика критических ошибок. Этот обработчик получает управление от операционной системы, когда драйвер какого-либо устройства обнаруживает ошибку аппаратуры.

Обратите внимание на то, что обработчик критических ошибок не вызывается при работе с диском через прерывания INT 25h или INT 26h. Тем более, он не вызывается при работе с диском на уровне прерывания INT 13h .

При запуске программы операционная система 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 Адрес заголовка драйвера устройства, в котором произошла ошибка

Обработчик критических ошибок не должен пользоваться функциями MS-DOS с кодами, большими чем 0Ch (из-за того, что функции MS-DOS не реентерабельны).

Программа обработки критических ошибок может вывести на экран сообщение об ошибке и запросить оператора о необходимых действиях. Ей разрешено также получить дополнительную уточняющую информацию об ошибке с помощью функции 59h прерывания INT 21h или узнать версию MS-DOS с помощью функции 30h этого же прерывания.

Дополнительная информация об устройстве, в котором произошла ошибка, может быть получена с использованием адреса заголовка драйвера устройства, который передается операционной системой при вызове обработчика в регистрах BP:SI.

Анализ стека

Для определения номера функции MS-DOS, в которой произошла критическая ошибка, программа-обработчик может выполнить анализ стека. Когда обработчик получает управление, стек имеет следующую структуру:

Адрес возврата в DOS для команды IRET

IP

CS

FLAGS

Содержимое регистров программы перед вызовом INT 21h

AX, BX, CX, DX, SI, DI, BP, DS, ES

Адрес возврата в программу, вызвавшую функцию DOS

IP

CS

FLAGS

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

Код действия

После выполнения всех необходимых действий программа обработки критических ошибок должна возвратить в регистре AL код действия, которое должна выполнить операционная система для обработки данной ошибки:

Код Описание
0 Игнорировать ошибку
1 Повторить операцию
2 Завершить задачу аварийно, используя адрес завершения, записанный в векторе прерывания INT 23h
3 Вернуть программе управление с соответствующим кодом ошибки

При открытии файлов с помощью функции 6Ch программа может заблокировать вызов обработчика критических ошибок.

Функции библиотеки Borland C++

Для составления программы обработки критических ошибок вы можете воспользоваться языком ассемблера или функциями стандартной библиотеки Borland C++ с именами _dos_getvect , _dos_setvect , _chain_intr . Однако лучше всего использовать специально предназначенные для этого функции _harderr , _hardresume и _hardretn .

_harderr

Функция _harderr предназначена для установки нового обработчика критических ошибок, она имеет следующий прототип:

void _harderr (void (far *handler)());

Параметр handler - указатель на новую функцию обработки критических ошибок.

_hardresume

Функция _hardresume и описанная ниже функция _hardretn должны быть использованы в обработчике критических ошибок, установленном функцией _harderr .

Функция _hardresume возвращает управление операционной системе, она имеет прототип:

_hardresume (int result);

Параметр result может иметь следующие значения (в соответствии с необходимыми действиями):

Значение Описание
_HARDERR_ABORT Завершить программу аварийно
_HARDERR_FAIL Вернуть код ошибки
_HARDERR_IGNORE Игнорировать ошибку
_HARDERR_RETRY Повторить операцию

Эти параметры описаны в файле dos.h.

_hardretn

Функция _hardretn возвращает управление непосредственно программе, передавая ей код ошибки, определяемый параметром функции error:

void _hardretn (int error);

При этом программа получает код ошибки error после возврата из вызванной ей функции MS-DOS. Если ошибка произошла при выполнении функции с номером, большим чем 38h, дополнительно устанавливается флаг переноса. Если номер функции был меньше указанного значения, в регистр AL записывается величина FFh.

Функция обработки критических ошибок

Функция обработки критических ошибок handler имеет следующие параметры:

void far handler(unsigned deverror, unsigned errcode,
  unsigned far *devhdr);

Первый параметр - код ошибки устройства. Он равен содержимому регистра AX при вызове обработчика прерывания INT 24h . Аналогично, параметр errcode соответствует содержимому регистра DI - код ошибки. Третий параметр devhdr - это указатель на заголовок драйвера устройства (передаваемый в регистрах BP:SI).

Программа CRITERR

Для демонстрации использования функций установки обработчика критических ошибок приведем программу, которая пытается создать каталог на диске А:. Эта программа сама обрабатывает критические ошибки, запрашивая у оператора информацию о необходимых действиях.


Листинг 3.8. Файл criterr\criterr.cpp


// Эту программу можно запускать только из командной
// строки. При запуске из интегрированной среды
// Borland C++ возможен конфликт с используемым в этих
// средах обработчиком критических ошибок

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <direct.h>
#include <string.h>
#include <dos.h>
#include <bios.h>

void far hhandler(unsigned deverr,
  unsigned doserr, unsigned far *hdr);
void _bios_str(char *p);

int main()
{
  // Устанавливаем обработчик критических ошибок
  _harderr (hhandler);

  // Моделируем критическую ошибку.
  // Выполняем попытку создать каталог на диске А:.
  // Если мы "забудем" вставить в дисковод
  // дискету, будет вызван обработчик
  // критической ошибки
  printf("\nВставьте (или не вставляйте) "
    "дискету в дисковод A:"
    "\nи нажмите любую клавишу...\n");
  getch();

  // Создаем каталог
  if(mkdir ("a:\\test_ctl"))
  {
    printf("\nОшибка при создании каталога");
    return(-1);
  }
  else
  {
    // Удаляем только что созданный каталог
    rmdir ("a:test_ctl");
  }
  return 0;
}

// Новый обработчик критических ошибок
#pragma argsused
void far hhandler(unsigned deverr,
  unsigned doserr, unsigned far *hdr)
{
  int ch;
  static char buf[200];

  // Выводим сообщение о критической ошибке
  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;

  inregs.h.ah = 0x0e;
  for(; *ptr; ptr++)
  {
    inregs.h.al = *ptr;
    int86(0x10, &inregs, &outregs);
  }
}

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