Golang: Runtime

 20th August 2020 at 2:19pm

Go 有自己的运行时,不依赖于 C 运行时(如 glibc,参考 C Runtime)。Go 1.5 实现了自举,之前运行时是用 C 写的。

Go 有自己的运行时,意味着 Go 只要在不同的平台都写一份运行时,就比较好实现跨平台的可移植性。这也支持了 Go 可以在不同平台做 交叉编译

但是 Go 为了做跨语言调用,实现了 cgo 工具,以在 Go 语言中编写 C 代码。同时有些内置库也提供了 cgo 的和非 cgo 的版本。比如 os/user 包中,同一个 lookupUser 函数有两份实现:

// cgo 实现,代码在 /usr/lib/go/src/os/user/cgo_lookup_unix.go
// 为了简洁省略了部分代码。留意 mygetpwnam_r 是 C 语言提供的接口
func lookupuser(username string) (*user, error) {
    // ...
    err := retrywithbuffer(buf, func() syscall.errno {
        return syscall.errno(c.mygetpwnam_r((*c.char)(unsafe.pointer(&namec[0])),
            &pwd,
            (*c.char)(buf.ptr),
            c.size_t(buf.size),
            &result))
    })
	// ...
    return builduser(&pwd), err
}

// 纯 go 实现,代码在 /usr/lib/go/src/os/user/lookup_unix.go
func lookupUser(username string) (*User, error) {
    f, err := os.Open(userFile)
    if err != nil {
        return nil, err
    }
    defer f.Close()
    return findUsername(username, f)
}

Go 默认用了 cgo 版本,并且内置库预编译了一份在 $GOROOT 中,比如上述的函数在 /usr/lib/go/pkg/linux_amd64/os/user.a 中。默认使用 cgo 的好处是:

  • 编译出来的二进制文件更小
  • 编译更快

缺点是:

  • 依赖于 glibc,导致不同 Linux 机器上,glibc 版本不一致会带来兼容性问题
  • 无法在不用 glibc 的发行版上运行,比如 Apline

可用环境变量 CGO_ENABLED 来控制 go 编译时用不用 cgo 版本,详情参考 Golang: Build

参考