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。