Чтение онлайн

ЖАНРЫ

Шрифт:

Функция выводит в стандартный поток сообщений об ошибках информацию об ошибочной ситуации, основываясь на значении переменной errno. Строка s, передаваемая функции, предваряет это сообщение и может служить дополнительной информацией, например содержа название функции или программы, в которой произошла ошибка.

Следующий пример иллюстрирует использование этих двух функций:

#include <errno.h>

#include <stdio.h>

main(int argc, char *argv[]) {

 fprintf(stderr, "ENOMEM: %s\n", strerror(ENOMEM));

 errno = ENOEXEC;

 perror(argv[0]);

}

Запустив программу, мы получим следующий результат на экране:

$ a.out

ENOMEM: Not enough space

a.out: Exec format error

Эти функции используются, в частности, командным интерпретатором и большинством стандартных утилит UNIX. Например:

$ rm does_not_exist

does_not_exist: No such file or directory
ошибка ENOENT

$ pg do_not_read

do_not_read: Permission denied
 ошибка EACCESS

$

В табл. 2.1 приведены наиболее общие ошибки системных вызовов, включая сообщения, которые обычно выводят функции strerror(3C) и perror(3C), а также их краткое описание.

Таблица 2.1. Некоторые ошибки системных вызовов

Код ошибки и сообщение Описание
E2BIG Arg list too long Размер списка аргументов, переданных системному вызову exec(2), плюс размер экспортируемых переменных окружения превышает ARG_MAX байт
EACCESS Permission denied Попытка доступа к файлу с недостаточными правами для данного класса (определяемого эффективным UID и GID процесса и соответствующими идентификаторами файла)
EAGAIN Resource temporarily unavailable Превышен
предел использования некоторого ресурса, например, переполнена таблица процессов или пользователь превысил ограничение по количеству процессов с одинаковым UID. Причиной также может являться недостаток памяти или превышение соответствующего ограничения (см. раздел "Ограничения" далее в этой главе)
EALREADY Operation already in progress Попытка операции с неблокируемым объектом, уже обслуживающим некоторую операцию
EBADF Bad file number Попытка операции с файловым дескриптором, не адресующим никакой файл; также попытка операции чтения или записи с файловым дескриптором, полученным при открытии файла на запись или чтение, соответственно
EBADFD File descriptor in bad state Файловый дескриптор не адресует открытый файл или попытка операции чтения с файловым дескриптором, полученным при открытии файла только на запись
EBUSY Device busy Попытка монтирования устройства (файловой системы), которое уже примонтировано; попытка размонтировать файловую систему, имеющую открытые файлы; попытка обращения к недоступным ресурсам (семафоры, блокираторы и т.п.)
ECHILD No child processes Вызов функции wait(2) процессом, не имеющим дочерних процессов или процессов, для которых уже был сделан вызов wait(2)
EDQUOT Disk quota exceeded Попытка записи в файл, создание каталога или файла при превышении квоты пользователя на дисковые блоки, попытка создания файла при превышении пользовательской квоты на число inode
EEXIST File exists Имя существующего файла использовано в недопустимом контексте, например, сделана попытка создания символической связи с именем уже существующего файла
EFAULT Bad address Аппаратная ошибка при попытке использования системой аргумента функции, например, в качестве указателя передан недопустимый адрес
EFBIG File too large Размер файла превысил установленное ограничение RLIMIT_FSIZE или максимально допустимый размер для данной файловой системы (см. раздел "Ограничения" далее в этой главе)
EINPROGRESS Operation now in progress Попытка длительной операции (например, установление сетевого соединения) для неблокируемого объекта
EINTR Interrupted system call Получение асинхронного сигнала, например, сигнала SIGINT или SIGQUIT, во время обработки системного вызова. Если выполнение процесса будет продолжено после обработки сигнала, прерванный системный вызов завершится с этой ошибкой
EINVAL Invalid argument Передача неверного аргумента системному вызову. Например, размонтирование устройства (файловой системы), которое не было примонтировано. Другой пример — передача номера несуществующего сигнала системному вызову kill(2)
EIO I/O error Ошибка ввода/вывода физического устройства
EISDIR Is a directory Попытка операции, недопустимой для каталога, например, запись в каталог с помощью вызова write(2)
ELOOP Number of symbolic links encountered during path name traversal exceeds MAXSYMLINKS При попытке трансляции имени файла было обнаружено недопустимо большое число символических связей, превышающее значение MAXSYMLINKS
EMFILE Too many open files Число открытых файлов для процесса превысило максимальное значение OPEN_MAX
ENAMETOOLONG File name too long Длина полного имени файла (включая путь) превысила максимальное значение PATH_MAX
ENFILE File table overflow Переполнение файловой таблицы
ENODEV No such device Попытка недопустимой операции для устройства. Например, попытка чтения устройства только для записи или операция для несуществующего устройства
ENOENT No such file or directory Файл с указанным именем не существует или отсутствует каталог, указанный в полном имени файла
ENOEXEC Exec format error Попытка запуска на выполнение файла, который имеет права на выполнение, но не является файлом допустимого исполняемого формата
ENOMEM Not enough space При попытке запуска программы (exec(2)) или размещения памяти (brk(2)) размер запрашиваемой памяти превысил максимально возможный в системе
ENOMSG No message of desired type Попытка получения сообщения определенного типа, которого не существует в очереди (см. раздел "Сообщения" в главе 3)
ENOSPC No space left on device Попытка записи в файл или создания нового каталога при отсутствии свободного места на устройстве (в файловой системе)
ENOSR Out of stream resources Отсутствие очередей или головных модулей при попытке открытия устройства STREAMS. Это состояние является временным. После освобождения соответствующих ресурсов другими процессами операция может пройти успешно
ENOSTR Not a stream device Попытка применения операции, определенной для устройств типа STREAMS (например системного вызова putmsg(2) или getmsg(2)), для устройства другого типа
ENOTDIR Not a directory В операции, предусматривающей в качестве аргумента имя каталога, было указано имя файла другого типа (например, в пути для полного имени файла)
ENOTTY Inappropriate ioctl for device Попытка системного вызова ioctl(2) для устройства, которое не является символьным
EPERM Not owner Попытка модификации файла, способом, разрешенным только владельцу и суперпользователю и запрещенным остальным пользователям. Попытка операции, разрешенной только суперпользователю
EPIPE Broken pipe Попытка записи в канал (pipe), для которого не существует процесса, принимающего данные. В этой ситуации процессу обычно отправляется соответствующий сигнал. Ошибка возвращается при игнорировании сигнала
EROFS Read-only file system Попытка модификации файла или каталога для устройства (файловой системы), примонтированного только на чтение
ESRCH No such process Процесс с указанным PID не существует в системе

Создание программы

Создание любой программы обычно начинается с базовой идеи (но не всегда), разработки ее блок-схемы (современные программисты часто пропускают этот этап), интерфейса пользователя (весьма ответственный процесс) и написания исходного текста. Далее следуют этапы компиляции и отладки.

В этом разделе рассмотрен процесс создания приложения, написанного на языке С и разработанного для операционной системы UNIX. Предвидя обвинения в архаизме, мы все-таки остановимся на добротном ANSI С и базовой среде разработки UNIX, во-первых, полагая, что старый друг лучше новых двух, а во-вторых потому, что объектом нашего обсуждения все же является UNIX, а не современные средства создания приложений. Заметим также, что язык программирования С является "родным" языком UNIX, поскольку ядро операционной системы написано на этом языке [15] . Это, безусловно, не ограничивает возможности других языков и технологий программирования, которые сегодня, наверное, используются даже чаще, чем обсуждаемый нами традиционный подход.

15

Несмотря на то, что многие современные версии UNIX (особенно коммерческие) поставляются без исходных текстов, основная часть кода ядра в них получена путем компиляции C-модулей.

Опустим также процесс рождения базовой идеи и разработку блок-схем, полагая, что все это уже сделано. Итак, начнем с исходного текста будущей программы.

Исходный текст

Исходные тексты программы, разработанной для UNIX, по большому счету мало отличаются от текстов приложений, создаваемых для других операционных систем. Можно сказать уверенно, что синтаксис языка определяется не операционной системой. Все, что вам потребуется, это хорошее знание самого языка и особенностей системы UNIX, а именно — ее системных вызовов.Во-первых, не забудьте включить в исходный текст необходимые файлы заголовков. Во-вторых, уточните синтаксис вызова библиотечных и системных функций. В-третьих, используйте их по назначению. В-четвертых, не пренебрегайте комментариями.

В этом (за исключением, пожалуй, четвертого совета) вам помогут электронный справочник man(1), ваш опыт, и, надеюсь, эта книга.

Заголовки

Использование системных функций обычно требует включения в текст программы файлов заголовков, содержащих определения функций — число передаваемых аргументов, типы аргументов и возвращаемого значения. Большинство системных файлов заголовков расположены в каталогах /usr/include или /usr/include/sys. Если вы планируете использовать малознакомую системную функцию, будет нелишним изучить соответствующий раздел электронного справочника man(1). Там же, помимо описания формата функции, возвращаемого значения и особых ситуаций, вы найдете указание, какие файлы заголовков следует включить в программу.

Файлы заголовков включаются в программу с помощью директивы #include. При этом, если имя файла заключено в угловые скобки (<>), это означает, что поиск файла будет производиться в общепринятых каталогах хранения файлов заголовков. Если же имя файла заголовка заключено в кавычки, то используется явно указанное абсолютное или относительное имя файла.

Например, системный вызов creat(2) служащий для создания обычного файла, объявлен в файле <fcntl.h> следующим образом:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int creat(const char *path, mode_t mode);

Включение в исходный текст прототипа системного вызова creat(2)

позволяет компилятору произвести дополнительную проверку правильности использования этой функции, а именно — числа аргументов и их типов. Можно заметить, что наряду со стандартными типами языка С, например char, для второго аргумента creat(2) используется производный тип — mode_t. В ранних версиях UNIX большинство системных вызовов использовали стандартные типы, например, creat(2) для второго аргумента охотно принимала тип int. Производные типы переменных, имеющие окончание _t, которые вы в большом количестве встретите при программировании в UNIX, получили название примитивов системных данных. Большинство этих типов определены в файле <sys/types.h>, а их назначение заключается в улучшении переносимости написанных программ. Вместо конкретных типов данных, каковыми являются int, char и т.п., приложению предлагается набор системных типов, гарантированно неизменных в контексте системных вызовов. Другими словами, во всех версиях UNIX сегодня и спустя десять лет, системный вызов creat(2) в качестве второго аргумента будет принимать переменную типа mode_t. Фактический размер переменных этого типа может быть разным для различных версий системы, но это отразится в изменении соответствующего файла заголовков и потребует только перекомпиляции вашей программы.

Среда программирования UNIX определяется несколькими стандартами, обсуждавшимися во введении, и может незначительно различаться для разных версий системы. В частности, стандарты ANSI С, POSIX. 1 и XPG4, определяют названия и назначения файлов заголовков, приведенных в табл. 2.2.

Таблица 2.2. Стандартные файлы заголовков

Файл заголовка Назначение
<assert.h> Содержит прототип функции assert(3C), используемой для диагностики
<cpio.h> Содержит определения, используемые для файловых архивов cpio(1)
<ctype.h> Содержит определения символьных типов, а также прототипы функций определения классов символов (ASCII, печатные, цифровые и т.д.) — isascii(3C), isprint(3C), isdigit(3C) и т.д.
<dirent.h> Содержит определения структур данных каталога, а также прототипы функций работы с каталогами opendir(3C), readdir(3C) и т.д.
<errno.h> Содержит определения кодов ошибок (см. раздел "Обработка ошибок" в начале главы)
<fcntl.h> Содержит прототипы системных вызовов fcntl(2), open(2) и creat(2), а также определения констант и структур данных, необходимых при работе с файлами
<float.h> Содержит определения констант, необходимых для операций с плавающей точкой
<ftw.h> Содержит прототипы функций, используемых для сканирования дерева файловой системы (file tree walk) ftw(3C) и nftw(3C), a также определения используемых констант
<grp.h> Содержит прототипы функций и определения структур данных, используемых для работы с группами пользователей: getgrnam(3C), getgrent(3C), getgrgid(3C) и т.д.
<langinfo.h> Содержит определения языковых констант: дни недели, названия месяцев и т.д., а также прототип функции langinfo(3C)
<limits.h> Содержит определения констант, определяющих значения ограничений для данной реализации: минимальные и максимальные значения основных типов данных, максимальное значение файловых связей, максимальная длина имени файла и т.д.
<locale.h> Содержит определения констант, используемых для создания пользовательской среды, зависящей от языковых и культурных традиций (форматы дат, денежные форматы и т.д.), а также прототип функции setlocale(3C)
<math.h> Содержит определения математических констант (, е, 2 и т.д.)
<nl_types.h> Содержит определения для каталогов сообщений (message catalog), а также прототипы функций catopen(3C) и catclose(3C)
<pwd.h> Содержит определение структуры файла паролей /etc/passwd, а также прототипы функций работы с ним: getpwnam(3C), getpwent(3C), getpwuid(3C) и т.д.
<regex.h> Содержит определения констант и структур данных, используемых в регулярных выражениях, а также прототипы функций для работы с ними: regcomp(3C), regexec(3C) и т.д.
<search.h> Содержит определения констант и структур данных, а также прототипы функций, необходимых для поиска: hsearch(3C), hcreate(3C), hdestroy(3C)
<setjmp.h> Содержит прототипы функций перехода setjmp(3C), sigsetjmp(3C), longjmp(3C), siglongjmp(3C), а также определения связанных с ними структур данных
<signal.h> Содержит определения констант и прототипы функций, необходимых для работы с сигналами: sigsetops(3C), sigemptyset(3C), sigaddset(3C) и т.д. (см. раздел "Сигналы" далее в этой главе)
<stdarg.h> Содержит определения, необходимые для поддержки списков аргументов переменной длины
<stddef.h> Содержит стандартные определения (например size_t)
<stdio.h> Содержит определения стандартной библиотеки ввода/вывода
<stdlib.h> Содержит определения стандартной библиотеки
<string.h> Содержит прототипы функций работы со строками string(3C), strcasecmp(3C), strcat(3C), strcpy(3C) и т.д.
<tar.h> Содержит определения, используемые для файловых архивов tar(1)
<termios.h> Содержит определения констант, структур данных и прототипы функций для обработки терминального ввода/вывода
<time.h> Содержит определения типов, констант и прототипы функций для работы со временем и датами: time(2), ctime(3C), localtime(3C), tzset(3C), а также определения, относящиеся к таймерам getitimer(2), setitimer(2). Таймеры будут рассмотрены в главе 3
<ulimit.h> Содержит определения констант и прототип системного вызова ulimit(2) для управления ограничениями, накладываемыми на процесс. См. также раздел "Ограничения" далее в этой главе
<unistd.h> Содержит определения системных символьных констант, а также прототипы большинства системных вызовов
<utime.h> Содержит определения структур данных и прототип системного вызова utime(2) для работы с временными характеристиками файла (временем доступа и модификации)
<sys/ipc.h> Содержит определения, относящиеся к системе межпроцессного взаимодействия (IPC), которые рассматриваются в главе 3
<sys/msg.h> Содержит определения, относящиеся к (сообщениям) подсистеме IPC. См. также раздел "Сообщения" главы 3
<sys/resource.h> Содержит определения констант и прототипы системных вызовов управления размерами ресурсов, доступных процессу: getrlimit(2) и setrlimit(2). Более подробно ограничения на ресурсы обсуждаются в разделе "Ограничения" далее в этой главе
<sys/sem.h> Содержит определения, относящиеся к (семафорам) подсистеме IPC. См. также раздел "Семафоры" главы 3
<sys/shm.h> Содержит определения, относящиеся к (разделяемой памяти) подсистеме IPC. См. также раздел "Разделяемая память" главы 3
<sys/stat.h> Содержит определения структур данных и прототипы системных вызовов, необходимых для получения информации о файле: stat(2), lstat(2), fstat(2). Подробнее эти системные вызовы рассмотрены в разделе "Метаданные файла" далее в этой главе
<sys/times.h> Содержит определения структур данных и прототипа системного вызова times(2), служащего для получения статистики выполнения процесса (времени выполнения в режиме ядра, задачи и т.д.)
<sys/types.h> Содержит определения примитивов системных данных
<sys/utsname.h> Содержит определения структур данных и прототип системного вызова uname(2), используемого для получения имен системы (компьютера, операционной системы, версии и т.д.)
<sys/wait.h> Содержит определения констант и прототипы системных вызовов wait(2), waitpid(2), используемых для синхронизации выполнения родственных процессов

Компиляция

Процедура создания большинства приложений является общей и приведена на рис. 2.2.

Рис. 2.2. Схема компиляции программы

Первой фазой является стадия компиляции, когда файлы с исходными текстами программы, включая файлы заголовков, обрабатываются компилятором cc(1). Параметры компиляции задаются либо с помощью файла makefile (или Makefile), либо явным указанием необходимых опций компилятора в командной строке. В итоге компилятор создает набор промежуточных объектных файлов. Традиционно имена созданных объектных файлов имеют суффикс ".o".

Поделиться с друзьями: