文件I/O故障处理策略

3
我正在将数据缓存到文件中,包括读取和写入操作。使用的是fopen(), fseeko()等标准 ANSI C 文件 I/O 函数。在所有情况下,我都将数据写入到磁盘上的标准本地文件中。这些文件 I/O 操作失败的概率有多大?对于失败,应该采取什么策略?我不是追求统计数据,而是寻求一般性建议,了解如何处理错误条件。
例如,每个人都知道malloc()有可能失败,在某些用户的机器上可能会失败,开发者应该检查是否返回了 NULL。但是,对于像malloc()这样的内存分配函数,没有很好的修复策略,因为这可能意味着系统已经没有可用内存了。至少,在桌面系统上似乎是这种方法,而嵌入式系统则不同。
同样,在文件 I/O 操作中,是否值得尝试重新尝试,还是应该认为失败是基本不可恢复的等等。我希望提供一些演示正确用法的代码示例,或者提供一个库指南参考,以说明如何处理这些问题。当然,任何其他数据都是受欢迎的。

好问题。当写入失败时,通常意味着文件系统已满。我曾经看到过一些代码尝试解决这个问题,并要求用户释放一些空间。这是一个 CAD 程序,在磁盘容量很小的时候创建了巨大的文件,所以这是一个合理的期望。 - Charlie Burns
如果你的程序有任何形式的用户界面,那么在循环中提示重试是值得的。最常见的写入失败是当另一个进程中打开文件时发生的。 - paddy
2个回答

1

我猜您是一名初学者程序员。这里给出的建议并不适用于所有情况,但它将帮助您编写可靠的代码。

尝试弄清楚如何从错误中恢复是很困难的,除非您对错误可能发生的方式及其含义有非常坚实的模型。因此,除非您确切地知道错误是什么以及其含义,否则请在stderr或其他位置报告错误并退出。如果第一件事情出了问题就立即退出,您将被迫理解错误并修复代码。即使您的直觉告诉您相反,这也会导致长期更高质量的代码。某些函数返回“错误”,但不表示严重失败。在POSIX中,EINTR作为一个hack存在于那里,以使信号处理更容易实现,并且它具有使关心信号的单线程程序的某种体系结构更容易实现的副作用。当I/O函数返回EAGAIN时,这意味着您以非阻塞模式打开了文件,而I/O想要阻塞。您需要正确处理这些事情。一些错误表明发生了可怕的事情;在POSIX中,EIO表示发生了某些无法谈论的错误。使用文件系统代码时,您会注意到某些错误可能是由文件的并发更新引起的。试图“优雅地”从这些事情中“恢复”是愚蠢的行为,请不要尝试。

我认为EINTR并不是一种hack手段,即使有很多流行文章支持这一说法。一个程序应该随时可中断(例如通过Control-C)。 - Jo So
@JoSo:你把EINTRSIGINT搞混了。^C发送SIGINT信号。当一个信号在阻塞系统调用期间被传递时,就会发生EINTR - tmyklebu
1
EINTR 就是在收到 SIGINT 信号时被中断的结果(除非你没有安装信号处理程序,在这种情况下,你的程序将被终止)。 - Jo So
是的,需要创建一个“慢速”设备的概念。请阅读有关 signal(7) 的内容。 - Jo So
不,你不是新手,但你有一些好的想法,我认为你的回答非常有帮助。谢谢! - Brett
显示剩余2条评论

0

这是我被快速downvote的回答的副本,我想再给它一次机会。

这非常取决于程序的类型。

以Glib为例,这是一个流行的C库,甚至不关心处理OOM;它只是中止。这对于应用程序代码可能是适当的,但对于某些系统级别的代码则不是。

在大多数情况下,像I/O错误或OOM这样的情况可以被认为是不可避免的。例如,许多遇到OOM的程序具有非常顺序化(少量分支)的代码路径,并且在分配失败时没有替代方案。因此,大多数程序将只退出(1)。

如果您正在操作敏感状态,则应尽力避免崩溃或退出。

然而,清理通常很困难,特别是在纯C中。

作为明智的最低要求,您应始终尝试调查失败的直接原因并将其打印到stderr--这有助于调试。

我建议阅读reprepro的源代码,它非常小心地处理错误条件和清理工作。这是大量的样板文件,因此在阅读后您可能会选择它不适合您的应用程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接