Правильная обработка ошибок для fclose невозможна (согласно man-странице)?
Так что я изучаю man-страницу fclose довольно долго, и я пришел к выводу, что если fclose прерывается каким-либо сигналом, то, согласно man-странице, нет никакого способа восстановить...? Я что-то упустил?
Обычно с небуферизованными функциями POSIX (открытие, закрытие, запись и т. Д.) ВСЕГДА существует способ восстановления после прерывания сигнала (EINTR) путем перезапуска вызова; В отличие от этого, документация буферизованных вызовов гласит, что после неудачной попытки fclose другая попытка имеет неопределенное поведение... нет никаких намеков на то, КАК вместо этого восстановить. Мне просто "не повезло", если сигнал прерывает fclose? Данные могут быть потеряны, и я не могу быть уверен, что дескриптор файла действительно закрыт или нет. Я знаю, что буфер освобожден, но как насчет файлового дескриптора? Подумайте о крупномасштабных приложениях, которые используют множество файлов fd одновременно и столкнутся с проблемами, если файлы fd не будут должным образом освобождены.
Итак, давайте предположим, что я пишу библиотеку, и ей не разрешено использовать sigaction и SA_RESTART, и отправляется множество сигналов. Как мне восстановить систему, если fclose прервана? Было бы хорошей идеей вызывать close в цикле (вместо fclose) после сбоя fclose с EINTR? В документации fclose просто не упоминается состояние дескриптора файла; UNDEFINED не очень полезен, хотя... если fd закрыт, и я снова вызываю close, могут возникнуть странные, трудно отлаживаемые побочные эффекты, поэтому, естественно, я предпочел бы проигнорировать этот случай, как если бы поступил неправильно... тогда опять не существует неограниченного числа файловых дескрипторов, а утечка ресурсов является своего рода ошибкой (по крайней мере, для меня).
Конечно, я мог бы проверить одну конкретную реализацию fclose, но я не могу поверить, что кто-то разработал stdio и не думал об этой проблеме? Это плохая документация или дизайн этой функции?
Этот угловой случай действительно беспокоит меня:(
2 ответа
EINTR
а также close()
На самом деле, есть также проблемы с close()
не только с fclose()
,
POSIX утверждает, что close()
возвращается EINTR
, что обычно означает, что приложение может повторить вызов. Однако в Linux все сложнее. Смотрите эту статью на LWN, а также этот пост.
[...]
POSIXEINTR
семантика на самом деле невозможна в Linux. Файловый дескриптор переданclose()
отменен в начале обработки системного вызова, и к тому времени этот же дескриптор уже мог быть передан другому потокуclose()
возвращается.
Этот пост в блоге и этот ответ объясняют, почему не стоит повторять попытку close()
не удалось с EINTR
, Так что в Linux вы ничего не можете сделать, если close()
не удалось с EINTR
(или же EINPROGRESS
).
Также обратите внимание, что close()
является асинхронным в Linux. Например, иногда umount
может вернуться EBUSY
сразу после закрытия последнего открытого дескриптора в файловой системе, поскольку он еще не выпущен в ядре. Смотрите интересное обсуждение здесь: страница 1, страница 2.
EINTR
а также fclose()
POSIX состояния для fclose()
:
После звонка
fclose()
любое использование потока приводит к неопределенному поведению.Независимо от того, успешен ли вызов, поток должен быть отсоединен от файла и любого буфера, установленного
setbuf()
или жеsetvbuf()
функция должна быть отделена от потока. Если связанный буфер был автоматически выделен, он должен быть освобожден.
Я считаю, что это означает, что даже если close()
не удалось, fclose()
должен освободить все ресурсы и не производить утечек. Это верно по крайней мере для реализаций glibc и uclibc.
Надежная обработка ошибок
Вызов
fflush()
доfclose()
,Поскольку вы не можете определить, если
fclose()
не удалось, когда он позвонилfflush()
или жеclose()
, вы должны явно позвонитьfflush()
доfclose()
чтобы убедиться, что буфер пространства пользователя был успешно отправлен ядру.Не повторять после
EINTR
,Если
fclose()
не удалось сEINTR
, вы не можете повторитьclose()
а также не могу повторитьfclose()
,Вызов
fsync()
если тебе надо.- Если вы заботитесь о целостности данных, вы должны позвонить
fsync()
или жеfdatasync()
перед звонкомfclose()
1 - Если нет, просто игнорируйте
EINTR
отfclose()
,
- Если вы заботитесь о целостности данных, вы должны позвонить
Заметки
Если
fflush()
а такжеfsync()
удалось иfclose()
не удалось сEINTR
, данные не теряются и не происходит утечки.Вы также должны убедиться, что
FILE
объект не используется междуfflush()
а такжеfclose()
звонки из другого потока 2.
[1] См. Статью "Все, что вы всегда хотели знать о Fsync()", в которой объясняется, почему fsync()
также может быть асинхронной операцией.
[2] Вы можете позвонить flockfile()
перед звонком fflush()
а также fclose()
, Должно работать с fclose()
правильно.
Подумайте о крупномасштабных приложениях, которые используют множество файлов fd одновременно и столкнутся с проблемами, если файлы fd не будут должным образом освобождены -> я бы предположил, что должно быть ЧИСТОЕ решение этой проблемы.
Возможность повторить fflush()
а потом close()
О базовом файловом дескрипторе уже упоминалось в комментариях. Для крупномасштабного приложения я бы предпочел, чтобы шаблон использовал потоки и имел один выделенный поток обработки сигналов, в то время как все другие потоки блокировали сигналы, используя pthread_sigmask()
, Тогда, когда fclose()
не удается, у вас есть реальная проблема.