golang需要依赖操作系统吗?底层调用了库的api吗?

golang最开始使用c和汇编实现编译器,后面自举用自己语言实现了编译器,编译的到的执行程序,是调用了系统api还是直接的机器码呢?如果是机器码的话,…
关注者
57
被浏览
34,291

11 个回答

无论是否使用cgo, go语言的源码都是由go编译器自己直接编译到机器码的。

> ls /usr/lib/go/pkg/tool/linux_amd64/
addr2line  api  asm  buildid  cgo  compile  cover  dist  doc  fix  link  nm  objdump  oldlink  pack  pprof  test2json  trace  vet

go有自己的连接器和汇编器,汇编语法用的是plan 9的汇编(因为开发go的那帮人就是之前开发plan 9系统的)

Linux 上CGO=0直接调用syscall, 因为Linux内核的syscall的接口是稳定的,而且有详细的文档和源码描述了syscall的作用。使用了cgo就会链接到libc。

使用file可以看一个文件是不是静态链接的,如果是静态链接的,大概率没有使用cgo.

> file /usr/lib/go/bin/gofmt
/usr/lib/go/bin/gofmt: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=7CNMUy5mt536qu9YRy6P/7Frsz27gZ2vNGF0UDT5K/-A0KysgXH51w2QhNF0CJ/np15rFrPY-jUKUXGqAas, not stripped
> file /usr/lib/go/bin/go
/usr/lib/go/bin/go: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, Go BuildID=hzq3ywu7Vz9lE7wfr3Ir/oJO_d3mxhOdJI_d4t7Wi/9_PaQ_NYnSFzwQSJLlht/x6yO94PaewRCgn55gRae, not stripped

macOS是闭源系统,syscall及内部细节经常变动,所有软件(包括go语言写的)必须链接到 /usr/lib/libSystem.B.dylib 以及其他framework.

Windows是闭源系统,虽然比较注重二进制兼容性,且Windows的系统调用虽然经过人们对ntoskrnl.exe和win32k.sys的逆向都了解其参数和作用,但是Windows各个版本的系统调用号是不固定的(具体数据详见 j00ru/windows-syscalls ),因此无法直接发起系统调用(因为无法在运行前确定系统版本)。除了驱动和病毒之外的Windows程序都使用接口稳定的 kernel32.dll 等win32 api

所以:

  1. golang需要依赖操作系统。(拿go写操作系统除外)
  2. 除cgo=0+Linux平台之外,go编译的程序需要调用系统库的api

当然有依赖,所有应用软件开发语言,只要涉及系统IO(包括网络,文件系统操作,显示或输出),都会需要调用操作系统API,包括golang。

学过操作系统原理都知道,CPU分ring0-ring3四个等级(目前操作系统基本只用了ring0和ring3两个等级,分别称为“内核态”和“用户态”),应用都是跑在ring3等级的,应用软件编程语言编译后的代码也只能是ring3等级的指令。

而涉及系统IO的操作,都是操作系统内核态(通过驱动程序实现)代码,都需要操作系统通过系统调用暴露为用户态可调用的API。

当然,golang能否编写内核态代码?当然可以,只要用golang编写驱动程序,让操作系统载入到内核地址空间运行即可。但一般没有这个场景,golang写用户态软件系统才是绝大多数场景。

golang的系统标准库都是跨平台的,因为在编译的时候,golang会根据操作系统不同,载入库的不同操作系统调用实现,链接的时候也会根据操作系统的不同而生成ELF或者PE文件格式,这都是不需要程序员关注的细节部分。但如果golang标准库没有覆盖到的API调用,就需要自己显式封装和编写操作系统调用了。