Golang中的"Too Many Open File"错误

9

我只是在读取/proc/diskstats文件。我的代码如下:

func ReadFromFile(filepath string)(string){
    defer func() {
        if err1 := recover(); err1 != nil {
            fmt.Println("!!!!!!!!!!!!!!!!Panic Occured and Recovered in readFromFile(), Error Info: ", err1)
        }
     }()

    strData := ""

    data, err := ioutil.ReadFile(filepath)
    if err != nil{
        fmt.Println("File read error: ", err)
        return ""
    }

    strData = string(data)
    return strData
}

我收到的错误信息是:
File read error: open /proc/diskstats: too many open files

不仅对于这个文件,我还遇到了其他一些文件出现相同的错误。

我已经运行了这个命令:

root@golang:~# lsof|wc -l

785

请指导我。

1
你的ulimit是多少? - khrm
1
你读完后要关闭吗? - khrm
不,我认为需要帮助。你能帮忙吗? - GKV
3
你是如何调用这个函数的?泄漏似乎来自其他地方。 - khrm
lsof|wc -l 统计系统范围内的打开文件数。您必须使用 -p 选项运行它以针对您的进程进行统计。实际查看 lsof 的输出也有助于查看打开的文件描述符以帮助识别问题。 - JimB
显示剩余3条评论
3个回答

16

我遇到了同样的问题(可能是不同的情况或设置),但我用不同的方法解决了它:

func some_func(file_name []string) {
    for _, file_name := range file_names {
        f, _ := os.Create(file_name)
        // defer f.Close() // bad idea
        _, _ = f.Write([]byte("some text"))
        f.Close() // better idea
    }
}

问题是defer将在函数返回时执行,这可能需要一段时间-取决于循环大小(不好的想法)。因此,只需显式执行它(更好的想法)。


f.Write需要一个[]byte作为输入,例如[]byte("一些文本") - 030

8

该帖没有提供一个最小可复现示例。引起错误的代码没有被发布。简单演示这一点的方法是在最小示例(没有其他活动)中运行提供的代码,看它是否不会失败。

当然,ioutil.ReadFile函数会关闭文件所以在这种情况下涉及它只是因为当资源限制已达到时它试图打开新文件。


Go语言中常见的问题之一是未显式关闭隐式打开的流。 其特定情况是使用http库的客户端功能时会打开流。

例如:(1), (2)

客户端完成后必须关闭响应体:

resp, err := http.Get("http://example.com/")
if err != nil {
    // handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
这样的请求应始终包括对Close的调用,使用如上所示的形式。

可能还有其他类似的隐式流被打开的情况...


这是一个特别棘手的问题,因为对于简单的程序,你永远不会知道差别。只有在运行了数百次或数千次之后,你才会发现问题。然后,错误通常会被呈现为某些不相关函数调用的失败--正如OP所演示的那样。


// Body 是请求的正文。 // // 对于客户端请求,nil 正文表示该请求没有正文,例如 GET 请求。HTTP 客户端的传输 // 负责调用 Close 方法。 // // 对于服务器请求,请求正文始终为非 nil,但当不存在正文时,将立即返回 EOF。服务器将关闭请求正文。 // ServeHTTP 处理程序不需要关闭它。 Body io.ReadCloser - Brent Bradburn
1
如果在更大的函数上下文中使用defer,可能最好将该部分包装在一个单独的函数中(也许是IIFE)-- 因为执行被推迟到“直到周围的函数返回”(https://tour.golang.org/flowcontrol/12)。 - Brent Bradburn

7

在UNIX平台上,操作系统限制了进程在任何给定时间内可以拥有的打开文件描述符数量的上限。
当您已达到当前打开的文件(和/或管道或套接字)的限制并尝试打开新文件(和/或管道或套接字)时,会出现错误too many open files
为避免此问题,在使用完打开的文件后,必须使用Close()函数关闭该文件。


1
说实话,我本来以为ioutil.ReadFile会关闭文件(尽管很可能是其他文件描述符在别处被打开导致了问题)。 - Vatine
如果我们查看ReadFile,我们可以看到Close()函数仅在打开文件失败时调用(https://golang.org/src/io/ioutil/ioutil.go?s=1464:1510#L39)。 另一个选项是使用返回语句来自动调用Close()函数(作为void,因此您必须不返回任何东西)。 - Tinwor
3
不,第54行有一个“defer f.Close()”,导致该文件在函数返回时关闭。如果打开调用失败,会出现早期返回(以避免在未定义值上调用Close()方法)。 - Vatine
你是对的@Vatine,我没有看到之前的返回,是我的错。 - Tinwor

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