日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一个 bad file descriptor 的问题

發布時間:2024/4/11 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一个 bad file descriptor 的问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

先來看一個 demo:

?????1?package?main2?3?import?(4??"fmt"5??"net"6??"os"7??"runtime"8?)9?10?var?rawFileList?[]*os.File11?12?func?main()?{13??l,?err?:=?net.Listen("tcp",?":12345")14??if?err?!=?nil?{15???fmt.Println(err)16???return17??}18?19??var?connList?[]net.Conn20??for?{21???conn,?err?:=?l.Accept()22???connList?=?append(connList,?conn)23???if?err?!=?nil?{24????fmt.Println(err)25????return26???}27?28???go?func()?{29????f,?err?:=?conn.(*net.TCPConn).File()30????if?err?!=?nil?{31?????fmt.Println(err)32?????return33????}34?35????rawFile?:=?os.NewFile(f.Fd(),?"")36????rawFileList?=?append(rawFileList,?rawFile)37????_?=?rawFile38????for?{39?????var?buf?=?make([]byte,?1024)40?????conn.Read(buf)41?????conn.Write([]byte(`HTTP/1.1?200?OK42?Connection:?Keep-Alive43?Content-Length:?044?Content-Type:?text/html45?Server:?Apache46?47?`))48?????runtime.GC()49????}50???}()51??}52?}

可以認為是一個簡單 read request,write response 的 http server,用 wrk 壓的話,也能正常運行:

~?????wrk?http://localhost:12345 Running?10s?test?@?http://localhost:123452?threads?and?10?connectionsThread?Stats???Avg??????Stdev?????Max???+/-?StdevLatency???589.84us????0.86ms??27.30ms???98.98%Req/Sec?????9.19k?????1.00k???10.91k????68.50%183093?requests?in?10.02s,?16.94MB?read Requests/sec:??18278.93 Transfer/sec:??????1.69MB

進程也沒有什么錯誤日志,把上面的代碼注釋掉第 36 行再用 wrk 壓測,這回結果就不一樣了:

file?tcp?[::1]:12345->[::1]:58949:?fcntl:?bad?file?descriptor

這個結果還是有點令人意外的,我們又沒有主動關閉連接,為什么會出現 bad file descriptor?

在代碼中,我們使用連接的 rawFile 的 fd 新建了一個文件:

????29????f,?err?:=?conn.(*net.TCPConn).File()30????if?err?!=?nil?{31?????fmt.Println(err)32?????return33????}34?35????rawFile?:=?os.NewFile(f.Fd(),?"")?//?這里36????rawFileList?=?append(rawFileList,?rawFile)37????_?=?rawFile

注釋掉 36 和沒注釋有什么區別呢?是誰把我們的連接給關了?

答案比較簡單,rawFileList 是在堆上分配的全局對象,我們把 rawFile 追加進該數組后,GC 時便不會回收 rawFile。在 Go 語言中,文件類型在 GC 回收時會執行其 close 動作,這是通過 newFile 時的 SetFinalizer 完成的:

func?newFile(fd?uintptr,?name?string,?kind?newFileKind)?*File?{...?省略runtime.SetFinalizer(f.file,?(*file).close)return?f }

也就是說所有文件類型都會在 GC 時被 close,在本文開頭的 demo 中,這個被 close 的文件是我們用 raw fd 創建出來的,而 raw fd 本身是 uintptr 類型。我們知道,帶 GC 的語言,對象之間主要是通過指針引用的,當我們用 uintptr 來創建新文件時,其實已經把這個引用關系破壞掉了:


右邊的 NewFile 如果被 GC 先回收了,那么左邊還在用這個文件就會報 bad file descriptor:

這時候可能有讀者會覺得奇怪了,按說 net.Conn 是有 File 方法的,為什么我們直接用 File 這個方法生成出來的文件就沒有問題?

那是因為 File 的實現中,將原有的 fd 復制了一份:

func?(c?*conn)?File()?(f?*os.File,?err?error)?{f,?err?=?c.fd.dup()?//?復制?fdif?err?!=?nil?{err?=?&OpError{Op:?"file",?Net:?c.fd.net,?Source:?c.fd.laddr,?Addr:?c.fd.raddr,?Err:?err}}return }

dup 操作會在 fd 上增加一個引用計數,當引用計數減為 0 時,才會執行 finalizer。

綜上,看起來是個很簡單的問題,生產環境查起來還是要費一些時間。因為類似的問題并不常見,祝你好運。

總結

以上是生活随笔為你收集整理的一个 bad file descriptor 的问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。