Андрей Робачевский - Операционная система UNIX
Создание программы
Создание любой программы обычно начинается с базовой идеи (но не всегда), разработки ее блок-схемы (современные программисты часто пропускают этот этап), интерфейса пользователя (весьма ответственный процесс) и написания исходного текста. Далее следуют этапы компиляции и отладки.
В этом разделе рассмотрен процесс создания приложения, написанного на языке С и разработанного для операционной системы UNIX. Предвидя обвинения в архаизме, мы все-таки остановимся на добротном ANSI С и базовой среде разработки UNIX, во-первых, полагая, что старый друг лучше новых двух, а во-вторых потому, что объектом нашего обсуждения все же является UNIX, а не современные средства создания приложений. Заметим также, что язык программирования С является "родным" языком UNIX, поскольку ядро операционной системы написано на этом языке[15]. Это, безусловно, не ограничивает возможности других языков и технологий программирования, которые сегодня, наверное, используются даже чаще, чем обсуждаемый нами традиционный подход.
Опустим также процесс рождения базовой идеи и разработку блок-схем, полагая, что все это уже сделано. Итак, начнем с исходного текста будущей программы.
Исходный текст
Исходные тексты программы, разработанной для 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), используемых для синхронизации выполнения родственных процессовКомпиляция