对fclose进行适当的错误处理是不可能的(根据联机帮助页)?

boo*_*hoo 7 c linux stdio eintr fclose

所以我正在研究fclose manpage,我的结论是,如果fclose被某些信号中断,根据联机帮助页有没有办法恢复......?我错过了一些观点吗?

通常,使用无缓冲的POSIX功能(打开,关闭,写入等),总有一种方法可以通过重新启动呼叫来从信号中断(EINTR)中恢复; 相反,缓冲调用的文档说明在fclose尝试失败后,另一个尝试有未定义的行为...没有关于如何恢复的提示.如果信号中断fclose,我只是"不幸"吗?数据可能会丢失,我无法确定文件描述符是否实际关闭.我知道缓冲区已被释放,但文件描述符呢?想想大规模的应用程序同时使用很多fd的,并且如果fd没有被正确释放会遇到问题 - >我会假设必须有一个CLEAN解决方案来解决这个问题.

所以我们假设我正在编写一个库,并且不允许使用sigaction和SA_RESTART并且发送了大量信号,如果fclose被中断,我该如何恢复?fclose与EINTR失败后,在循环(而不是fclose)中调用close是不是一个好主意?fclose的文档根本没有提到文件描述符的状态; 但是,UNDEFINED并不是很有帮助...如果fd关闭并且我再次调用close,可能会发生奇怪的难以调试的副作用,所以我宁愿忽略这种情况,因为做了错误的事情......然后再次,那里没有无限数量的文件描述符可用,资源泄漏是某种错误(至少对我而言).

当然我可以检查fclose的一个具体实现,但我不相信有人设计了stdio并且没有想到这个问题?它只是文档是坏的还是这个功能的设计?

这个角落案件真的让我感到困惑:(

gav*_*avv 5

EINTRclose()

事实上,close()不仅与fclose().

POSIX 声明close()返回EINTR,这通常意味着应用程序可以重试调用。然而,事情在 linux 中更为复杂。请参阅有关 LWN 的这篇文章以及这篇文章

[...]POSIXEINTR语义在 Linux 上是不可能的。传递给的文件描述符close()在系统调用处理的早期被取消分配,并且相同的描述符可能在时间close()返回时已经分发给另一个线程。

这篇博文这个答案解释了为什么close()使用EINTR. 因此,在 Linux 中,如果close()使用EINTR(或EINPROGRESS)失败,您将无法做任何有意义的事情。

另请注意,这close()在 Linux中是异步的。例如,有时umount可能会EBUSY在关闭文件系统上最后打开的描述符后立即返回,因为它尚未在内核中发布。在此处查看有趣的讨论:第 1第 2 页


EINTRfclose()

POSIX 声明为fclose()

在调用 之后fclose(),对流的任何使用都会导致未定义的行为。

无论调用是否成功,流都应与文件解除关联,setbuf()setvbuf()函数设置的任何缓冲区都应与流解除关联。如果关联的缓冲区是自动分配的,则应取消分配。

我相信这意味着即使close()失败,也fclose() 应该释放所有资源并且不会产生泄漏。至少对于glibcuclibc实现是这样。


可靠的错误处理

  • fflush()之前打电话fclose()

    由于您无法确定fclose()在调用fflush()or时是否失败,因此close()您必须在fflush()之前显式调用fclose()以确保用户空间缓冲区已成功发送到内核。

  • 之后不要重试EINTR

    如果fclose()失败EINTR,则不能重试close(),也不能重试fclose()

  • fsync()有需要就打电话。

    • 如果您关心数据完整性,则应该在调用1之前调用fsync()或。fdatasync()fclose()
    • 如果不这样做,请忽略EINTRfrom fclose()

笔记

  • 如果fflush()fsync()成功,fclose()有失败EINTR没有数据丢失出现无泄漏

  • 你也应该确保FILE对象不之间使用fflush()fclose()调用从另一个线程2


[1] 请参阅“关于 Fsync() 的一切你一直想知道的”文章,其中解释了为什么fsync()也可能是异步操作。

[2] 可以flockfile()先调用fflush()fclose()。它应该fclose()正确工作。