Каковы причины для проверки на ошибку при закрытии ()?
Примечание: пожалуйста, прочитайте до конца, прежде чем пометить это как дубликат. Хотя это похоже, объем того, что я ищу в ответе, выходит за рамки того, о чем просил предыдущий вопрос.
Широко распространенная практика, с которой я склонен соглашаться, имеет тенденцию лечить close
чисто как функция освобождения ресурса для файловых дескрипторов, а не как потенциальная операция ввода-вывода с осмысленными случаями сбоя. И действительно, до решения проблемы 529 POSIX оставлял состояние файлового дескриптора (то есть, был ли он по-прежнему выделен или нет) неопределенным после ошибок, что делало невозможным переносимый ответ на ошибки любым значимым способом.
Тем не менее, много программного обеспечения GNU идет на все, чтобы проверить ошибки от close
и справочную страницу Linux дляclose
называет неудачу "распространенной, но, тем не менее, серьезной ошибкой программирования". NFS и квоты цитируются как обстоятельства, при которых close
может выдать ошибку, но не дает подробностей.
В каких ситуациях close
может произойти сбой в реальных системах, и актуальны ли они сегодня? Мне особенно интересно знать, существуют ли какие-либо современные системы, где close
дает сбой по любым причинам, не связанным с NFS, не зависящим от узла устройства, а также по отношению к сбоям NFS или устройств, при каких условиях (например, конфигурации) они могут быть замечены.
2 ответа
Однажды (24 марта 2007 г.) Эрик Сосман поделился следующей историей, чтобы поделиться в группе новостей comp.lang.c:
(Позвольте мне начать с признания в небольшой белой лжи: это была не fclose(), ошибка которой осталась незамеченной, а функция POSIX close(); эта часть приложения использовала ввод / вывод POSIX. Однако ложь безвредна, потому что средства C I/O потерпели бы сбои точно таким же образом, и необнаруженный сбой имел бы те же последствия. Я опишу то, что произошло в терминах ввода-вывода C, чтобы избежать слишком частого рассмотрения POSIX.)
Ситуация была очень похожа на то, что описал Ричард Тобин. Приложение представляло собой систему управления документами, которая загружала файл документа в память, вносила изменения пользователя в копию в памяти, а затем записывала все в новый файл, когда ему было приказано сохранить изменения. Он также поддерживал одноуровневую резервную копию "старой версии" в целях безопасности: операция сохранения записывалась во временный файл, а затем, если она прошла успешно, удаляла старую резервную копию, переименовывала старый файл документа в имя резервной копии и переименовывала временный файл к документу. bak -> trash, doc -> bak, tmp -> doc.
Шаг записи в временный файл проверил почти все. Очевидно, что fopen(), но также все fwrite() и даже финальный fflush() были проверены на наличие ошибок - но fclose() - нет. И в одной системе случилось так, что последние несколько дисковых блоков фактически не были распределены до fclose() - система ввода-вывода располагалась на более низком уровне механизма доступа к файлам VMS, и этой схеме была присуща небольшая асинхронность.,
В системе клиента были включены дисковые квоты, и жертва была близка к своему пределу. Он открыл документ, отредактировал его на некоторое время, сохранил свою работу и превысил свою квоту - которая осталась незамеченной, поскольку ошибка не появлялась до тех пор, пока не была отключена функция fclose(). Думая, что сохранение прошло успешно, приложение отменило старую резервную копию, переименовало исходный документ в резервную копию и переименовало усеченный временный файл в новый документ. Пользователь работал немного дольше и сохранял снова - то же самое, за исключением того, что вы заметите, что на этот раз единственный выживший полный файл был удален, а резервная копия и файл основного документа обрезаны. Результат: весь файл документа стал мусором, не только последний сеанс работы, но и все, что было раньше.
Как сказал бы Мерфи, жертва была начальником отдела, который приобрел несколько сотен лицензий на наше программное обеспечение, и я получил привилегию полететь в Сент-Луис, чтобы бросить его львам.
[...]
В этом случае сбой функции fclose() (в случае обнаружения) остановит последовательность удаления и переименования. Пользователю сказали бы: "Эй, при сохранении документа возникла проблема; сделайте что-нибудь с этим и попробуйте снова. Между тем на диске ничего не изменилось". Даже если бы он не смог спасти свою последнюю партию работы, он, по крайней мере, не потерял бы все, что было раньше.
Рассмотрим обратный вопрос: "В каких ситуациях мы можем гарантировать, что close
удастся?"Ответ:
- когда вы называете это правильно, и
- если вы знаете, что файловая система, в которой находится файл, не возвращает ошибок из
close
в этой версии ОС и ядра
Если вы уверены, что в вашей программе нет логических ошибок, и у вас есть полный контроль над ядром и файловой системой, вам не нужно проверять возвращаемое значение close
,
В противном случае вы должны спросить себя, насколько вы заботитесь о диагностике проблем с close
, Я думаю, что есть смысл в проверке и регистрации ошибок в диагностических целях:
- Если кодер делает логическую ошибку и передает неверный fd
close
, тогда вы сможете быстро отследить его. Это может помочь обнаружить ошибку раньше, чем она вызовет проблемы. - Если пользователь запускает программу в среде, где
close
возвращает ошибку, когда (например) данные не были сброшены, тогда вы сможете быстро определить причину повреждения данных. Это простой красный флаг, потому что вы знаете, что ошибка не должна возникать.