Skip to content

golang_gc 相关问题

Posted on:September 12, 2019 at 10:57 PM

golang gc 关闭fd

4月还是5月的时候写了个golang 的程序,因为要保证最多只有一个进程存在所以进程启动就去获取锁,没有获取文件锁的进程就退出。每分钟我会启动一次进程。目的就是为了进程保活。

使用文件锁就是为了他的特性:

遇到的问题

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)
}
## 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回收掉

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)  //②加锁
    ...
}