Несмотря на стремление сэкономить на наборе текста путем исключения подобных проверок (особенно после просмотра примеров программ, написанных под UNIX и Linux, где коды завершения не проверяются), это «экономия на спичках». Из-за отсутствия проверки кода, возвращенного системным вызовом или вызовом библиотечной функции, которая «в принципе не должна дать сбой», можно впустую потратить многие часы на отладку.

Есть несколько системных вызовов, никогда не дающих сбоев. Например, getpid() всегда успешно возвращает идентификатор процесса, а _exit() всегда прекращает процесс. Проверять значения, возвращаемые такими системными вызовами, нет никакого смысла.

Обработка ошибок системных вызовов

Возможные возвращаемые вызовом значения документируются на странице руководства по каждому системному вызову, и там показывается значение (или значения), свидетельствующее об ошибке. Обычно ошибка выявляется возвращением значения –1. Следовательно, системный вызов может быть проверен с помощью такого кода:

fd = open(pathname, flags, mode); /* Системный вызов для открытия файла */

if (fd == -1) {

/* Код для обработки ошибки */

}

if (close(fd) == -1) {

/* Код для обработки ошибки */

}

При неудачном завершении системного вызова для глобальной целочисленной переменной errno устанавливается положительное значение, позволяющее идентифицировать конкретную ошибку. Объявление errno, а также набора констант для различных номеров ошибок предоставляется за счет включения заголовочного файла . Все относящиеся к ошибкам символьные имена начинаются с E. Список возможных значений errno, которые могут быть возвращены каждым системным вызовом, предоставляется на каждой странице руководства в разделе заголовочного файла ERRORS. Простой пример использования errno для обнаружения ошибки системного вызова имеет следующий вид:

cnt = read(fd, buf, numbytes);

if (cnt == -1) {

if (errno == EINTR)

fprintf(stderr, "read was interrupted by a signal\n");

// чтение было прервано сигналом

else {

/* Произошла какая-то другая ошибка */

}

}

Успешно завершенные системные вызовы и вызовы библиотечных функций никогда не сбрасывают errno в 0, следовательно, эта переменная может иметь ненулевое значение из-за ошибки предыдущего вызова. Более того, SUSv3 разрешает успешно завершающей свою работу функции устанавливать для errno ненулевое значение (хотя это делают всего несколько функций). Поэтому при проверке на ошибку нужно всегда сперва проверить, не вернула ли функция значение, свидетельствующее о возникновении ошибки, и только потом исследовать errno для определения причины ошибки.

Некоторые системные вызовы (например, getpriority()) могут вполне законно возвращать при успешном завершении значение –1. Чтобы определить, не возникла ли при таких вызовах ошибка, перед самим вызовом нужно установить errno в 0 и проверить ее значение после вызова. Если вызов возвращает –1, а errno имеет ненулевое значение, значит, произошла ошибка. (Это же правило применимо к некоторым библиотечным функциям.)

Общая линия поведения после неудачного системного вызова заключается в выводе сообщения об ошибке на основе значения переменной errno. Для этой цели предоставляются библиотечные функции perror() и strerror().

Функция perror() выводит строку, указываемую с помощью аргумента msg. За строкой следует сообщение, соответствующее текущему значению переменной errno.

#include

void perror(const char *msg);

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

fd = open(pathname, flags, mode);

if (fd == -1) {

perror("open");

exit(EXIT_FAILURE);

}

Функция strerror() возвращает строку описания ошибки, соответствующую номеру ошибки, который задан в ее аргументе errnum.

#include

char *strerror(int errnum);

Возвращает указатель на строку с описанием ошибки, соответствующую значению errnum

Строка, возвращенная strerror(), может быть размещена статически, что означает, что она может быть переписана последующими вызовами strerror().

Если в errnum указан нераспознаваемый номер ошибки, то strerror() возвращает строку вида Unknown error nnn (неизвестная ошибка с таким-то номером). В некоторых других реализациях strerror() в таких случаях возвращает значение NULL.

Поскольку функции perror() и strerror() чувствительны к настройкам локали (см. раздел 10.4), описания ошибок выводятся на языке локали.

Обработка ошибок из библиотечных функций

Различные библиотечные функции для обозначения ошибок возвращают разные типы данных и разные значения. (По каждой функции нужно обращаться к странице руководства.) В рамках этого раздела библиотечные функции могут быть разбиты на несколько категорий.

Перейти на страницу:

Похожие книги