Выбрать главу

Портируемость системных программ можно улучшить, используя типы системных данных, которые определены в различных стандартах и могут отличаться от типов, присущих языку C. В SUSv3 указывается широкий диапазон типов системных данных, которые должны поддерживаться реализациями и использоваться приложениями.

3.8. Упражнение

3.1. Когда для перезапуска системы используется характерный для Linux системный вызов reboot(), в качестве второго аргумента magic2 необходимо указать одно из магических чисел (например, LINUX_REBOOT_MAGIC2). Какой смысл несут эти числа? (Подсказка: обратите внимание на шестнадцатеричное представление такого числа4.)

3 Подробнее о нем вы можете прочитать по адресу http://microsin.net/programming/arm/lvalue-rvalue.html. — Примеч. пер.

4 Таким образом закодировны дни рождения Торвальдса и его дочерей: https://stackoverflow.com/questions/4808748/magic-numbers-of-the-linux-reboot-system-call.

4. Файловый ввод-вывод: универсальная модель ввода-вывода

Теперь перейдем к подробному рассмотрению API системных вызовов. Лучше всего начать с файлов, поскольку они лежат в основе всей философии UNIX. Основное внимание в этой главе будет уделено системным вызовам, предназначенным для выполнения файлового ввода-вывода.

Вы узнаете, что такое дескриптор файла, а затем мы рассмотрим системные вызовы, составляющие так называемую универсальную модель ввода-вывода. Это те самые системные вызовы, которые открывают и закрывают файл, а также считывают и записывают данные.

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

В главе 5 рассматриваемые здесь вопросы будут расширены дополнительными сведениями, касающимися файлового ввода-вывода. Еще одна особенность файлового ввода-вывода — буферизация — настолько сложна, что заслуживает отдельной главы. Буферизация ввода-вывода в ядре и с помощью средств библиотеки stdio будет описана в главе 13.

4.1. Общее представление

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

Обычно от большинства программ ожидается возможность использования трех стандартных дескрипторов файлов, перечисленных в табл. 4.1. Эти три дескриптора открыты оболочкой от имени программы еще до запуска самой программы. Точнее говоря, программа наследует у оболочки копии дескрипторов файлов, а оболочка обычно работает с этими всегда открытыми тремя дескрипторами файлов. (В интерактивной оболочке эти три дескриптора обычно ссылаются на терминал, на котором запущена оболочка.) Если в командной строке указано перенаправление ввода-вывода, то оболочка перед запуском программы обеспечивает соответствующее изменение дескрипторов файлов.

Таблица 4.1. Стандартные дескрипторы файлов

Дескриптор файла — Назначение — Имя согласно POSIX — Поток stdio

0 — Стандартный ввод — STDIN_FILENO — stdin

1 — Стандартный вывод — STDOUT_FILENO — stdout

2 — Стандартная ошибка — STDERR_FILENO — stderr

При ссылке на эти дескрипторы файлов в программе можно использовать либо номера (0, 1 или 2), либо, что предпочтительнее, стандартные имена POSIX, определенные в файле <unistd.h>.

Хотя переменные stdin, stdout и stderr изначально ссылаются на стандартные ввод, вывод и ошибку процесса, с помощью библиотечной функции freopen() их можно изменить для ссылки на любой файл. В качестве части своей работы freopen() способен изменить дескриптор файла на основе вновь открытого потока. Иными словами, после вызова freopen() в отношении, к примеру, stdout, нельзя с полной уверенностью предполагать, что относящийся к нему дескриптор файла по-прежнему имеет значение 1.