Linux программирование в примерах - Роббинс Арнольд
Шрифт:
Интервал:
Закладка:
Многие системы предоставляют также другие значения ошибок, а в более старых системах может не быть всех перечисленных значений ошибок. Полный список следует проверить с помощью справочных страниц intro(2) и errno(2) для локальной системы.
ЗАМЕЧАНИЕ. errno следует проверять лишь после того, как возникла ошибка и до того, как сделаны дальнейшие системные вызовы. Начальное значение той переменной 0. Однако, в промежутках между ошибками ничто не изменяет ее значения, это означает, что успешный системный вызов не восстанавливает значение 0. Конечно, вы можете вручную установить ее в 0 в самом начале или когда захотите, однако это делается редко.
Сначала мы используем errno лишь для сообщений об ошибках. Для этого имеются две полезные функции. Первая — perror():
#include <stdio.h> /* ISO С */
void perror(const char *s);
Функция perror() выводит предоставленную программой строку, за которой следует двоеточие, а затем строка, описывающая значение errno:
if (some_system_call(param1, param2) < 0) {
perror("system call failed");
return 1;
}
Мы предпочитаем функцию strerror(), которая принимает параметр со значением ошибки и возвращает указатель на строку с описанием ошибки:
#include <string.h> /* ISO С */
char *strerror(int errnum);
strerror() предоставляет для сообщений об ошибках максимальную гибкость, поскольку fprintf() дает возможность выводить ошибки любым нужным нам способом, наподобие этого.
if (some_system_call(param1, param2) < 0) {
fprintf(stderr, "%s: %d, %d: some_system_call failed: %sn",
argv[0], param1, param2, strerror(errno));
return 1;
}
По всей книге вы увидите множество примеров использования обеих функций.
4.3.2. Стиль сообщения об ошибках
Для использования в сообщениях об ошибках С предоставляет несколько специальных макросов. Наиболее широкоупотребительными являются __FILE__ и __LINE__, которые разворачиваются в имя исходного файла и номер текущей строки в этом файле. В С они были доступны с самого начала. C99 определяет дополнительный предопределенный идентификатор, __func__, который представляет имя текущей функции в виде символьной строки. Макросы используются следующим образом:
if (some_system_call(param1, param2) < 0) {
fprintf(stderr, "%s: %s (%s %d): some_system_call(%d, %d) failed: %sn",
argv[0], __func__, __FILE__, __LINE__,
param1, param2, strerror(errno));
return 1;
}
Здесь сообщение об ошибке включает не только имя программы, но также и имя функции, имя исходного файла и номер строки. Полный список идентификаторов, полезных для диагностики, приведен в табл. 4.2.
Таблица 4.2. Диагностические идентификаторы C99
Идентификатор Версия С Значение __DATE__ C89 Дата компиляции в виде «Mmm nn yyyy» __FILE_ Оригинальная Имя исходного файла в виде «program.c» __LINE__ Оригинальная Номер строки исходного файла в виде 42 __TIME__ C89 Время компиляции в виде «hh:mm:ss» __func__ C99 Имя текущей функции, как если бы было объявлено const char __func__[] = "name"Использование __FILE__ и __LINE__ было вполне обычно для ранних дней Unix, когда у большинства людей были исходные коды и они могли находить ошибки и устранять их. По мере того, как системы Unix становились все более коммерческими, использование этих идентификаторов постепенно уменьшалось, поскольку знание положения в исходном коде дает немного пользы, когда имеется лишь двоичный исполняемый файл.
Сегодня, хотя системы GNU/Linux поставляются с исходными кодами, указанный исходный код часто не устанавливается по умолчанию. Поэтому использование этих идентификаторов для сообщений об ошибках не представляет дополнительной ценности. GNU Coding Standards даже не упоминает их.
4.4. Ввод и вывод
Все операции Linux по вводу/выводу осуществляются посредством дескрипторов файлов. Данный раздел знакомит с дескрипторами файлов, описывает, как их получать и освобождать, и объясняет, как выполнять с их помощью ввод/вывод.
4.4.1. Понятие о дескрипторах файлов
Дескриптор файла является целым значением. Действительные дескрипторы файлов начинаются с 0 и растут до некоторого установленного системой предела. Эти целые фактически являются индексами таблицы открытых файлов для каждого процесса (Таблица поддерживается внутри операционной системы; она недоступна запущенным программам.) В большинстве современных систем размеры таблиц большие. Команда 'ulimit -n' печатает это значение:
$ <b>ulimit -n</b>
1024
Из С максимальное число открытых файлов возвращается функцией getdtablesize() (получить размер таблицы дескрипторов):
#include <unistd.h> /* Обычный */
int getdtablesize(void);
Следующая небольшая программа выводит результат работы этой функции:
/* ch04-maxfds.с --- Демонстрация getdtablesize(). */