7

所以我一直在研究 fclose 手册页,我的结论是,如果 fclose 被某些信号打断,根据手册页,没有办法恢复......?我错过了什么吗?

通常,对于无缓冲的 POSIX 函数(打开、关闭、写入等),总是有一种方法可以通过重新启动调用从信号中断 (EINTR) 中恢复;相反,缓冲调用的文档指出,在 fclose 尝试失败后,另一次尝试具有未定义的行为......没有提示如何恢复。如果信号中断 fclose,我只是“不走运”吗?数据可能会丢失,我无法确定文件描述符是否实际关闭。我知道缓冲区已被释放,但文件描述符呢?考虑一下同时使用大量 fd 的大型应用程序,如果 fd 没有正确释放会遇到问题 -> 我认为必须有一个 CLEAN 解决方案来解决这个问题。

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

当然,我可以检查fclose 的一个具体实现,但我不敢相信有人设计了 stdio 却没有考虑到这个问题?仅仅是文档不好还是这个功能的设计?

这个角落的案例真的让我很烦:(

4

2 回答 2

5

EINTRclose()

事实上,也有问题close(),不仅仅是fclose()

POSIX 声明close()返回EINTR,这通常意味着应用程序可以重试调用。但是在linux中事情要复杂得多。请参阅有关 LWN的这篇文章和这篇文章

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

这篇博客文章这个答案close()解释了为什么重试失败不是一个好主意EINTR。因此,在 Linux 中,如果使用(or )close()失败,您将无法做任何有意义的事情。EINTREINPROGRESS

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


EINTRfclose()

POSIX 状态为fclose()

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

无论调用是否成功,流都应与文件解除关联,并且由setbuf()orsetvbuf()函数设置的任何缓冲区都应与流解除关联。如果关联的缓冲区是自动分配的,则应将其释放。

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


可靠的错误处理

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

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

  • 之后不要重试EINTR

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

  • fsync()如果您需要,请致电。

    • 如果您关心数据完整性,您应该调用fsync()orfdatasync()之前调用fclose() 1
    • 如果你不这样做,就忽略EINTRfrom fclose()

笔记

  • 如果fflush()fsync()成功 和fclose()失败EINTR,不会 丢失 数据 也不会发生 泄漏.

  • 您还应该确保该FILE对象未在另一个线程2fflush()和调用之间使用。fclose()


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

[2] 您可以在调用flockfile()之前先调用fflush()fclose()。它应该可以fclose()正常工作。

于 2015-07-28T08:44:23.487 回答
2

考虑一下同时使用大量 fd 的大型应用程序,如果 fd 没有正确释放会遇到问题 -> 我认为必须有一个 CLEAN 解决方案来解决这个问题。

评论中已经提到了重试fflush()然后close()在底层文件描述符上的可能性。对于大型应用程序,我更倾向于使用线程并拥有一个专用信号处理线程的模式,而所有其他线程都使用pthread_sigmask(). 然后,当fclose()失败时,你就会遇到真正的问题。

于 2015-07-21T06:18:41.210 回答