golang gc 关闭fd
4月还是5月的时候写了个golang 的程序,因为要保证最多只有一个进程存在所以进程启动就去获取锁,没有获取文件锁的进程就退出。每分钟我会启动一次进程。目的就是为了进程保活。
使用文件锁就是为了他的特性:
- 如果文件关闭,那么锁也会被回收
遇到的问题
- 问题是:过了半天之后
ps aux
看启动的进程,发现居然有7-8
个。按照预想应该只有一个。 代码大概是长这样的
func lockFile(){
name := "lockfiletest.lock"
file, err := os.OpenFile(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666) //①打开文件
...
err = syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, &flockT) //②加锁
...
}
func main(){
err :=lockFile()
if err!=nil{
os.Exit(2) // ③加锁失败退出
}
}
很简单的逻辑,就是获取文件锁,获取失败则退出
找问题
- 问题出在哪里呢?
想了很久很久:难道是我用的库哪里fork了进程?文件被哪个第三方包关闭了?
想了很久很久一直怀疑第三方包有问题,但是最后经过google
很多次后定位到是gc 的问题。
在下面的例子里面编译后会在手动执行runtime.GC()
后文件被回收
package main
import (
"os"
"log"
"time"
"runtime"
)
func openFile(path string) error {
_, err := os.Open(path)
return err
}
func main() {
if err := openFile(os.Args[1]); err != nil {
log.Fatal(err)
}
// trigger GC below will also recycle the non-referenced fd opened before
runtime.GC()
time.Sleep(time.Hour)
}
-
怎么看进程打开的文件呢?
通过
proc
文件系统就可以了,proc
文件系统几乎把linux内核所有的统计量都导出来了哦
## 8808 就是我的nginx 的master 的pid
ll /proc/8808/fd/
total 0
dr-x------ 2 root root 0 8月 10 06:16 ./
dr-xr-xr-x 9 root root 0 8月 9 07:35 ../
lrwx------ 1 root root 64 8月 10 06:16 0 -> /dev/null
lrwx------ 1 root root 64 8月 10 06:16 1 -> /dev/null
l-wx------ 1 root root 64 8月 10 06:16 2 -> /usr/local/nginx/logs/error.log*
lrwx------ 1 root root 64 8月 10 06:16 3 -> socket:[78178946]
l-wx------ 1 root root 64 8月 10 06:16 4 -> /usr/local/nginx/logs/access.log*
l-wx------ 1 root root 64 8月 10 06:16 5 -> /usr/local/nginx/logs/error.log*
lrwx------ 1 root root 64 8月 10 06:16 6 -> socket:[78180730]
lrwx------ 1 root root 64 8月 10 06:16 7 -> socket:[78178947]
怎么解决
第一:我们的问题是什么? 其实问题很简单:
- 我们的fd这个
对象
被回收了 - gc的调用fd
对象
的回调函数
- 回调函数把fd
对象
对应的文件描述符关闭了
解决方案:
把fd 弄成全局变量,全局变量一直被引用所以不会被gc回收掉
var file *File // 加了一行变成全局变量
func lockFile(){
name := "lockfiletest.lock"
file, err := os.OpenFile(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666) //①打开文件
...
err = syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, &flockT) //②加锁
...
}