2.2.3. Коды ошибок системных вызовов
2.2.3. Коды ошибок системных вызовов
Большинство системных вызовов возвращает 0, если операция выполнена успешно, и ненулевое значение — в случае сбоя. (В некоторых случаях используются другие соглашения. Например, функция malloc() при возникновении ошибки возвращает нулевой указатель. Никогда не помешает прочесть man-страницу, посвященную требуемому системному вызову.) Обычно этой информации достаточно для того, чтобы решить, следует ли продолжать привычное выполнение программы. Но для более специализированной обработки ошибок необходимы дополнительные сведения.
Практически все системные вызовы сохраняют в специальной переменной errno расширенную информацию о произошедшей ошибке.[7] В эту переменную записывается число, идентифицирующее возникшую ситуацию. Поскольку все системные вызовы работают с одной и той же переменной, необходимо сразу же после завершения функции скопировать значение переменной в другое место. Переменная errno модифицируется после каждого системного вызова.
Коды ошибок являются целыми числами. Возможные значения задаются макроконстантами препроцессора, которые, по существующему соглашению, записываются прописными буквами и начинаются с литеры "E", например EACCESS и EINVAL. При работе со значениями переменной errno следует всегда использовать макроконстанты, а не реальные числовые значения. Все эти константы определены в файле <errno.h>.
В Linux имеется удобная функция strerror(), возвращающая строковый эквивалент кода ошибки. Эти строки можно включать в сообщения об ошибках. Объявление функции находится в файле <string.h>.
Есть также функция perror() (объявлена в файле <stdio.h>), записывающая сообщение об ошибке непосредственно в поток stderr. Перед собственно сообщением следует размещать строковый префикс, содержащий имя функции или модуля, ставших причиной сбоя.
В следующем фрагменте программы делается попытка открыть файл. Если это не получается, выводится сообщение об ошибке и программа завершает свою работу. Обратите внимание на то, что в случае успеха операции функция open() возвращает дескриптор открытого файла, иначе — -1.
fd = open("inputfile.txt", O_RDONLY);
if (fd == -1) {
/* Открыть файл не удалось.
Вывод сообщения об ошибке и выход. */
fprintf(stderr, "error opening file: %s ", strerror(errno));
exit(1);
}
В зависимости от особенностей программы и используемого системного вызова конкретные действия, предпринимаемые в случае ошибки, могут быть разными: вывод сообщения об ошибке, отмена операции, аварийное завершение программы, повторная попытка и даже игнорирование ошибки. Тем не менее важно включить в программу код, обрабатывающий все возможные варианты ошибок.
Одни из кодов, с которым приходится сталкиваться наиболее часто, особенно в функциях ввода-вывода, — это EINTR. Ряд функций, в частности read(), select() и sleep(), требует определенного времени на выполнение. Они называются блокирующими, так как выполнение программы приостанавливается до тех пор, пока функция не завершится. Но если программа, будучи заблокированной, принимает сигнал, функция завершается, не закончив выполнение операции. В данном случае в переменную errno записывается значение EINTR. Обычно в подобной ситуации следует повторно выполнить системный вызов.
Ниже приведен фрагмент программы, в котором функция chown() меняет владельца файла, определяемого переменной path, назначая вместо него пользователя с идентификатором user_id. Если функция завершается неуспешно, дальнейшие действия программы зависят от значения переменной errno. Обратите внимание на интересный момент: при обнаружении возможной ошибки в самой программе ее выполнение завершается с помощью функции abort() или assert(), вследствие чего генерируется файл дампа оперативной памяти. Анализ этого файла может помочь выяснить природу таких ошибок. В случае невосстанавливаемых ошибок, например нехватки памяти, программа завершается с помощью функции exit(), указывая ненулевой код ошибки: в подобных ситуациях файл дампа оказывается бесполезным.
rval = chown(path, user_id, -1);
if (rval != 0) {
/* Сохраняем переменную errno, поскольку она будет изменена
при следующем системном вызове. */
int error_code = errno;
/* Операция прошла неуспешно; в случае ошибки функция chown()
должна вернуть значение -1. */
assert(rval == -1);
/* Проверяем значение переменной errno и выполняем
соответствующее действие. */
switch (error_code) {
case EPERM: /* Доступ запрещен. */
case EROFS: /* Переменная PATH ссылается на файловую
систему, доступную только для чтения. */
case ENAMETOOLONG: /* Переменная PATH оказалась слишком длинной. */
case ENOENT: /* Переменная PATH ссылается на
несуществующий файл. */
case ENOTDIR: /* Один из компонентов переменной PATH
не является каталогом. */
case EACCES: /* Один из компонентов переменной PATH
недоступен. */
/* Что-то неправильно с файлом, выводим сообщение
об ошибке. */
fprintf(stderr, "error changing ownership of %s: %s ",
path, strerror(error_code));
/* He завершаем программу; можно предоставить пользователю
шанс открыть другой файл. */
break;
case ЕFAULT:
/* Переменная PATH содержит неправильный адрес. Это, скорее
всего, ошибка программы. */
abort();
case ENOMEM:
/* Ядро столкнулось с нехваткой памяти. */
fprintf(stderr, "%s ", strerror(error_code));
exit(1);
default:
/* Произошла какая-то другая, непредвиденная ошибка. Мы
пытались обработать все возможные коды ошибок. Если
что-то пропущено, то это ошибка программы! */
abort();
};
}
В самом начале программного фрагмента можно было поставить следующий код:
rval = chown(path, user_id, -1);
assert(rval == 0);
Но в таком случае, если функция завершится неуспешно, у нас не будет возможности обработать или исправить ошибку и даже просто сообщить о ней. Какую форму проверки использовать — зависит от требований к обнаружению и последующему исправлению ошибок в программе.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Номера системных вызовов
Номера системных вызовов Каждому системному вызову операционной системы Linux присваивается номер системного вызова (syscall number). Этот уникальный номер используется для обращения к определенному системному вызову. Когда процесс выполняет системный вызов из пространства
Производительность системных вызовов
Производительность системных вызовов Системные вызовы в операционной системе Linux работают быстрее, чем во многих других операционных системах. Это отчасти связано с невероятно малым временем переключения контекста. Переход в режим ядра и выход из него являются хорошо
Обработка системных вызовов
Обработка системных вызовов Приложения пользователя не могут непосредственно выполнять код ядра. Они не могут просто вызвать функцию, которая существует в пространстве ядра, так как ядро находится в защищенной области памяти. Если программы смогут непосредственно
Реализация системных вызовов
Реализация системных вызовов Реализация системного вызова в ОС Linux не связана с поведением обработчика системных вызовов. Добавление нового системного вызова в операционной системе Linux является сравнительно простым делом. Тяжелая работа связана с разработкой и
9.2.1. Ограничения системных вызовов
9.2.1. Ограничения системных вызовов Режим ядра защищен от влияния режима пользователя. Одна из таких защит состоит в том, что тип данных, передаваемых между режимами ядра и пользователя, ограничен, легко верифицируется и следует строгим соглашениям.• Длина каждого
9.2.2. Коды возврата системных вызов
9.2.2. Коды возврата системных вызов Коды возврата, зарезервированные для всех системных вызовов — это универсальные коды возврата ошибок, представленные небольшими отрицательными числами. Библиотека С проверяет наличие ошибок каждый раз, когда происходит системный
9.2.3. Использование системных вызовов
9.2.3. Использование системных вызовов Интерфейс, с которым вам, как программисту, возможно, доведется работать, представляет собой набор оболочек библиотеки С для системных вызовов. В оставшейся части этой книги под системным вызовом будет подразумеваться функция
9.2.4. Общие коды возврата ошибок
9.2.4. Общие коды возврата ошибок Существует множество общих кодов ошибок, для которых вы вполне могли наблюдать сообщения. Некоторые из этих сообщений могут сбивать с толку. Без знаний о том, что можно делать в Linux-системе, трудно понять ошибки, которые могут возникать в
Обработка прерванных системных вызовов
Обработка прерванных системных вызовов Термином медленный системный вызов (slow system call), введенным при описании функции accept, мы будем обозначать любой системный вызов, который может быть заблокирован навсегда. Такой системный вызов может никогда не завершиться. В эту
В.1. Трассировка системных вызовов
В.1. Трассировка системных вызовов Многие версии Unix предоставляют возможность трассировки (отслеживания) системных вызовов. Зачастую это может оказаться полезным методом отладки.Работая на этом уровне, необходимо различать системный вызов и функцию. Системный вызов
22.4. Трассировка системных вызовов
22.4. Трассировка системных вызовов Вы когда-нибудь задумывались о том, какие системные вызовы использует наша программа во время своего выполнения? Если да, то этот пункт как раз для вас. Возможно, пока он только удовлетворит ваше любопытство, но через некоторое время эта
2.2.2. Ошибки системных вызовов
2.2.2. Ошибки системных вызовов Большинство из нас училось писать программы, которые выполняются по четко намеченному алгоритму. Мы разделяли программу на задачи и подзадачи, и каждая функция решала свою задачу, вызывая другие функции для решения соответствующих подзадач.
Использование системных вызовов операционной системы MS-DOS
Использование системных вызовов операционной системы MS-DOS Функция Краткое описание bdos вызов системы MS-DOS; используются только регистры DX и AL dosexterr получение значений регистров из системы MS-DOS вызовом 59H FP_OFF возвращает смещение far-указателя FP_SEG возвращает сегмент
Приложение 10. Коды ошибок
Приложение 10. Коды ошибок Коды ошибок, возвращаемые клиентам или модулям PSQL сервером Firebird 1.5.0, представлены в табл. П10.1. Некоторые коды недоступны в более ранних версиях Firebird. Важно убедиться, что сервер и клиент имеют корректную версию файла firebird.msg (interbase.msg для Firebird 1.0.x),
Глава 6 Коды ошибок Windows
Глава 6 Коды ошибок Windows Диалоговые окна В диалоговых окнах обычно выводится код ошибки и ее краткое описание (рис. 6.1). Далеко не всегда короткого описания хватает, чтобы понять, что же случилось, и устранить причину ошибки. А иногда описание непонятно, потому что оно на
Приложение Д Коды ошибок DOS
Приложение Д Коды ошибок DOS 2 – файл не найден3 – путь доступа не найден5 – доступ отвергнут6 – недопустимая обработка8 – недостаточно памяти10 – недопустимая программная среда11 – неверный формат18 – файлы