在golang中安全关闭文件描述符

3
另一个问题如何使用Go读写文件?在评论中谈到了安全关闭文件描述符的问题。

请注意,这些示例没有检查fo.Close()的错误返回。从Linux手册close(2)中可以看出:不检查close()的返回值是一种常见但严重的编程错误。先前write(2)操作的错误可能首先在最终close()时报告。关闭文件时不检查返回值可能会导致数据静默丢失。这在使用NFS和磁盘配额时尤其明显。- Nick Craig-Wood Jan 25 '13 at 7:12

更新后的解决方案使用了panic:
// close fo on exit and check for its returned error
defer func() {
    if err := fo.Close(); err != nil {
        panic(err)
    }
}()

我希望将这个错误作为一个值而不是抛出异常。

1个回答

5

如果我们担心写入操作没有完成,那么仅使用close是不够的,因此更新错误仍然是不正确的。

如果您想避免这种情况,正确的解决方案是对文件进行fsync

defer(fd.Close())
// Do stuff
return fd.Sync()

相对于通过 defer 或在整个函数中维护非 nil 修改后的错误,直接返回一个非 nil 错误更易于阅读。

这将导致性能下降,但会捕获写入缓冲区和实际写入磁盘时的关闭错误。


1
“这意味着匿名函数必须在每个函数中定义”是一个错误的说法。比如,你可以声明func checkedClose(c io.Closer, perr *error)并使用defer checkedClose(fd, &err)来安排它的延迟调用。换句话说,这是那些罕见情况之一,其中指向接口值的指针非常有用。 - kostix
还有一件需要考虑的事情是,你似乎过于专注于处理释放外部资源(如文件)的延迟调用中的错误机制,而实际上这些机制本身并不是最重要的。请注意,在你的解决方案中,你基本上是通过向标准错误流(或者任何log输出被重定向到的地方)写入消息来替代关闭文件时静默处理错误。虽然这可能比完全忽略错误稍微好一些,但是你将很难将你的通用解决方案适应到执行... - kostix
在关闭调用失败时,有一些有用的操作。有多个事情需要考虑:文件是否仅以读取方式打开?如果是,并且读取过程没有错误,那么忽略未能关闭文件的失败(可能会记录一个警告,但其他方面是安全的)。文件是否以写入方式打开?如果是,未能关闭它可能确实很严重。如果是这样,仅仅发出警告可能不足够:您可能希望将整个操作标记为失败,其中包括写入此文件的部分,或者您可能希望与用户交互以重试保存,或者完全进行其他操作。 - kostix
@kostix 关于接口引用的观点很好!我一定会进行修改。至于示例代码,它只是展示功能。在我的实际用例中,文件使用截断和写入方式打开,然后我使用os.Copy来读取临时文件的内容并写入fd。目前我没有重试功能,但如果该函数返回关闭错误,那可能是正确的解决方案。 - Brian
请在讨论Go时尽量不要使用"引用"这个词;Go语言并没有引用,它有多种内置类型,当这些类型的值被传递时可以说具有"引用语义"(这些类型包括切片、字符串、映射、通道、函数和指针),而任何用户定义的struct类型如果包含具有引用语义的字段,则会自动"继承"这些引用语义。但是,Go语言仍然没有引用(例如,C++和C#作为一种语言概念中有引用,但Go语言没有),它有指针,并且我建议使用指针;-) - kostix

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