Scroll to navigation

close(2) System Calls Manual close(2)

ИМЯ

close - закрывает файловый дескриптор

LIBRARY

Standard C library (libc, -lc)

СИНТАКСИС

#include <unistd.h>
int close(int fd);

ОПИСАНИЕ

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

Если fd является последней копией какого-либо файлового дескриптора, ссылающегося на используемое описание открытого файла, (см. open(2)), то ресурсы, связанные с описанием открытого файла, освобождаются; если файловый дескриптор был последней ссылкой на файл, удалённый с помощью unlink(2), то файл окончательно удаляется.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

close() returns zero on success. On error, -1 is returned, and errno is set to indicate the error.

ОШИБКИ

Значение fd не является допустимым открытым файловым дескриптором.
Вызов close() был прерван по сигналу; см. signal(7).
Произошла ошибка ввода-вывода.
Для NFS об этих ошибках, обычно, не сообщается при первой записи, которая превысила доступное пространство памяти, а только в последующем write(), fsync(2) или close().

Смотрите в ЗАМЕЧАНИЯХ почему close() нельзя вызывать снова после ошибки.

СТАНДАРТЫ

POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.

ПРИМЕЧАНИЯ

Выполнение закрытия без ошибок не гарантирует, что данные были успешно записаны на диск, так как в ядре используется буферный кэш для отложенных записей. Как правило, файловые системы не записывают буферы при закрытии файла. Если требуется гарантировать физическую запись на используемый диск, то можно использовать fsync(2) (дальше всё будет зависеть от аппаратной составляющей диска).

Флаг файлового дескриптора закрытие-при-exec можно использовать для гарантии того, что файловый дескриптор автоматически закроется при успешном выполнении execve(2); подробности смотрите в fcntl(2).

Multithreaded processes and close()

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

Furthermore, consider the following scenario where two threads are performing operations on the same file descriptor:

(1)
One thread is blocked in an I/O system call on the file descriptor. For example, it is trying to write(2) to a pipe that is already full, or trying to read(2) from a stream socket which currently has no available data.
(2)
Another thread closes the file descriptor.

The behavior in this situation varies across systems. On some systems, when the file descriptor is closed, the blocking system call returns immediately with an error.

On Linux (and possibly some other systems), the behavior is different: the blocking I/O system call holds a reference to the underlying open file description, and this reference keeps the description open until the I/O system call completes. (See open(2) for a discussion of open file descriptions.) Thus, the blocking system call in the first thread may successfully complete after the close() in the second thread.

Обработка ошибки, возвращённой close()

Аккуратный программист всегда проверяет возвращаемое close() значение, так как очень вероятно, что об ошибках предыдущей операции write(2) будет сообщено только при последнем вызове close(), который освобождает описание открытого файла. Невыполнение проверки возвращаемого значение при закрытии файла может привести к неучтённой потере данных. Это особенно часто встречается с NFS и дисковыми квотами.

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

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

Многие другие реализации поступают схожим образом и всегда закрывают файловый дескриптор (за исключением случая EBADF, означающего некорректный файловый дескриптор) даже, если они затем возвратят ошибку close(). В POSIX.1 ничего не сказано на этот счёт, но есть планы узаконить такое поведение в следующем выпуске стандарта.

Аккуратный программист, который хочет узнать об ошибках ввода-вывода, перед close() вызовет fsync(2).

Ошибка EINTR является особым случаем. Про EINTR в POSIX.1-2008 сказано:

Если close() прерван сигналом, который будет обработан, то вызов должен вернуть -1, а errno присвоить значение EINTR; при этом состояние fildes не определено.

Этим допускается режим работы, действующий в Linux и многих других реализациях, где как и при других ошибках, которые может вернуть close(), файловый дескриптор гарантированно закрывается. Однако, это также допускает и другой режим: реализация возвращает ошибку EINTR и оставляет файловый дескриптор открытым (согласно документации, так делает close() в HP-UX). Вызывающий должен позднее ещё раз вызвать close() для закрытия файлового дескриптора, чтобы не допустить утечек файловых дескрипторов. Такое расхождение в поведении реализаций является сложным препятствием для переносимых приложений, так как во многих реализациях close() не должен вызываться ещё раз после получения ошибки EINTR и, по крайней мере, в одной close() нужно вызвать снова. Есть планы разгадать эту загадку в следующей большой версии стандарта POSIX.1.

СМОТРИТЕ ТАКЖЕ

close_range(2), fcntl(2), fsync(2), open(2), shutdown(2), unlink(2), fclose(3)

ПЕРЕВОД

Русский перевод этой страницы руководства разработал Azamat Hackimov <azamat.hackimov@gmail.com>, Dmitriy S. Seregin <dseregin@59.ru>, Dmitry Bolkhovskikh <d20052005@yandex.ru>, Katrin Kutepova <blackkatelv@gmail.com>, Yuri Kozlov <yuray@komyakino.ru> и Иван Павлов <pavia00@gmail.com>

Этот перевод является свободной программной документацией; он распространяется на условиях общедоступной лицензии GNU (GNU General Public License - GPL, https://www.gnu.org/licenses/gpl-3.0.html версии 3 или более поздней) в отношении авторского права, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ.

Если вы обнаружите какие-либо ошибки в переводе этой страницы руководства, пожалуйста, сообщите об этом разработчику по его адресу электронной почты или по адресу списка рассылки русских переводчиков.

30 октября 2022 г. Linux man-pages 6.03