Системные вызовы open, creat, close, unlink
В отличие от стандартных файлов ввода, вывода и ошибок, которые открыты по умолчанию, остальные файлы нужно открывать явно. Для этого есть два системных вызова: open и creat.
Функция open почти совпадает с fopen, рассмотренной в главе 7. Разница между ними в том, что первая возвращает не файловый указатель, а дескриптор файла типа int. При любой ошибке open возвращает -1.
include <fcntl.h>
int fd; int open(char *name, int flags, int perms);
fd = open(name, flags, perms);
Как и в fopen, аргумент name - это строка, содержащая имя файла. Второй аргумент, flags, имеет тип int и специфицирует, каким образом должен быть открыт файл. Его основными значениями являются:
O_RDONLY - открыть только на чтение; O_WRONLY - открыть только на запись; O_RDWR - открыть и на чтение, и на запись.
В System V UNIX эти константы определены в <fcntl.h>, а в версиях Berkley (BSD) - в <sys/file.h>.
Чтобы открыть существующий файл на чтение, можно написать
fd = open(name, 0_RDONLY, 0);
Далее везде, где мы пользуемся функцией open, ее аргумент perms равен нулю.
Попытка открыть несуществующий файл является ошибкой. Создание нового файла или перезапись старого обеспечивается системным вызовом creat. Например
int creat(char *name, int perms); fd = creat(name, perms);
Функция creat возвращает дескриптор файла, если файл создан, и -1, если по каким-либо причинам файл создать не удалось. Если файл уже существует, creat "обрежет" его до нулевой длины, что равносильно выбрасыванию предыдущего содержимого данного файла; создание уже существующего файла не является ошибкой.
Если строится действительно новый файл, то creat его создаст с правами доступа, специфицированными в аргументе perms. В системе UNIX с каждым файлом ассоциированы девять битов, содержащие информацию о правах пользоваться этим файлом для чтения, записи и исполнения лицам трех категорий: собственнику файла, определенной им группе лиц и всем остальным. Таким образом, права доступа удобно специфицировать с помощью трех восьмеричных цифр. Например, 0755 специфицирует чтение, запись и право исполнения собственнику файла, а также чтение и право исполнения группе и всем остальным.
Для иллюстрации приведем упрощенную версию программы cp системы UNIX, которая копирует один файл в другой. В нашей версии копируется только один файл, не позволяется во втором аргументе указывать директорий (каталог), и права доступа не копируются, а задаются константой.
#include <stdio.h> #include <fcntl.h> #include "syscalls.h" #define PERMS 0666 /* RW для собственника, группы и остальных */
void error(char *, ...);
/* cp: копирование f1 в f2 */ main(int argc, char *argv[]) { int f1, f2, n; char buf[BUFSIZ];
if (argc ! = 3) error("Обращение: cp откуда куда"); if ((f1 = open(argv[1], O_RDONLY, 0)) == -1) error("cp: не могу открыть файл %s", argv[1]); if ((f2 = creat(argv[2], PERMS)) == -1) error("cp: не могу создать файл %s, режим %03o", argv[2], PERMS); while ((n = read(f1, buf, BUFSIZ)) > 0) if (write(f2, buf, n) != n) error ("cp: ошибка при записи в файл %s", argv[2]); return 0; }
Данная программа создаст файл вывода с фиксированными правами доступа, определяемыми кодом 0666. С помощью системного вызова stat, который будет описан в параграфе 8.6, мы можем определить режим использования существующего файла и задать тот же режим для копии.
Заметим, что функция error, вызываемая с различным числом аргументов, во многом похожа на printf. Реализация error иллюстрирует, как пользоваться другими программами семейства printf. Библиотечная функция vprintf аналогична printf, с той лишь оговоркой, что переменная часть списка аргументов заменена в ней одним аргументом, который инициализируется макросом va_start. Подобным же образом соотносятся функции vfprinf с fprintf и vsprintf с sprintf.
#include <stdio.h> #include <stdarg.h>
/* error: печатает сообщение об ошибке и умирает */ void error(char *fmt, ...) { va_list args;
va_start(args, fmt); fprintf(stderr, "ошибка: "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); exit(1); }
На количество одновременно открытых в программе файлов имеется ограничение (обычно их число колеблется около 20). Поэтому любая программа, которая намеревается работать с большим количеством файлов, должна быть готова повторно использовать их дескрипторы. Функция close(int fd) разрывает связь между файловым дескриптором и открытым файлом и освобождает дескриптор для его применения с другим файлом. Она аналогична библиотечной функции fclose с тем лишь различием, что никакой очистки буфера не делает. Завершение программы с помощью exit или return в главной программе закрывает все открытые файлы.
Функция unlink(char *name) удаляет имя файла из файловой системы. Она соответствует функции remove стандартной библиотеки.
Упражнение 8.1. Перепишите программу cat из главы 7, используя функции read, write, open и close. Замените ими соответствующие функции стандартной библиотеки. Поэкспериментируйте, чтобы сравнить быстродействие двух версий.