由一个问题引起对文件描述符的研究

故事的原由

一次一个同事给我发了一段简单的代码,问我这段代码有什么问题?linux

package main

import (
    "fmt"
    "os"
)

func main() {
    f, err := os.Open("/test.txt")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(f.Name(), "opened successfully")
}

看到这段代码后不加思索的回答,文件没有close,他说错,可能当时咱们没在一个频道上,“err处理没有return”。微信

又仔细的看了下代码,发现err的处理代码块后使用了f.Name(),这个是存在问题的,由于当open发生错误时,返回的文件句柄则为nil,下文直接使用f.Name()。这种错误对于初学者常常会犯,改进的方式也不少,只要保证运行f.Name()的获得的f不为nil便可。
能够在发生错误时,能够return或者os.Exit(-1) 也或下文的f.Name()放到else逻辑块中。spa

具体的处理方式要根据对报错的容忍度来处理3d

故事的发展

  • 猜测

刚又提到,程序未对打开的文件作close,固然运行也没问题。既然没问题,也就没有close的必要。可是在open后加defer close已经成为go语言教课书级的示例。
猜测,这里的open底层是一个I/O操做,在linux下全部的I/O操做都会转化为对文件的操做。若是程序对文件open后,没有关闭,则会一直占有资源,打开的数量愈来愈多,最终必定会因达到上限而致使程序出现问题。code

  • 猜测调查
    经过谷歌找到lsof这一命令能够查看打开的文件描述符的上限。

    经过改命令发现我电脑上能够支持程序最大打开的文件描述符是4864个
  • 验证

修改下代码,看下当程序打开4865次会发生什么状况?blog

package main

import (
    "fmt"
    "os"
)

func main() {
    for i := 1; i <= 4865; i++ {
        f, err := os.Open("./test.txt")
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println(f.Name(), "opened successfully", i)
    }
    fmt.Scanln()
}

执行结果
进程

发生了猜测中的问题,刚查看最大文件描述符是4864,这里只打开了4861个,为何少了三个?图片

  • 再次猜测

这里少了三个,那么这三个应该是被系统占用了,这里存在两种可能:资源

    1. 被其它程序占用
    2. 被该程序占用
    • 再次验证

    先来确认第一点,被其它程序占用
    怎么验证呢?能够一样的程序,一个循环数设置3000,一个设置2000,若是结论成立的话,那么后运行的一个必定会出错。

    程序并无向想象中的那样出错rem

    那么就是该程序默认占用了三个
    经过lsof查下进程打开的描述符状况

    发现程序会默认打开三个系统文件描述符
    也就是标准输入,标准输出,错误输出

    这样的解释就能够自说其圆了,真的是这样么?

    • 理论支撑

      如下是维基百科对文件描述符的叙述

    对文件的描述符的探索,能够画上一个句号了。

    遗留问题


    在产看进程关联的文件时,发现有多出以上四个,这些有什么?这个问题做为一个遗留问题抛在这里,等待有心去探索

    总结

    经过以上的试验和验证,在程序打开文件后,记得close
    完善后的最终处理代码

    package main
    
    import (
        "fmt"
        "os"
    )
    
    func main() {
        for i := 1; i <= 50000; i++ {
            f, err := os.Open("./test.txt")
            if err != nil {
                fmt.Println(err)
                return
            }
            fmt.Println(f.Name(), "opened successfully", i)
            f.Close()
        }
        fmt.Scanln()
    }

    喜欢请关注微信公众号“云端漫记" 持续为你更新
    图片描述

    相关文章
    相关标签/搜索