一个 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 的问题相关推荐
- linux 安装包 在此作用域中尚未声明_Linux运行go项目报错:copy_file_range: bad file descriptor...
这两天在 Linux 环境部署一个 Go 项目遇到一个报错:copy_file_range: bad file descriptor.网上查找各种方法,花了两天的时间,经过一番折腾后才解决,觉得非常有 ...
- php扩展dio,PHP Dio扩展新函数dio_fdopen参数返回--bad file descriptor的分
昨天准备做一个程序,PHP的串口扩展程序,用来做串口打开的,于是用dio_fdopen来新建一个文件: view plaincopy to clipboardprint? 1. 2.<?php ...
- This file can not be opened as a file descriptor; it is probably compressed
链接:FileNotFoundException: This file can not be opened as a file descriptor; it is probably compresse ...
- 文件描述符file descriptor与inode的相关知识
每个进程在Linux内核中都有一个task_struct结构体来维护进程相关的 信息,称为进程描述符(Process Descriptor),而在操作系统理论中称为进程控制块 (PCB,Process ...
- logstash 报错Bad file descriptor - Bad file descriptor
使用bin/logstash -f config/D_HINFO_PDT.conf 启动日志中没有错误: 我使用 nohup bin/logstash -f config/D_HINFO_PDT.co ...
- i节点(inode)和文件描述符(file descriptor)的区别和联系
http://blog.csdn.net/wangchaoxjtuse/article/details/6036816 inode 或i节点是指对文件的索引.如一个系统,所有文件是放在磁盘或flash ...
- 用dup2和dup产生一份file descriptor 的拷贝
在类Unix操作系统里面,.dup2和dup都通过系统调用来产生一份file descriptor 的拷贝. dup对我来说还很简单 int dup(int filedes); ...
- mysql 文件描述符_MySQL异常探究:File Descriptor xxxx exceeded FD_SETSIZE=xxxx
异常LOG 问题表象 表象为:在连接数据库时报以下错误: The last packet sent successfully to the server was 0 milliseconds ago ...
- File Descriptor问题总结
今天客户物理机上遇到文件描述符用尽的问题,现象包括: SSH连接物理机卡住 PG服务端口TCP心跳检测失败 PSQL卡住 报错:too many open files 概念 在Linux系统中一切皆可 ...
最新文章
- python语言基础汇总
- 数字经济时代,什么是关键资源?(算力篇)
- 用服务器安装nginx部署web页面
- python交并补_Python 集合的交差并补操作及方法
- EDM邮件群发十大技巧提升邮件群发效果
- 使用逻辑回归制作评分卡
- cdn dashjs_CSS以及JS各种库的在线CDN引用地址
- npoi 所有列调整为一页_Word节约纸张打印 多页内容一页打印
- assoc fetch mysql 用法_mysql_fetch_assoc、mysql_fetch_object、mysql_fetch_row、mysql_fetch_array用法学习...
- php是视频还是图片格式,php 视频、音频和图片文件上传,该如何解决
- Spring依赖注入static静态变量相关问题
- android屏幕操作提示音,快捷指令库提示音
- 5个免费可商用的图片素材网站,赶快收藏
- 防盗系统Java_java小区防盗报警系统
- Python微信防撤回,基于itchat模块
- 为何延时函数不起作用?
- 华为手机word插件加载失败_c#调用word的组件时失败解决方法
- 数学之美-读书笔记6-10章
- CSP 202206-1 归一化处理
- 北京市道路街道区县shape分享
热门文章
- python调用winrar解压_批量文件解压缩脚本(Python3.5 + WinRAR)
- php 删除mysql 返回_php 返回mysql字符编码与删除字符编码
- webpack 4.0 小记
- Spring Cloud微服务系列文,服务调用框架Feign
- Mac下crontab -e没结果的解决办法
- 柯南君:看大数据时代下的IT架构(4)消息队列之RabbitMQ--案例(Helloword起航)...
- 接口隔离原则(设计模式4)
- C++虚函数与虚函数表
- 分享字符串右移的算法
- Windows Phone MultiBinding :Cimbalino Toolkit