Вскоре мы увидим пример кода. Дополнительные значения flags
Системный вызов close()
#include
int close(int fd);
В случае успеха возвращается 0, при ошибке (-1). При возникновении ошибки нельзя ничего сделать, кроме сообщения о ней. Ошибки при закрытии файлов являются необычными, но не невозможными, особенно для файлов, доступ к которым осуществляется через сеть. Поэтому хорошей практикой является проверка возвращаемого значения, особенно для файлов, открытых для записи.
Если вы будете игнорировать возвращаемое значение, специально приведите его к типу void
(void)close(fd); /* отказ от возвращаемого значения */
Легкомысленность этого совета в том, что слишком большое количество приведений к void
printf() или приводящий его к void. Как и со многими аспектами программирования на С, здесь также требуются опыт и рассудительность.Как упоминалось, число открытых файлов, если оно большое, ограничивается, и вам всегда следует закрывать файлы, когда работа с ними закончена. Если вы этого не сделаете, то в конечном счете выйдете за пределы лимита дескрипторов файлов, создав ситуацию, которая ведет к потере устойчивости части вашей программы.
Система закрывает все открытые файлы, когда процесс завершается, но — за исключением 0, 1 и 2 — плохая манера полагаться на это.
Когда open()
open() вернет 5, а не 7. Это поведение важно; далее в книге мы увидим, как оно используется для аккуратной реализации многих важных особенностей Unix, таких, как перенаправление ввода/вывода и конвейеризация (piping)4.4.2.1. Отображение переменных FILE*
Стандартные библиотечные функции ввода/вывода и переменные FILE*
, такие, как stdin, stdout и stderr, построены поверх основанных на дескрипторах файлов системных вызовах.Иногда полезно получить непосредственный доступ к дескриптору файла, связанному с указателем файла
fileno() возвращает лежащий в основе дескриптор файла:#include
int fileno(FILE *stream);
Пример мы увидим позже, в разделе 4.4.4. «Пример: Unix cat».
4.4.2.2. Закрытие всех открытых файлов
Открытые файлы наследуются порожденными процессами от своих родительских процессов. Фактически они являются
Поскольку программы могут наследовать другие файлы, иногда вы можете увидеть программы, которые закрывают все свои файлы, чтобы начать с «чистого состояния» В частности, типичен код наподобие этого:
int i;
/* оставить лишь 0, 1, и 2 */
for (i = 3; i < getdtablesize(); i++)
(void)close(i);
Предположим, что результат getdtablesize()
1020 из них не нужны, поскольку возвращаемое значение getdtablesize() не изменяется. Вот лучший вариант этого кода:int i, fds;
for (i = 3, fds = getdtablesize(); i < fds; i++)
(void)close(i);
Такая оптимизация не ухудшает читаемость кода, но может быть заметна разница, особенно на медленных системах. В общем, стоит поискать случаи, когда в циклах повторно вычисляется один и тот же результат, чтобы посмотреть, нельзя ли вынести вычисление за пределы цикла. Хотя в таких случаях нужно убедиться, что вы (а) сохраняете правильность кода и (б) сохраняете его читаемость!
4.4.3. Чтение и запись
Ввод/вывод осуществляется системными вызовами read()
write() соответственно:#include
#include
#include
#include