Linux的“man close”警告(SVr4,4.3BSD,POSIX.1-2001):
不检查close()的返回值是 common ,但仍然是严重的编程错误。很可能首先在最后的close()上报告先前write(2)操作的错误。关闭文件时不检查返回值可能会导致数据无声丢失。使用NFS和磁盘配额尤其可以观察到这种情况。
我可以相信这个错误是常见的(至少在应用程序中;我不是内核黑客)。但是,在今天或过去三十年的任何时候,严重是怎样的呢?特别是:
是否存在这种无声数据丢失的简单,可重现的示例?即使是一个人为的人,比如在close()期间发送SIGKILL?
如果存在这样的示例,可以比
更优雅地处理数据丢失 printf("Sorry, dude, you lost some data.\n");
?
答案 0 :(得分:7)
[H]严重的是,今天还是过去三十年的任何一点?
典型应用程序进程数据。他们消耗一些输入,并产生结果。因此,有两种常见情况close()
可能会返回错误:关闭输入(只读?)文件时,以及关闭刚刚生成或修改的文件时。
close()
返回错误的已知情况特定于将数据写入/刷新到永久存储。特别是,在实际写入永久存储(close()
,fsync()
或fdatasync()
)之前,操作系统通常会在本地缓存数据;这在远程文件系统中非常常见,这也是手册页中提到NFS的原因。
关闭只读输入文件时,我从未遇到过错误。我可以想到使用任何常见文件系统在现实生活中可能发生的所有情况都是发生灾难性故障的情况,例如内核数据结构损坏。如果发生这种情况,我认为close()
错误不能是出现严重错误的唯一迹象。
当写入远程文件系统上的文件时,close()
- 时间错误非常常见,如果本地网络容易出现故障或丢失大量数据包。作为最终用户,我希望我的应用程序告诉我写入文件时是否有错误。通常,与远程文件系统的连接完全被破坏,写入新文件失败的事实是用户的第一个指示。
如果您不检查close()
返回值,则应用程序将由用户负责。它将指示(由于缺少错误消息,否则),文件被正确写入,而事实上它不是,并且应用程序被告知;应用程序只是忽略了指示。如果用户和我一样,他们会非常不满意这个应用程序。
问题是,用户数据对您有多重要?大多数当前的应用程序员根本不关心。 Basile Starynkevitch(在对原始问题的评论中)是绝对正确的;检查close()
错误并不是大多数程序员都在做的事情。
我认为这种态度是应该受到谴责的;骑士无视用户数据。
很自然,因为用户没有明确指出哪个应用程序损坏了他们的数据。根据我的经验,最终用户最终会指责操作系统,硬件,开源或免费软件或本地IT支持;因此,程序员无需承担社交或其他方面的压力。因为只有程序员知道这样的细节,并且大多数程序员都不在乎,所以没有改变现状的压力。
(我知道上面的说法会让很多程序员讨厌我的胆量,但至少我是诚实的。我指出这样的事情的典型反应是,这是一种罕见的情况,这可能是浪费资源来检查这一点。这可能是真的..但我愿意花费更多的CPU周期并为程序员多付几个百分点,如果这意味着我的机器实际上工作更加可预测,并且告诉我它是否丢失了情节,而不是默默地破坏我的数据。)
是否存在这种无声数据丢失的简单,可重现的例子?
我知道三种方法:
使用USB记忆棒,在最后write()
之后但在close()
之前将其拉出。
不幸的是,大多数USB记忆棒的硬件都不是为了能够存活而设计的,因此最终可能会使USB记忆棒变硬。
根据文件系统的不同,你的内核也可能会感到恐慌,因为大多数文件系统都是在假设永远不会发生的情况下编写的。
设置NFS服务器,并使用iptables删除NFS服务器和客户端之间的所有数据包,模拟间歇性数据包丢弃。 具体方案取决于服务器和客户端,装入选项以及使用的版本。但是,使用两个或三个虚拟机可以相对容易地设置测试台。
使用自定义文件系统在close()
时模拟写入错误。
当前内核不允许强制卸载tmpfs或环回挂载,只允许NFS挂载,否则通过在最终写入之后强制卸载文件系统但在close()
之前,可以很容易地进行模拟。 (如果该文件系统上有打开的文件,当前内核只会拒绝umount。)
对于应用程序测试,创建tmpfs的变体,如果文件模式指示它是合意的,则在close()
返回错误(例如,其他可写但不是其他可读或其他可执行的,即。{{1} })会很容易,也很安全。它实际上不会破坏数据,但如果内核在关闭时报告(风险)数据损坏,它将使检查应用程序的行为变得容易。
答案 1 :(得分:6)
调用POSIX's close()
可能会导致errno
设置为:
EBADF
:错误的文件编号EINTR
:系统调用中断EIO
:I / O错误(来自POSIX规范第6期)不同的错误表明存在不同的问题:
EBADF
表示编程错误,因为程序应该跟踪哪些文件/套接字描述符仍然打开。我会考虑将此错误作为质量管理操作进行测试。
EINTR
似乎是最难处理的,因为在返回函数之后传递的文件/套接字描述符是否有效尚不清楚(在Linux下它可能不是:{{3 }})。观察此错误,您应该检查程序处理信号的方式。
EIO
预计仅在特殊条件下出现,如手册页中所述。但是至少只是因为这个应该跟踪这个错误,因为如果它最有可能出现真的错误。
总而言之,这些错误至少有一个被捕的好理由,所以就这样做吧! ; - )
可能的具体反应:
在稳定性方面,忽略EBADF
可能是可以接受的,但错误不会发生。如上所述修复你的代码,因为程序似乎并不真正知道它在做什么。
观察EINTR
可能表示信号正在疯狂。这不好。绝对是根本原因。由于尚不清楚描述符是否已关闭,因此请尽快重新启动系统。
明确地遇到EIO
可能会导致所涉及的硬件 * 1 严重失败。但是,在强烈建议关闭系统之前,简单地重试操作可能是值得的,尽管同样的问题适用于EINTR
,不确定描述符是否真正关闭。如果它确实关闭了,再次关闭它是一个坏主意,因为它可能已被另一个线程使用。请尽快关闭并更换硬件 * 1 。
* 1硬件在这里可以看出:NFS服务器充当磁盘,因此EIO
可能只是由于配置错误的服务器或网络或NFS中涉及的任何内容连接。