1.6 探寻Go语言程序的编译执行过程

无论是通过go build命令运行,还是通过IDE直接运行,其实都是封装了一系列编译执行的操作,这为程序员的开发提供了很多便利。但是,Go语言的具体编译执行过程还是值得我们探究一下,以更好地理解Go语言相对于其他语言的特点。

1.6.1 go build命令的选项

在Linux/Windows/macOS中,大多数命令都有多种选项,这些选项可以实现命令的个性化需求。我们可以利用go help build来查看go build命令的选项,具体的命令及输出如下:

可以看到,go build的选项非常多,其中有3个需要我们特别关注:

· -n:打印编译的详细过程,但是并不实际执行,相当于预览功能。

· -x:执行编译,同时也打印编译的详细过程。

· --work:这是一个追加选项,在go build的编译过程中会生成部分中间目录和文件,编译结束后,会将生成的临时目录和文件删除,--work用于保留生成的临时目录和文件,方便开发者查看和诊断问题。

1.6.2 查看编译的详细过程

我们可以直接利用-x和--work选项来查看编译的详细过程,具体的命令及输出如下:

从go build命令的输出可以看出,该命令实际执行了若干操作系统指令。各指令说明如下:

(1)WORK=/var/folders/28/w3v87nrn6fsd06mkl9v21vnr0000gn/T/go-build3761933774:用于指定WORK变量的值。WORK就是go build的工作目录,该目录的前半部分“/var/folders/28/w3v87nrn6fsd06mkl9v21vnr0000gn/T”来源于操作系统的临时目录;后半部分“go-build3761933774”是编译时自动追加的子文件夹。在macOS中,操作系统临时目录利用变量$TMPDIR进行定义;而在Windows操作系统中,该值来源于环境变量TMP。

(2)mkdir -p $WORK/b001/:用于递归创建工作目录。它保证了工作目录一定存在。

(3)cat >$WORK/b001/importcfg.link << 'EOF' # internal,以及其后的多条packagefile语句,直至EOF:用于生成名为importcfg.link的文件。最后,将所有需要链接的文件(.a文件)写入importcfg.link中。

(4)mkdir -p $WORK/b001/exe/:用于在工作目录下创建一个名为exe的文件夹,该文件夹用于存放生成的可执行文件。

(5)/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/a.out -importcfg...:该指令执行链接操作,并生成a.out文件。

(6)/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out:该指令用于更新a.out文件的build ID。-w选项代表要重写a.out中的build ID。build ID是可执行文件的独一无二的身份标识。

(7)mv $WORK/b001/exe/a.out first:将a.out重命名为first,first文件就是最终生成的可执行文件。

1.6.3 链接环节

在上述go build的过程中,链接(link)环节尤其值得我们注意。所有的程序实际上都需要与外部其他程序(或代码)进行关联和调用,被调用的代码往往被称为共享库。例如,在Go语言程序中,往往需要引用Go语言内置的函数。实际运行时,这些共享库如何被我们的代码调用就成为一个问题。链接就是为所有相互调用的函数指定入口位置。

链接分为静态链接和动态链接。

· 静态链接:将使用到的所有共享库复制到最终的可执行文件中。典型的输出文件的形式是单个文件。

· 动态链接:只将共享库的地址编译到最终的可执行文件中。共享库以独立的文件存在,其最终表现形式往往是多个文件。例如,在Windows中的一个应用程序,往往存在一个.exe文件和多个.dll文件。

因此,静态链接最终的可执行文件往往比较大,而动态链接最终的可执行文件比较小。当然,这并不意味着二者有绝对的优劣之分,只是不同的场合使用不同的链接方式。

Go语言在绝大多数情况下都使用静态链接,这一点从前面的编译过程也能看出来——最终文件是单一的、较大的可执行文件。当然,Go语言也支持动态编译,这是因为Go语言也支持对于C语言程序的调用,而C语言程序往往以外部共享库的形式存在。但在本章中,我们只讨论常用的静态链接场景。

在演示实例的链接环节中,我们可以看到link的完整命令为:

其中,命令选项-buildmode=exe指定构建模式为exe。

我们可以通过go help build mode命令来查看构建模式的详细信息:

在其中找到buildmode=exe的解释,可以看到,在该模式下会将main包以及所有导入包构建为一个可执行文件。这其实就是静态链接的过程。