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

Операционная система MS-DOS

© Александр Фролов, Григорий Фролов
Том 1, книги 1-2, М.: Диалог-МИФИ, 1991.

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

1.6. Обработка ошибок

Когда программа обращается к DOS для выполнения какой-либо операции, она должна вызвать соответствующее прерывание, загрузив перед вызовом прерывания все необходимые операнды в регистры процессора. Если выполнение операции невозможно по каким-то причинам (неправильные операнды, устройство неработоспособно, запрашиваемая операция не поддерживается текущей версией DOS и т.д.), то для большинства функций DOS устанавливается признак ошибки - флаг переноса CARRY. Для DOS версии 2.0 и более поздних версий регистр AX при этом содержит код ошибки.

Приведем коды ошибок, возвращаемые программе через регистр AX:

1 Неправильный код функции
2 Файл не найден
3 Путь не найден
4 Слишком много открытых файлов
5 Доступ запрещен
6 Неправильный идентификатор файла
7 Разрушен блок управления памятью
8 Недостаточно памяти
9 Неправильный адрес блока памяти
10 Неправильная среда
11 Неправильный формат
12 Неправильный код доступа
13 Неправильные данные
14 Зарезервировано
15 Ошибка при указании дисковода
16 Невозможно удалить текущий каталог
17 Другое устройство
18 Больше нет подходящих файлов

Для DOS версии 3.0 и более поздних версий обработка ошибок значительно расширена. Введена функция 59h прерывания INT 21h, предназначенная для получения дополнительной информации об ошибках.

При вызове этой функции регистр BX должен содержать индикатор уровня анализа ошибок, который должен быть равен 0. Кроме расширенного кода ошибки, возвращаемого в регистре AX, программа может получить класс ошибки (регистр BH), код предполагаемых действий (регистр BL), локализацию ошибки, т.е. место, где произошла ошибка (регистр CH).

К сожалению, эта функция разрушает содержимое регистров CL, DX, SI, DI, BP, DS, ES. Программа, использующая функцию 59h, должна позаботиться о сохранении содержимого этих регистров.

Расширенный код ошибки, возвращаемый в регистре AX, может принимать значения, указанные в приводимой ниже таблице. Коды от 1 до 18 эквивалентны представленным выше и второй раз не приводятся.

Расширенные коды ошибок:

19 Запись на защищенный от записи диск
20 Задан неизвестный идентификатор устройства
21 Дисковод не готов
22 Неизвестная команда
23 Ошибка циклического кода проверки
24 Неправильная длина структуры запроса
25 Ошибка поиска
26 Неизвестен тип среды носителя данных
27 Сектор не найден
28 Кончилась бумага в принтере
29 Ошибка записи
30 Ошибка чтения
31 Общая ошибка
32 Нарушение разделения файла
33 Нарушение блокировки файла
34 Неправильная замена диска
35 FCB недоступен (слишком много блоков FCB)
36 Переполнился буфер разделения
37 Зарезервировано
38 Не завершена операция "Конец файла"
39-49 Зарезервировано
50 Сетевая функция не поддерживается
51 Удаленный компьютер "не слышит"
52 Дублирование имени в сети
53 Сетевое имя не найдено
54 Сеть занята
55 Сетевое устройство больше не существует
56 Превышен лимит команды сетевой BIOS
57 Ошибка в аппаратуре сетевого адаптера
58 Неправильный ответ из сети
59 Непредусмотренная ошибка сети
60 Несовместимый удаленный адаптер
61 Заполнена очередь печати
62 Для печатаемого файла недостаточно места
63 Печатающийся файл был удален
64 Сетевое имя было удалено
65 Доступ запрещен
66 Неправильный тип сетевого устройства
67 Сетевое имя не найдено
68 Превышен лимит сетевого имени
69 Превышен лимит сеанса сетевой BIOS
70 Временная пауза
71 Сетевой запрос отвергнут
72 Приостановлена печать или переадресация диска
73-79 Зарезервировано
80 Файл уже существует
81 Зарезервировано
82 Невозможно создать дескриптор в каталоге
83 Ошибка обработчика критических ошибок INT 24h
84 Слишком много переназначений
85 Двойное переназначение
86 Неправильный пароль
87 Неправильный параметр
88 Ошибка данных в сети
89 Нет такой функции в сети
90 Требуемый компонент системы не установлен

Класс ошибки, передаваемый в регистре BH, содержит информацию, которая поможет вам обработать данную ошибку:

1 Недостаточно ресурсов: блоков FCB, памяти и т.д.
2 Временная ситуация
3 Нет прав доступа
4 Внутренняя ошибка DOS
5 Ошибка аппаратуры
6 Системная ошибка DOS (нет CONFIG.SYS и т.п.)
7 Ошибка в прикладной программе
8 Файл или объект не найден
9 Неправильный формат файла или объекта
10 Файл или объект заблокирован
11 Ошибка носителя данных
12 Файл или объект уже существует
13 Прочие ошибки

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

1 Повторить операцию позже. Можно спросить пользователя, желает он повторить операцию или завершить работу программы.
2 Повторить предыдущую операцию после небольшой паузы. Если ошибка не исчезла, следует спросить пользователя, будет он ждать и дальше, или следует завершить работу программы.
3 Если пользователь вводил какие-то данные для DOS, следует попросить его ввести эти данные еще раз (например, пользователь мог указать неправильный идентификатор диска или путь доступа к файлу).
4 Аварийно завершить работу прикладной программы с выполнением всех обычных завершающих действий (закрытие файлов, сброс буферов на диск, освобождение блоков памяти и т.д.)
5 Немедленный выход из программы без выполнения завершающих действий. Система находится в непредсказуемом состоянии.
6 Следует игнорировать ошибку.
7 Повторить операцию после того, как пользователь выполнит требуемые действия (установит дискету и т.п.).

Сведения о локализации ошибки передаются в регистре CH. Приведем таблицу кодов локализации:

1 Локализация ошибки не может быть определена (система не знает, где произошла ошибка).
2 Ошибка произошла в блочном устройстве (диск или магнитная лента).
3 Ошибка связана с сетью.
4 Ошибка произошла в символьном устройстве, например, в принтере.
5 Ошибка связана с оперативной памятью.

Если Ваша программа составлена на языке ассемблера, то после обращения к DOS через прерывание следует проверить состояние флага переноса:

int     21h
jc      error

Программы, составленные на языке Си, обращаются к прерываниям DOS обычно с помощью таких функций, как intdos, int86, intdosx и т.д. Для передачи параметров используются структуры REGS, WORDREGS, BYTEREGS, SREGS. Они описаны в файле dos.h, для использования этих структур программа должна содержать строку:

include <dos.h>

Значение флага переноса записывается в переменную cflag, определенную в структуре WORDREGS. Эта структура входит в объединение REGS:

union REGS {
        struct WORDREGS x;
        struct BYTEREGS h;
}

struct WORDREGS {
        unsigned int ax;
        unsigned int bx;
        unsigned int cx;
        unsigned int dx;
        unsigned int si;
        unsigned int di;
        unsigned int cflag;
}

struct BYTEREGS {
        unsigned char al, ah;
        unsigned char bl, bh;
        unsigned char cl, ch;
        unsigned char dl, dh;
}

Проверка переменной cflag может быть выполнена, например, таким образом:

union REGS inregs, outregs;

intdos(&inregs, &outregs);
if( outregs.x.cflaf != 0 ) error();

Код ошибки при этом содержится в переменной outregs.x.ax.

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

#include <dos.h>
#include <stdio.h>

union REGS inregs, outregs;
struct SREGS segregs;

void main(void);

void main(void) {

char _far *dir_name = "DIR";

// Стираем каталог с именем DIR. Для этого вызываем
// функцию 0x3A прерывания INT 21h.

        inregs.h.ah = 0x3a;
        segregs.ds = FP_SEG(dir_name);
        inregs.x.dx = FP_OFF(dir_name);
        intdosx(&inregs, &outregs, &segregs);

// Если после выполнения прерывания установлен
// флаг переноса, выводим сообщение об ошибке.

        if(outregs.x.cflag != 0) {
           printf( "Ошибка при удалении каталога: %d",
           outregs.x.ax);

// Получаем расширенную информацию об ошибке
// с помощью функции 0x59 прерывания INT 21h.

           inregs.h.ah = 0x59;
           inregs.x.bx = 0;

// Сохраняем регистры в стеке, т.к. их содержимое
// изменится

        _asm {
              push ds
              push es
              push si
              push di
        }

        intdosx(&inregs, &outregs, &segregs);

        _asm {
               pop di
               pop si
               pop es
               pop ds
        }

// Выводим расширенную информацию об ошибке.

        printf("\nРасширенный код ошибки:   %d"
               "\nКласс ошибки:             %d"
               "\nПредполагаемые действия:  %d"
               "\nЛокализация ошибки:       %d",
               outregs.x.ax,
               outregs.h.bh,
               outregs.h.bl,
               outregs.h.ch);
        }
}

При составлении программ обработки ошибок следует учитывать, что для DOS версии 1.0 при некоторых ошибках функции DOS возвращают в регистре AX значение 0FFh. Начиная с версии DOS 2.0, при ошибке устанавливается флаг переноса, код ошибки записывается в регистр AX. Однако для более полной диагностики причины ошибки следует использовать функцию 59h прерывания INT 21h.

Если Ваша программа, составленная на языке Си, вызывает функции DOS неявным образом (через функции стандартной библиотеки транслятора, такие как fprintf, puts и т.д.), то можно воспользоваться средствами обработки ошибок, входящими в состав стандартной библиотеки.

Когда при обращении к функциям DOS средствами стандартной библиотеки транслятора Си возникает ошибка, то в глобальную переменную errno записывается код ошибки.

Возможны следующие коды ошибок (они описаны в файле errno.h и stdlib.h):

ECHILD Нет порожденных процессов. Задача, не имеющая подзадач, выдала команду ожидания, или была выдана команда ожидания для подзадачи, имеющей признак NO-WAIT.
EAGAIN Больше нет процессов. Попытка создать новый процесс окончилась неудачно, т.к. либо больше нет резервов для создания процессов, либо недостаточно оперативной памяти, либо превышен максимальный уровень вложенности процессов.
E2BIG Слишком велик список аргументов. Либо размер списка аргументов превышает 128 байт, либо требуемый размер памяти для среды превышает 32К.
EACCES Доступ запрещен. Затребованный вид доступа к файлу запрещен или несовместим с установленными атрибутами файла (или каталога). Этот код ошибки передается при попытке чтения из неоткрытого файла, при попытке записи в файл, защищенный от записи, или при попытке открыть каталог как файл.
EBADF Плохой номер файла. Номер файла, использованный при вызове функции, имеет неверное значение или не относится к открытому файлу, или сделана попытка записи в открытый только для чтения файл или устройство.
EDEADLOCK Произошла блокировка ресурсов. Произведено 10 неудачных попыток заблокировать файл. Этот код ошибки используется только DOS версии 3.0 и более поздних версий.
EDOM Ошибка в аргументе математической функции. Аргумент математической функции вышел за пределы области определения этой функции.
EEXIST Файл уже существует. Сделана попытка создать файл с именем, которое уже используется существующим файлом.
EINVAL Неверный аргумент. Для одного из аргументов функции было задано неверное значение.
EMFILE Открыто слишком много файлов. Исчерпан запас номеров файлов , нельзя больше открыть ни одного файла.
ENOENT Нет такого файла или каталога. Запрошенный файл или каталог отсутствует или не может быть найден.
ENOEXEC Сделана попытка выполнить загрузочный файл, имеющий неправильный формат.
ENOMEM Недостаточно памяти. Эта ошибка появляется, когда недостаточно памяти для запуска процесса или для удовлетворения запроса программы на выделение блока памяти.
ENOSPC Нет свободного места на устройстве. На устройстве нет места для записи информации (например, переполнился диск).
ERANGE Слишком большой результат. Слишком большой по величине аргумент математической функции привел к частичной или полной потере значимости результата.
EXDEV Связь различных устройств. Сделана попытка переслать файл на другое устройство, используя функцию переименования.

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

Для диагностической выдачи сообщения об ошибке можно использовать функции perror и strerror. Первая функция выводит в stderr соответствующее сообщение об ошибке, вторая только формирует строку сообщения. Функции perror и strerror имеют операнд - указатель на строку. Эта строка добавляется в начало стандартного сообщения об ошибке. Если к стандартному сообщению ничего добавлять не надо, операнд должен иметь значение NULL.

Следует заметить, что значение переменной errno отражает последнюю ошибку. Успешный вызов функции не приводит к автоматическому сбросу переменной errno.

Поэтому функции perror и strerror необходимо вызывать сразу после того, как вызываемая функция возвратит признак ошибки.

Приведем пример программы, обрабатывающей ошибки с использованием переменной errno:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

void main(int argc, char *argv[]) {

FILE *stream;

// Открываем файл только для чтения

        stream = fopen(argv[1], "r");

// Если произошла ошибка, выводим сообщение

        if( (stream == NULL) || (ferror(stream)) ) {
           perror("Не могу открыть файл");
           exit(errno);
        }

// Пытаемся произвести запись в файл, который
// открыт только для чтения. Это приведет к ошибке.

        fprintf(stream, "Пишем в файл\n");

        if( (stream == NULL) || (ferror(stream)) ) {

// Выводим сообщение об ошибке двумя способами -
// с помощью функции perror и strerror

                perror("Запись в защищенный файл");
                printf("Запись в защищенный файл: %s\n",
                strerror(errno));

                exit(errno);
        }
        exit( 0 );
}

DOS имеет еще одно средство для обработки ошибок - обработчик критических ошибок (Critical Error Handler). Этот модуль вызывается DOS, когда она получает сообщение об ошибке от драйвера устройства.

Модуль выдает на экран хорошо известное вам сообщение:

Abort, Retry, Ignore?

Это сообщение обычно появляется тогда, когда вы забываете вставить дискету или начинаете печатать при отключенном принтере.

Прикладные программы могут подключать свой модуль обработки критических ошибок вместо стандартного. Мы научимся обрабатывать критические ошибки в книге 3. Там же будет приведен соответствующий пример.

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