Нет порожденных процессов. Задача, не
Таблица 7
ECHILD | Нет порожденных процессов. Задача, не имеющая подзадач, выдала команду ожидания, или была выдана команда ожидания для подзадачи, имеющей признак NO-WAIT. |
EAGAIN | Больше нет процессов. Попытка создать новый процесс окончилась неудачно, т.к. либо больше нет резервов для создания процессов, либо недостаточно оперативной памяти, либо превышен максимальный уровень вложенности процессов. |
E2BIG | Слишком велик список аргументов. Либо размер списка аргументов превышает 128 байт, либо требуемый размер памяти для среды превышает 32К. |
EACCES | Доступ запрещен. Затребованный вид доступа к файлу запрещен или несовместим с установленными атрибутами файла (или каталога). Этот код ошибки передается при попытке чтения из неоткрытого файла, при попытке записи в файл, защищенный от записи, или при попытке открыть каталог как файл. |
EBADF | Плохой номер файла. Номер файла, использованный при вызове функции, имеет неверное значение или не относится к открытому файлу, или сделана попытка записи в открытый только для чтения файл или устройство. |
EDEADLOCK | Произошла блокировка ресурсов. Произведено 10 неудачных попыток заблокировать файл. Этот код ошибки используется только DOS версии 3.0 и более поздних версий. |
EDOM | Ошибка в аргументе математической функции. Аргумент математической функции вышел за пределы области определения этой функции. |
EEXIST | Файл уже существует. Сделана попытка создать файл с именем, которое уже используется существующим файлом. |
EINVAL | Неверный аргумент. Для одного из аргументов функции было задано неверное значение. |
EMFILE | Открыто слишком много файлов. Исчерпан запас номеров файлов , нельзя больше открыть ни одного файла. |
ENOENT | Нет такого файла или каталога. Запрошенный файл или каталог отсутствует или не может быть найден. |
ENOEXEC | Сделана попытка выполнить загрузочный файл, имеющий неправильный формат. |
ENOMEM | Недостаточно памяти. Эта ошибка появляется, когда недостаточно памяти для запуска процесса или для удовлетворения запроса программы на выделение блока памяти. |
ENOSPC | Нет свободного места на устройстве. На устройстве нет места для записи информации (например, переполнился диск). |
ERANGE | Слишком большой результат. Слишком большой по величине аргумент математической функции привел к частичной или полной потере значимости результата. |
EXDEV | Связь различных устройств. Сделана попытка переслать файл на другое устройство, используя функцию переименования. |
Для диагностической выдачи сообщения об ошибке можно использовать функции 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. Там же будет приведен соответствующий пример.
Таблица 7
(0) 4 | next | указатель на следующую таблицу файлов |
(+4) 2 | file_count | количество файлов в этой таблице |
--- Дальше идут блоки DFCB в количестве file_count штук ---- | ||
(0) 2 | handl_num | количество файловых чисел, связанных с данным файлом (file handle) |
(+2) 1 | access_mode | режим доступа к файлу, заданный при открытии файла |
(+3) 2 | reserv1 | зарезервировано |
(+5) 2 | dev_info | информация IOCTL, полученная для устройства, на котором расположен этот файл (подробно формат и назначение этого поля будут расмотрены в главе, посвященной драйверам) |
(+7) 4 | driver | указатель на драйвер, обслуживающий устройство, содержащее файл |
(+11) 2 | first_clu | номер первого кластера, распределенного файлу |
(+13) 2 | time | время последнего изменения файла в упакованном формате |
(+15) 2 | date | дата последнего изменения файла в упакованном формате |
(+17) 4 | fl_size | размер файла в байтах |
(+21) 4 | offset | текущее смещение внутри файла в байтах |
(+25) 2 | reserv2 | зарезервировано |
(+27) 2 | reserv7 | зарезервировано |
(+29) 3 | reserv3 | зарезервировано |
(+32) 1 | reserv4 | зарезервировано |
(+33) 11 | filename | имя файла в формате FCB (имя выравнено на левую границу поля, дополнено пробелами до 8 символов, справа к нему прилегает 3 символа расширения без точки) |
(+44) 2 | reserv5 | зарезервировано |
(+46) 2 | ownr_psp | PSP программы, открывшей файл |
(+48) 2 | reserv6 | зарезервировано |
(+50) 2 | last_clu | номер только что прочитанного кластера |
(+52) 4 | reserv8 | зарезервировано |
Приведем текст программ, возвращающих указатели на первый и последующий элементы списка таблиц файлов DOS: /** *.Name get_fdft * *.Title Получить адрес первой DTF * *.Descr Функция возвращает адрес первой таблицы файлов DOS * *.Params DTF far *get_fdtf(CVT far *cvt) * * cvt - адрес векторной таблицы связи * *.Return Указатель на первый блок DDCB **/ #include <stdlib.h> #include <stdio.h> #include "sysp.h" DFT far *get_fdft(CVT far *cvt) { DFT far * dft; dft = cvt->file_tab; return(dft); } /** *.Name get_ndft * *.Title Получить адрес следующей DTF * *.Descr Функция возвращает адрес следующей * таблицы файлов DOS или 0, если это последняя таблица * *.Params DFT far *get_ndft(DFT far *dft) * * dft - адрес предыдущей таблицы DFT * *.Return Указатель на следующую DFT или 0, если последняя **/ #include <dos.h> #include <stdlib.h> #include <stdio.h> #include "sysp.h" DFT far *get_ndft(DFT far *dft) { DFT far * dft_next; dft_next = dft->next; if(FP_OFF(dft_next) == 0xffff) return((DFT far *)0); return(dft_next); }
Для подробной распечатки содержимого таблицы файлов можно использовать следующую программу, которая была проверена в MS-DOS версии 4.01: #include <dos.h> #include <stdio.h> #include <stdlib.h> #include "sysp.h" void main(void); void main(void) { CVT far *cvt; DFT far *dft; unsigned i,j,k; DFCB far *dfcb; FILE *list; printf("Информация об открытых файлах DOS\n" "Copyright Frolov A. (C),1990\n"); // Открываем файл для вывода информации о файлах list=fopen("!dfcb.lst","w+"); fprintf(list,"Информация об открытых файлах DOS\n" "Copyright Frolov A. (C),1990\n\n"); cvt=get_mcvt(); // Адрес векторной таблицы связи dft=get_fdft(cvt); // Адрес начала таблицы файлов for(;;) { if(dft == (DDCB far *)0) break; // Конец таблицы i=dft->file_count; fprintf(list,"Таблица файлов DFT: %Fp, в ней %d файлов\n" "===========================================\n", dft,i); for(j=0;j<i;j++) { // Цикл по файловым // управляющим блокам dfcb=(&(dft->dfcb))+j; // Адрес DFCB файла fprintf(list,"\nDFCB файла: %Fp\n\n",dfcb); fprintf(list,"Имя файла: "); for(k=0;k<11;k++) { fputc(dfcb->filename[k],list); } fprintf(list,"\nКоличество file handles: %d\n" "Режим доступа: %d\n" "Поле reserv1: %04X\n" "Информация об устройстве: %04X\n" "Адрес драйвера: %Fp\n" "Начальный кластер: %d\n" "Время: %04X\n" "Дата: %04X\n" "Размер файла в байтах: %ld\n" "Текущее смещение в файле: %ld\n" "Поле reserv2: %04X\n" "Последний прочитанный кластер: %d\n" "Сегмент PSP владельца файла: %04X\n" "Поле reserv7: %d\n" "-------------------------------\n\n", dfcb->handl_num, dfcb->access_mode, dfcb->reserv1, dfcb->dev_info, dfcb->driver, dfcb->first_clu, dfcb->time, dfcb->date, dfcb->fl_size, dfcb->offset, dfcb->reserv2, dfcb->last_clu, dfcb->ownr_psp, dfcb->reserv7); } dft=get_ndft(dft); } fclose(list); exit(0); }
Описание содержимого таблицы файлов будет записано в файл с именем "!dfcb.lst".
Таблица 7
(0) 2 | seg_env | сегментный адрес среды, которая создается родительской программой для запускаемой программы. Если в этом поле находится 0, то для запускаемой программы копируется среда родительской программы |
(+2) 4 | cmd | FAR-адрес строки параметров для запускаемой программы. |
(+6) 4 | fcb1 | адрес блока FCB, который будет помещен в PSP со смещением 5Ch |
(+10) 4 | fcb2 | адрес блока FCB, который будет помещен в PSP со смещением 6Ch. |
Таблица 7
(0) 13 | header | Заголовок запроса. |
(+13) 1 | n_units | Количество устройств, обслуживаемых драйвером. Это поле заполняется только блочным драйвером. |
(+14) 4 | end_addr | Конечный FAR-адрес резидентной части кода драйвера. В это поле драйвер записывает адрес байта памяти, следующего за той частью кода драйвера, которая должна стать резидентной. |
(+18) 4 | parm | FAR-адрес строки параметров инициализации драйвера из файла CONFIG.SYS. Эта строка содержит все, что находится в строке файла после команды 'DEVICE=', она заканчивается символами перевода строки и возврата каретки 0Ah, 0Dh. При возврате драйвер блочного устройства должен записать в это поле адрес массива указателей на блоки параметров BIOSBIOS (BPB), по одному указателю на каждое устройство, обслуживаемое драйвером. |
(+22) 1 | drive | Номер устройства. Для версии DOS 3.0 и более поздних версий в это поле при загрузке драйвера операционная система заносит номер, назначенный устройству, обслуживаемому драйвером. Например, для устройства А: это 0, для B: - 1 и т.д. |
Затем драйвер может выполнить инициализацию обслуживаемого физического устройства ввода/вывода, инициализацию своих внутренних переменных, вывести на экран какие-либо сообщения либо даже запросить у оператора дополнительные данные - функция инициализации может пользоваться для организации диалога с оператором и других действий функциями прерывания 21h с номерами от 01h до 0Ch, 25h, 30h, 35h и функциями BIOS.
Кроме этого, драйвер должен заполнить поле end_addr адресом конца резидентной части драйвера. Так как программа инициализации выполняется только один раз, обычно ее располагают в конце драйвера и для экономии памяти не оставляют резидентной.
Драйверы блочных устройств дополнительно должны возвратить DOS количество обслуживаемых устройств (в поле n_units) и указатель на массив указателей на блоки BPB (в поле parm).
Количество устройств используется DOS для определения логических имен устройств. Например, если Ваш драйвер обслуживает три логических устройства, и на момент его загрузки в системе имеются устройства A:, B: и C:, то устройства, обслуживаемые Вашим драйвером, получат имена D:, E: и F:. Количество устройств необходимо указывать также и в заголовке драйвера, в первом байте поля имени устройства dev_name.
Для каждого логического устройства драйвер должен содержать так называемый блок параметров BIOS (BIOS Parameter Block) BPB.
Блок BPB содержится в загрузочном секторе диска и содержит информацию, необходимую BIOS для работы с диском. Приведем формат BPB: