Системный вызов close() закрывает открытый дескриптор файла, высвобождая его для последующего повторного использования процессом. Когда процесс прекращает работу, все его открытые дескрипторы файлов автоматически закрываются.
#include <unistd.h>
int close(int fd);
Возвращает 0 при успешном завершении или –1 при ошибке
Обычно предпочтительнее явно закрывать ненужные дескрипторы файлов. Тогда код, с учетом последующих изменений, будет проще для чтения и надежнее. Более того, дескрипторы файлов являются расходуемым ресурсом, поэтому сбой при закрытии дескриптора файла может вылиться в исчерпание процессом ограничения дескрипторов. Это, в частности, играет важную роль при написании программ, рассчитанных на долговременную работу и обращающихся к большому количеству файлов, например при создании оболочек или сетевых серверов.
Как и любые другие системные вызовы, close() должен сопровождаться проверкой кода на ошибки:
if (close(fd) == -1)
errExit("close");
Такой код отлавливает ошибки вроде попыток закрытия неоткрытого дескриптора файла или закрытия одного и того же дескриптора файла дважды. Он также отлавливает сбойные ситуации, диагностируемые конкретной файловой системой в ходе операции закрытия.
Сетевая файловая система — NFS (Network File System) — предоставляет пример такой специфичной для нее ошибки. Когда в NFS происходит сбой завершения транзакции, означающий, что данные не достигли удаленного диска, эта ошибка доходит до приложения в виде сбоя системного вызова close().
Для каждого открытого файла в ядре записывается файловое смещение, которое иногда также называют смещением чтения-записи или указателем. Оно обозначает место в файле, откуда будет стартовать работа следующего системного вызова read() или write(). Файловое смещение выражается в виде обычной байтовой позиции относительно начала файла. Первый байт файла расположен со смещением 0.
При открытии файла смещение устанавливается на его начало, а затем автоматически корректируется каждым последующим вызовом read() или write(), чтобы указывать на следующий байт файла непосредственно после считанного или записанного байта (или байтов). Таким образом, успешно проведенные вызовы read() и write() идут по файлу последовательно.
Системный вызов lseek() устанавливает файловое смещение открытого файла, на который указывает дескриптор fd, в соответствии со значениями, заданными в аргументах offset и whence.
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
Возвращает новое файловое смещение при успешном завершении или –1 при ошибке
Аргумент offset определяет значение смещения в байтах. (Тип данных off_t — целочисленный тип со знаком, определенный в SUSv3.) Аргшумент whence указывает на отправную точку, от которой отсчитывается смещение, и может иметь следующие значения:
• SEEK_SET — файловое смещение устанавливается в байтах на расстоянии offset от начала файла;
• SEEK_CUR — смещение устанавливается в байтах на расстоянии offset относительно текущего файлового смещения;
• SEEK_END — файловое смещение устанавливается на размер файла плюс offset. Иными словами, offset рассчитывается относительно следующего байта после последнего байта файла.
Порядок интерпретации аргумента whence показан на рис. 4.1.
В ранних реализациях UNIX вместо констант SEEK_*, перечисленных выше, использовались целые числа 0, 1 и 2. В старых версиях BSD для этих значений применялись другие имена: L_SET, L_INCR и L_XTND.
Рис. 4.1. Интерпретация аргумента whence системного вызова lseek()
Если аргумент whence содержит значение SEEK_CUR или SEEK_END, то у аргумента offset может быть положительное или отрицательное значение. Для SEEK_SET значение offset должно быть неотрицательным.
При успешном выполнении lseek() возвращается значение нового файлового смещения. Следующий вызов извлекает текущее расположение файлового смещения, не изменяя его значения:
curr = lseek(fd, 0, SEEK_CUR);
В некоторых реализациях UNIX (но не в Linux) имеется нестандартная функция tell(fd), которая служит той же цели, что и описанный системный вызов lseek().