检查close()中的错误的原因是什么?

注意:在将其标记为重复之前,请阅读到最后。 虽然它是相似的,我在寻找答案的范围超出了前面的问题所要求的范围。

我倾向于认同的广泛的实践往往将纯粹作为文件描述符的资源释放函数,而不是有意义的失败情况下的潜在IO操作。 事实上,在解决问题529之前 ,POSIX留下了文件描述符的状态(即它是否仍被分配),在错误之后没有指定,使得不可能以任何有意义的方式对错误作出可移植的响应。

然而,很多GNU软件都花了很大的功夫来检查close错误,而Linux手册页的close调用失败了,这是“一个普通但严重的编程错误”。 NFS和配额被引用为close可能会产生错误但不提供细节的情况。

在现实世界的系统中close可能会失败的情况是什么? 我特别感兴趣的是,是否有任何现代系统,在任何非NFS,非设备节点特定的原因close失败,以及NFS或设备相关的故障,在什么条件下(例如configuration),他们可能可见。

曾几何时(2007年3月24日), Eric Sosmancomp.lang.c新闻组中分享了以下故事:

(让我开始承认一个小小的谎言:这不是fclose()的失败,但是POSIX的close()函数;这部分应用程序使用了POSIX I / O,谎言是无害的,因为CI / O设施将会以完全相同的方式失败,而一个未被发现的失败也会产生相同的后果。我将描述发生在C的I / O方面的事情,以避免过多地停留在POSIX上。

理查德·托宾所描述的情况非常严重。 该应用程序是一个文档管理系统,将文档文件加载到内存中,将用户的编辑应用到内存中,然后在被告知保存编辑时将所有内容写入新文件。 为了安全起见,它还维护了一个“旧版本”备份:保存操作写入临时文件,如果成功则删除旧备份,将旧文档文件重命名为备份名称,并重命名为临时文件到文件。 bak – > trash,doc – > bak,tmp – > doc。

写入到临时文件的步骤几乎检查了一切。 显然,fopen(),也包括所有的fwrite(),甚至最终的fflush()都被检查了错误指示 – 但是fclose()不是。 在一个系统上,碰巧最后几个磁盘块在fclose()之前并没有真正分配 – I / O系统位于VMS的底层文件存取机器的上面,并且在这个安排中有一点不同步。

客户的系统启用了磁盘配额,受害者正好接近他的限制。 他打开了一个文档,编辑了一段时间,到目前为止保存了他的工作,超过了他的配额 – 这是没有被发现的,因为直到没有检查的fclose()才出现错误。 考虑到保存成功,应用程序丢弃旧备份,将原始文档重命名为备份,并将截断的临时文件重命名为新文档。 用户工作了一段时间,再次保存 – 同样的事情,除了你会注意到,这次唯一幸存的完整文件被删除,备份和主文档文件被截断。 结果:整个文档文件变成了垃圾,不仅仅是最新的工作,而是之前的一切。

就像墨菲所说的那样,受害者是那个为我们的软件购买了数百个许可证的部门的老板,我有机会把飞往圣路易斯的权利扔给狮子。

[…]

在这种情况下,fclose()的失败会(如果检测到)停止了删除和重命名序列。 用户可能会被告知“嘿,保存文档时遇到了问题,请做一些尝试,然后再试一次,同时磁盘上没有任何变化。 即使他无法挽救他最新的一批工作,他至少也不会失去以前的一切。

考虑一下你的问题的反面:“在什么情况下我们可以保证close会成功? 答案是:

  • 当你打电话正确,和
  • 当您知道该文件所在的文件系统在此操作系统和内核版本中不会从close返回错误

如果你确信你的程序没有任何逻辑错误,并且你完全控制了内核和文件系统,那么你不需要检查close的返回值。

否则,你必须问自己多少你关心诊断问题。 我认为检查和记录错误以进行诊断是有价值的:

  • 如果编码器发生逻辑错误,并传递一个无效的fd close ,那么您将能够快速追踪它。 这可能有助于在出现问题之前及早发现错误。
  • 如果用户在close (例如)数据未刷新时返回错误的环境中运行该程序,则可以快速诊断数据被破坏的原因。 这是一个容易的红旗,因为你知道错误不应该发生。