一尘不染

不检查close()的返回值:真的有多严重?

linux

Linux的“手动关闭”警告(SVr4、4.3BSD,POSIX.1-2001):

不检查close()的返回值是 常见严重的
编程错误。很有可能首先在最后的close()中报告了先前write(2)操作的错误。关闭文件时不检查返回值可能会导致数据静默丢失。使用NFS和磁盘配额尤其可以观察到这一点。

我可以相信这个错误是 常见的 (至少在应用程序中;我不是内核黑客)。但是,今天或过去三十年中的任何时候,它有多 严重 ?特别是:

是否有一个简单,可复制的示例,说明这种无声的数据丢失?即使是人为的人也喜欢在close()期间发送SIGKILL?

如果存在这样的例子,是否可以比仅仅更优雅地处理数据丢失

printf("Sorry, dude, you lost some data.\n");


阅读 766

收藏
2020-06-03

共1个答案

一尘不染

[现在]是严重的还是在过去的三十年中的任何时候?

典型应用程序 处理
数据。他们消耗一些输入,并产生结果。因此,在两种情况下close()可能会返回错误:关闭输入(只读?)文件时,以及关闭刚刚生成或修改的文件时。

close()返回错误的已知情况特定于将数据写入/刷新到永久存储中。特别是,它是常见的操作系统来缓存数据在本地,之前实际写入永久存储(在close()fsync()fdatasync());
这在远程文件系统中非常常见,这就是手册页中提到NFS的原因。

关闭只读输入文件时,我从未遇到错误。我可以想到的是,使用任何常见文件系统在现实生活中可能发生的所有情况都是发生灾难性故障的情况,例如内核数据结构损坏。如果发生这种情况,我认为该close()错误不可能是发生严重错误的唯一迹象。

当写入远程文件系统上的文件时,close()如果本地网络容易出现故障或只是丢弃大量数据包,则-time错误非常普遍。作为最终用户,我 希望
我的应用程序告诉我写入文件时是否出错。通常,到远程文件系统的连接会完全断开,并且向新文件写入失败的事实是用户的第一个指示。

如果不检查close()返回值,应用程序将对用户说谎。它将指示(如果没有错误消息,则将缺少错误消息)表明文件已正确写入,而实际上并非如此,并且已告知应用程序;该应用程序只是忽略了指示。如果用户像我一样,他们会对应用程序感到非常不满意。

问题是,用户数据对您有多重要?当前大多数应用程序程序员根本不在乎。Basile
Starynkevitch(在对原始问题的评论中)是绝对正确的;close()大多数程序员都不愿意检查错误。

我认为这种态度是应受谴责的。骑士无视用户数据。

但是,这很自然,因为用户没有明确指示哪个应用程序破坏了他们的数据。根据我的经验,最终用户最终会归咎于操作系统,硬件,开放源代码或免费软件,或者是本地IT支持。因此,程序员不必担心社交或其他方面的压力。因为只有程序员才知道诸如此类的细节,而大多数程序员都不在乎,所以没有改变现状的压力。

(我知道上面的内容会使很多程序员讨厌我的胆量,但至少我是诚实的。我指出诸如此类的典型反应是,这种情况很少见,浪费资源来检查这一点。这可能是正确的。但是我愿意花更多的CPU周期并向程序员多付几分钱,这是否意味着我的机器实际上可以更可预测地工作,并告诉我是否它丢失了情节,而不是无声地破坏了我的数据。)

是否有一个简单,可复制的示例,说明这种无声的数据丢失?

我知道三种方法:

  1. 使用USB记忆棒,然后在决赛之后write()但在之前将其拔出close()。不幸的是,大多数USB记忆棒的硬件都设计得不能幸免,因此您最终可能会弄脏USB记忆棒。取决于文件系统,您的内核也可能会死机,因为大多数文件系统都是在假设这种情况永远不会发生的情况下编写的。

  2. 设置NFS服务器,并通过使用iptables丢弃NFS服务器与客户端之间的所有数据包来模拟间歇性数据包丢弃。具体情况取决于服务器和客户端,安装选项和使用的版本。但是,使用两个或三个虚拟机来建立测试台应该相对容易。

  3. 使用自定义文件系统来模拟一次写入错误close()。当前的内核不允许您强制卸载tmpfs或回送挂载,而只能强制卸载NFS挂载,否则可以通过在最终写入之后但在之前写入文件来强制卸载文件系统,这很容易模拟close()。(当前的内核只是在该文件系统上存在打开的文件时拒绝umount。)对于应用程序测试,创建tmpfs的变体,close()如果文件模式表明需要,则返回一个错误(例如,其他可写但非其他可写-可读性或其他可执行性,即-??????-w-)将非常容易且安全。它实际上并不会破坏数据,但是如果内核在关闭时间报告数据破坏(存在风险),它将使检查应用程序的行为变得容易。

2020-06-03