используется и в другом случае. Библиотека С предлагает три способа получения строк, предназначенных для описания возникшей ошибки.
perror
Печатает сообщение об ошибке. Передайте в функцию строку с информацией о том, что код намеревался предпринять.
if ((file = open(DB_PATH, O_RDONLY)) < 0) {
perror("не удается открыть файл базы данных");
}
Функция
perror
выведет сообщение, описывающее возникшую ошибку, а также объяснение того, что код собирался делать:
не
удается открыть файл базы данных: No such file or directory
Обычно неплохо делать свои аргументы для
perror
уникальными на протяжении всей программы, в результате при получении сообщений об ошибках из
perror
вы будете точно знать, откуда начинать поиск. Обратите внимание, что в строке, передаваемой
perror
, нет символа новой строки. Его передавать не нужно — функция сама его выведет.
strerror
Возвращает статически распределенную строку, описывающую ошибку с номером, передаваемым в единственном аргументе. Это можно использовать при построении, например, своей собственной версии
perror
.
if ((file = open(DB_PATH, O_RDONLY) ) < 0) {
fprintf(stderr,
"не удается открыть файл базы данных %s, %s\n",
DB_PATH, strerror(errno));
}
sys_errlist
He очень хорошая альтернатива
strerror
.
sys_errlist
— это массив размером
sysnerr
указателей на статические, доступные только для чтения символьные строки, которые описывают ошибки. Попытка записи в эти строки приводит к нарушению сегментации и сбросу дампа ядра.
if ((file = open(DB_PATH, O_RDONLY)) < 0) {
if (errno < sys_nerr) {
fprintf(stderr,
"не удается открыть файл базы данных %s, %s\n",
DB_PATH, sys_errlist[errno]);
}
}
Этот массив не является ни стандартным, ни переносимым, и упоминается здесь лишь потому, что вы можете столкнуться с кодом, от него зависящим. Заменив такой код вызовом функции
strerror
, вы получите существенный выигрыш.
Если вы не собираетесь использовать
errno
сразу же после генерации ошибки, сохраните ее копию. Любая библиотечная функция может установить
errno
в любое значение, поскольку в ней могут присутствовать системные вызовы, о которых вы даже не подозреваете. А некоторые библиотечные функции могут устанавливать
errno
даже без системных вызовов.
9.2.3. Использование системных вызовов
Интерфейс, с которым вам, как программисту, возможно, доведется работать, представляет собой набор оболочек библиотеки С для системных вызовов. В оставшейся части этой книги под системным вызовом будет подразумеваться функция оболочки С, которая используется для реализации системного вызова, а не тот неуклюжий интерфейс, который скрывает библиотека С.
Большинство, но не все, системные вызовы объявлены в
<unistd.h>
. Фактически файл
<unistd.h>
представляет собой универсальное вместилище практически для всех системных вызовов. Чтобы определить, какие включаемые файлы нужно использовать, обычно нужно обратиться к системным man-страницам. Хотя описания функций на man-страницах зачастую весьма лаконичны, там можно найти точные указания о том, какой файл должен быть включен для использования функции.
Есть одна особенность, свойственная системам Unix. Системные вызовы документированы в отдельном разделе man-страниц для библиотечных функций, и вы будете использовать библиотечные функции для доступа к системным вызовам. Там, где библиотечные функции отличаются от системных вызовов, предусмотрены отдельные man- страницы. Это не вызывало бы проблем, однако практически всегда требуется читать страницу, описывающую библиотечную функцию, номер которой больше номера страницы с описанием соответствующего системного вызова. Ввиду того, что man-страницы выводятся, начиная с меньших номеров, приходится проделывать лишнюю работу.
Простого указания номера раздела недостаточно. Системные вызовы, которые помещены в минимальные функции-оболочки из библиотеки С, не документированы как часть библиотеки, следовательно команда
man 3 функция
не найдет их. Для того чтобы убедиться, что вы прочли всю необходимую для вас информацию, вначале взгляните на man-страницу, не указывая раздел. Если это раздел 2 на man-странице, посмотрите, есть ли там раздел 3 с таким же именем. Если открывается раздел 1 man-страницы, как это часто случается, внимательно просмотрите разделы 2 и 3.
К счастью, существует другой способ решения такой проблемы. Многие версии программы
man
, включая используемую в системах Linux, позволяют указывать альтернативный путь поиска man-страниц. Прочтите man-страницу о самой программе
man
, чтобы определить, поддерживается ли в вашей версии
man
переменная окружения
MANSECT
и аргумент
– S
. Если переменная поддерживается, можно установить
MANSECT
в что-нибудь вроде
3:2:1:4:5:6:7:8:tcl:n:l:p:о
. Просмотрите в файле конфигурации
man
(в большинстве систем Linux это
/etc/man.config
) текущую настройку
MANSECT
.
Большинство системных вызовов возвращает
0
при успешном выполнении. В случае возникновения ошибки они возвращают отрицательное значение. Вследствие этого, во многих случаях, подходит простейшая форма обработки ошибок.
if (ioctl(fd, FN, data)) {
/* обработка ошибки на основе errno */
}
Часто встречается следующая форма:
if (ioctl(fd, FN, data) < 0) {
/* обработка ошибки на основе errno */
}
Для системных вызовов, которые возвращают
0
, обозначая успех, оба эти случая идентичны. В своем коде можете выбрать то, что вам больше подходит. Будьте готовы к тому, что столкнетесь с этими и другими способами обработки в чужих кодах.
9.2.4. Общие коды возврата ошибок
Существует множество общих кодов ошибок, для которых вы вполне могли наблюдать сообщения. Некоторые из этих сообщений могут сбивать с толку. Без знаний о том, что можно делать в Linux-системе, трудно понять ошибки, которые могут возникать в процессе работы. Внимательно прочитайте приведенный ниже список.