Программирование в стандарте POSIX

       

Открытие и закрытие файлов


Как уже указывалось, открытие файла должно предшествовать операциям ввода/вывода, поскольку оно возвращает дескриптор файла или поток, которые используют подобные операции. Для открытия файлов и формирования новых описаний открытых файлов, файловых дескрипторов и потоков служат функции нижнего уровня open() и pipe() (см. пример 5.1), а также функции буферизованного ввода/вывода, показанные в пример 5.2.

#include <fcntl.h> int open (const char *path, int oflag, ...); #include <unistd.h> int pipe (int fildes [2]);

Листинг 5.1. Описание функций open() и pipe(). (html, txt)

#include <stdio.h> FILE *fopen (const char *restrict path, const char *restrict mode); #include <stdio.h> FILE *fdopen (int fildes, const char *mode); #include <stdio.h> FILE *freopen (const char *restrict path, const char *restrict mode, FILE *restrict stream);

Листинг 5.2. Описание функций fopen(), fdopen(), freopen(). (html, txt)

Функция open() открывает файл с заданным маршрутным именем (первый аргумент, path), создавая для него описание – новое и, следовательно, не разделяемое с другими процессами. Возвращаемый в качестве результата файловый дескриптор является минимальным из числа не используемых в данный момент текущим процессом (при неудаче возвращается -1).

Второй аргумент, oflag, устанавливает флаги статуса файла и определяет допустимые виды операций ввода/вывода. Его значение формируется как побитное ИЛИ перечисленных ниже флагов. Из первых трех флагов должен быть задан ровно один.

O_RDONLY

Открыть файл только на чтение.

O_WRONLY

Открыть файл только на запись.

O_RDWR

Открыть файл на чтение и запись.

Следующие флаги могут комбинироваться произвольным образом.

O_APPEND

Перед каждой записью устанавливать индикатор текущей позиции на конец файла.

O_CREAT

Если файл существует, данный флаг принимается во внимание только при наличии описываемого далее флага O_EXCL. Если файла нет, он создается от имени текущего пользователя. Обратим внимание, что функция open() имеет переменное число аргументов.
При создании файла предполагается, что в качестве третьего аргумента типа mode_t задается режим доступа.

O_EXCL

Если установлены флаги O_CREAT и O_EXCL, а файл существует (хотя бы и в виде символьной ссылки на несуществующий файл), вызов open() завершится неудачей. Проверка существования файла и его создание представляют собой атомарное действие по отношению к попыткам других процессов (потоков управления) выполнить аналогичный запрос.

O_TRUNC

Если файл существует, является обычным и успешно открывается с флагами O_RDWR или O_WRONLY, он опустошается (размер устанавливается равным нулю).

Отметим, что рассмотренная ранее функция creat (path, mode) по определению эквивалентна вызову open (path, O_WRONLY | O_CREAT | O_TRUNC, mode).

Следующий флаг относится к асинхронному вводу/выводу.

O_NONBLOCK

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

При открытии специального файла вызов open() завершается только после того, как устройство оказывается в состоянии готовности. Флаг O_NONBLOCK отменяет эту задержку, однако последующее поведе- ние устройства зависит от реализации.

Следующая группа флагов обслуживает синхронизированный ввод/вывод.

O_DSYNC

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

O_SYNC

Операции записи должны завершаться с обеспечением целостности файла.

O_RSYNC

Операции чтения должны завершаться на уровне целостности, заданном флагами O_DSYNC или O_SYNC.

Смысл последнего из стандартизованных флагов будет пояснен при рассмотрении процессов.

O_NOCTTY

Если открывается специальный файл, соответствующий терминалу, последний не должен становиться управляющим терминалом процесса. Для создания и открытия канала предназначена функция pipe().В массиве fildes она возвращает сразу два дескриптора: fildes [0]служит для чтения, fildes [1] – для записи. Данные читаются в том же порядке, в каком были записаны.

При успешном завершении pipe() возвращает 0, при неудаче – -1.



В массиве fildes она возвращает сразу два дескриптора: fildes [0]служит для чтения, fildes [1] – для записи. Данные читаются в том же порядке, в каком были записаны.

При успешном завершении pipe() возвращает 0, при неудаче – -1.

Функция fopen() из группы буферизованного ввода/вывода по сути аналогична open(), только вместо файлового дескриптора в качестве результата возвращается указатель на объект, служащий для управления сформированным потоком (в случае неудачи результат равен пустому указателю NULL).

Второй аргумент, mode, определяющий допустимые виды операций ввода/вывода, задается как цепочка символов. Он может принимать следующие значения.

"r"

Открыть файл на чтение.

"w"



Опустошить или создать файл, открыв его на запись.

"a"

Открыть или создать файл на запись в конец.

"r+"

Открыть файл на изменение (чтение и запись).

"w+"

Опустошить или создать файл, открыв его на изменение.

"a+"

Открыть или создать файл на изменение с записью в конец.

(Стандарт языка C позволяет приписывать к перечисленным цепочкам символ 'b', который, впрочем, ни на что не влияет.)

Если открытый файл не соответствует интерактивному устройству, ассоциированный с ним поток полностью буферизуется.

Функция fdopen() формирует поток, ассоциированный с дескриптором ранее открытого файла.

Второй аргумент, mode, может принимать те же значения, что и для fopen(), но их трактовка по понятным причинам отличается: существующие файлы не опустошаются, а новые не создаются.

Функция freopen() предназначена для ассоциирования существующего потока (третий аргумент, stream) с заданным файлом (первый аргумент, path) и разрешенными видами операций ввода/вывода (второй аргумент, mode).

В первую очередь freopen() пытается вытолкнуть буфера потока stream и закрыть ассоциированный с ним файл. Неудача данного действия ни на что не влияет.

Затем, аналогично fopen(), открывается заданный файл, только без формирования нового потока; результатом служит stream (лишенный, правда, ориентации) или NULL (в случае неудачи).



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

Для закрытия файлов (точнее, файловых дескрипторов или потоков) предназначены функции close() и fclose() (см. пример 5.3).

#include <unistd.h> int close (int fildes); #include <stdio.h> int fclose (FILE *stream);

Листинг 5.3. Описание функций close() и fclose().

Функция close() освобождает файловый дескриптор fildes, который становится доступным для последующего использования при открытии файлов.

Когда закрывается последний дескриптор, ссылающийся на описание открытого файла, оно освобождается.

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

Когда закрывается последний дескриптор, ассоциированный с каналом, все оставшиеся непрочитанными данные теряются.

Функция close() возвращает 0 в случае успешного завершения и -1 при неудаче.

Функция fclose() по сути аналогична, только она освобождает поток, выталкивая при этом буфера. Признаком успешного завершения также служит 0, признаком неудачи – константа EOF.

Приведем примеры использования описанных функций. Сочетание флагов O_CREAT и O_EXCL функции open() позволяет организовать проверку и создание файлов-замков, для которых важен факт существования в одном экземпляре, а не содержимое (см. пример 5.4).

#include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <errno.h> #define LOCK_FILE "my_lock" /* Функция пытается создать файл-замок */ /* Результат равен 0 в случае успеха, */ /* 1, если файл уже существует, */ /* -1 в случае прочих ошибок */ static int gate (const char *lock_name) { int fd; if ((fd = open (lock_name, O_WRONLY | O_CREAT | O_EXCL, (mode_t) 0)) < 0) { if (errno == EEXIST) { return (1); } return (-1); } return (close (fd)); } int main (void) { int res; if ((res = gate (LOCK_FILE)) > 0) { perror ("Ошибка при создании файла-замка " LOCK_FILE); } else if (res == 1) { fprintf (stderr, "Файл-замок " LOCK_FILE " уже существует\n"); } return (res); }



Листинг 5.4. Пример программы, использующей функции open() и close().

Читателю предлагается выполнить приведенную программу дважды.

Следующая программа иллюстрирует перенаправление стандартного вывода в файл (см. пример 5.5). Ее тоже полезно выполнить дважды и затем самостоятельно осмыслить результаты.

#include <stdio.h> #define LOGFILE "my_logfile" int main (void) { FILE *fp; printf ("До перенаправления стандартного вывода в файл " LOGFILE "\n"); if ((fp = freopen (LOGFILE, "a", stdout)) == NULL) { perror ("Не удалось перенаправить стандартный вывод в файл " LOGFILE); return (-1); } printf ("После перенаправления стандартного вывода в файл " LOGFILE "\n"); if (fclose (fp) == EOF) { perror ("Не удалось закрыть файл " LOGFILE); return (-1); } printf ("После закрытия файла " LOGFILE "\n"); return (0); }

Листинг 5.5. Перенаправление стандартного вывода с помощью функции freopen().

Весьма полезной с практической точки зрения является функция создания и открытия временных файлов tmpfile() (см. пример 5.6).

#include <stdio.h> FILE *tmpfile (void);

Листинг 5.6. Описание функции tmpfile().

Временный файл открывается на изменение (w+) и автоматически удаляется после закрытия всех ссылок на него.

Использование функции tmpfile() предпочтительнее генерации «временного» имени с помощью функции tmpnam() и последующего создания файла с этим именем, поскольку в промежутке какой-либо другой процесс может создать одноименный файл.


Содержание раздела