4.4.2. Открытие и закрытие файлов
4.4.2. Открытие и закрытие файлов
Новые дескрипторы файлов получают (наряду с другими источниками) в результате системного вызова open(). Этот системный вызов открывает файл для чтения или записи и возвращает новый дескриптор файла для последующих операций с этим файлом. Мы видели объявление раньше:
#include <sys/types.h> /* POSIX */
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int open(const char *pathname, int flags, mode_t mode);
Три аргумента следующие:
const char *pathname
Строка С, представляющая имя открываемого файла.
int flags
Поразрядное ИЛИ с одной или более констант, определенных в <fcntl.h>. Вскоре мы их рассмотрим.
mode_t mode
Режимы доступа для создаваемого файла. Это обсуждается далее в главе, см. раздел 4.6 «Создание файлов». При открытии существующего файла опустите этот параметр[46].
Возвращаемое open() значение является либо новым дескриптором файла, либо -1, означающим ошибку, в этом случае будет установлена errno. Для простого ввода/вывода аргумент flags должен быть одним из значений из табл. 4.3.
Таблица 4.3. Значения flags для open()
Именованная константа Значение Комментарий O_RDONLY 0 Открыть файл только для чтения, запись невозможны O_WRONLY 1 Открыть файл только для записи, чтение невозможно O_RDWR 2 Открыть файл для чтения и записиВскоре мы увидим пример кода. Дополнительные значения flags описаны в разделе 4.6 «Создание файлов». Большой объем ранее написанного кода Unix не использовал эти символические значения. Вместо этого использовались числовые значения. Сегодня это рассматривается как плохая практика, но мы представляем эти значения, чтобы вы их распознали, если встретитесь с ними
Системный вызов close() закрывает файл: его элемент в системной таблице дескрипторов файлов помечается как неиспользуемый, и с этим дескриптором нельзя производить никаких дальнейших действий. Объявление следующее:
#include <unistd.h> /* POSIX */
int close(int fd);
В случае успеха возвращается 0, при ошибке (-1). При возникновении ошибки нельзя ничего сделать, кроме сообщения о ней. Ошибки при закрытии файлов являются необычными, но не невозможными, особенно для файлов, доступ к которым осуществляется через сеть. Поэтому хорошей практикой является проверка возвращаемого значения, особенно для файлов, открытых для записи.
Если вы будете игнорировать возвращаемое значение, специально приведите его к типу void, чтобы указать, что вам не нужен результат:
(void)close(fd); /* отказ от возвращаемого значения */
Легкомысленность этого совета в том, что слишком большое количество приведений к void имеют тенденцию загромождать код. Например, несмотря на принцип «всегда проверять возвращаемое значение», чрезвычайно редко можно увидеть код, проверяющий возвращаемое значение printf() или приводящий его к void. Как и со многими аспектами программирования на С, здесь также требуются опыт и рассудительность.
Как упоминалось, число открытых файлов, если оно большое, ограничивается, и вам всегда следует закрывать файлы, когда работа с ними закончена. Если вы этого не сделаете, то в конечном счете выйдете за пределы лимита дескрипторов файлов, создав ситуацию, которая ведет к потере устойчивости части вашей программы.
Система закрывает все открытые файлы, когда процесс завершается, но — за исключением 0, 1 и 2 — плохая манера полагаться на это.
Когда open() возвращает новый дескриптор файла, она всегда возвращает наименьшее неиспользуемое целое значение. Всегда. Поэтому, если открыты дескрипторы файлов 0–6 и программа закрывает дескриптор файла 5, следующий вызов open() вернет 5, а не 7. Это поведение важно; далее в книге мы увидим, как оно используется для аккуратной реализации многих важных особенностей Unix, таких, как перенаправление ввода/вывода и конвейеризация (piping)