Работа с содержимым файла происходит через целочисленный файловый дескриптор, который представляет из себя номер строки в таблице ссылок на открытые файлы процесса.
При открытии файла в вызове ядра open() проверяются соответствие флагов и прав доступа к файлу.
//почти псевдокод
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int fd;
int flags;
mode_t mode;
// открытие (+создание) файла
fd=open("pathname", flags, mode);
//или
fd=creat("pathname", mode);
// при открытии существующего файла можно опустить параметр mode
fd=open("pathname", flags);
mode - права доступа к файлу, назначаемые в момент его создания. Чтобы нельзя было случайно создать файл со слишком свободным доступом, при создании файла производится побитовое умножение mode на битовую маску umask (mode & ~umask). mode и _umask__ удобно задавать в восьмеричном виде считая, что классические права доступа rwx соответствуют одной восьмеричной цифре. Например, права доступа rwxr-xr-- запишутся в восьмеричном виде как 0754. Типичная маска выглядит так ---w--w- или 022в восьмеричной записи. Такая маска отбирает права на запись у группы и остальных.
Системный вызов umask(mask) устанавливает новую маску и возвращает старую.
#include <sys/types.h>
#include <sys/stat.h>
mode_t old_mask=umask(new_mask);
Для удобства записи прав доступа существуют мнемонические макросы:
S_IRWXU 00700 User Read, Write,eXecute; S_IRUSR 00400 User Read; S_IWUSR 00200 User Write и т.д. S_IXUSR, S_IRWXG, S_IRGRP, S_IWGRP, S_IXGRP, S_IRWXO, S_IROTH, S_IWOTH, S_IXOTH.
flags - флаги уточняющие режим открытия файла. Флаги делятся на несколько групп:
При ошибке открытия файла возвращается -1 и в переменную errno заносится код ошибки. Возможные значения ошибки (не все):
#include <unistd.h>
// чтение/запись определенного числа байт
int fd
char buf[SIZE];
size_t count=SIZE;
ssize_t res;
res=read (fd, buf, count);
res=write(fd, buf, count);
Чтение и запись возвращают количество прочитанных/записанных байтов или -1. -1 не всегда означает ошибку.
Возможные варианты ответа при записи:
Возможные варианты ответа при чтении:
Ошибки чтения/записи:
Чтение/запись из/в фрагментированной памяти
struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
} iov[SIZE];
res=readv (fd, iov, SIZE);
res=writev(fd, iov, SIZE);
Чтение/запись в определенную позицию. offset - смещение в байтах относительно начала файла.
res=pread (fd, buf, count, offset);
res=pwrite(fd, buf, count, offset);
res=preadv (fd, iov, int iovcnt, offset);
res=pwritev(fd, iov, int iovcnt, offset);
// закрытие файла
int retval=close(fd);
Ошибки при закрытии файла встречаются редко, но встречаются.
Для установки позиции/чтения записи в файле используются два параметра offset - смещение в байтах и whence - место от которого отсчитывается смещение. Возможные значения whence:
Пример:
#include <sys/types.h>
#include <unistd.h>
// установка позиции чтения/записи
off_t offset=100;
int whence=SEEK_END;
off_t pos=lseek(fd, offset, whence);
Возвращается установленная позиция или -1 в случае ошибки.
Сочетание offset=0 и whence=SEEK_CUR позволяет узнать текущую позицию чтения/записи.
С помощью lseek возможно перемещение указателя записи за конец файла. Многие ФС в такой ситуации не выделяют блоки хранения под пропущенные байты и создают "дырявые" файлы, занимающие на диске пространство меньше своей длины.
int fd=open("/tmp/sparse-file", O_WRONLY|O_CREAT|O_TRUNC, 0700);
off_t pos=lseek(fd, 1000000000, SEEK_SET);
int res=write(fd,"c",1);
В данном примере создаётся файл длиной примерно 1 ГБ, занимающий на диске один блок данных (например 512 Б).
В 32-х разрядных системах могут быть проблемы с большими файлами. В этом случае надо использовать вызов lseek64 и некоторые дополнительные трюки.
Возможно создание ссылки на файловый дескриптор.
#include <unistd.h>
int fd1=dup(oldfd);
int fd2=dup2(oldfd, newfd);
dup() - выбирает в таблице открытых файлов первую свободную строку и записывает ссылку на oldfd в неё, dup2() - закрывает файл, связанный с дескриптором newfd (если он был открыт) и записывает ссылку oldfd в newfd. В случае успеха возвращается файловый дескриптор, в случае ошибки -1.
В связи с тем, что в таблицу открытых файлов вписывается именно ссылка, у файловых дескрипторов oldfd и newfd всегда будет одна и та же позиция головки чтения/записи.
Типичное применение dup2() - это подмена стандартных дескрипторов 0,1,2 (stdin,stdout,stderr). oldfd в этом случае закрывается после создания ссылки. dup2() предпочтительнее чем dup(), т.к. выполняется атомарно, что может быть важно в многопоточной среде.
int newfd=open("file",O_RDONLY);
dup2(newfd,0);
close(newfd);
вариант с dup()
int newfd=open("file",O_RDONLY);
close(0);
dup(newfd);
close(newfd);
Флаги | файл существует | файл не существует |
---|---|---|
Без флагов | Нет ошибки | ENOENT |
O_CREAT | Нет ошибки | Нет ошибки |
O_CREAT+O_EXCL | EEXIST | Нет ошибки |