Golang 简介

Golang 简介

目前有2个版本的编译器:Go 原生编译器 gc 和非原生编译器 gccgo,这两款编译器都是在类 Unix 系统下工作 。其中,gc 版本的编译器已经被移植到 Windows 平台上,并集成在主要发行版中.

对于非常底层的纯 Go 语言代码或者包而言,在各个操作系统平台上的可移植性是非常强的,只需要将源码拷贝到相应平台上进行编译即可,或者可以使用交叉编译来构建目标平台的应用程序。 Go 语言的编译器和链接器都是使用 C 语言编写并产生本地代码,Go 不存在自我引导之类的功能。因此如果使用一个有不同指令集的编译器来构建 Go 程序,就需要针对操作系统和处理器架构(32 位操作系统或 64 位操作系统)进行区别对待。( Go从1.5版本开始已经实现自举。Go语言的编译器和链接器都是Go语言编写的)

go build 和 go install 等命令,会自动调用相关的编译器或链接器。

尽管 Go 编译器产生的是本地可执行代码,这些代码仍旧运行在 Go 的 runtime(这部分的代码可以在 runtime 包中找到)当中。这个 runtime 类似 Java 和 .NET 语言所用到的虚拟机,它负责管理包括内存分配、垃圾回收、栈处理、goroutine、channel、切片(slice)、map 和反射(reflection)等等。runtime 主要由 C 语言编写(Go 1.5 开始自举),并且是每个 Go 包的最顶级包。

环境变量

  1. GOROOT 该环境变量的值为 Go 语言的当前安装目录。

  2. GOPATH 该环境变量的值为 Go 语言的工作区的集合(意味着可以有很多个)。工作区类似于工作目录。每个不同的目录之间用:分隔。

工作区是放置 Go 源码文件的目录。一般情况下,Go 源码文件都需要存放到工作区中。工作区一般会包含3个子文件夹,自己手动新建以下三个目录:src 目录,pkg 目录,bin 目录。

  • bin 目录里面存放的都是通过 go install 命令安装后,由 Go 命令源码文件生成的可执行文件。注意:有两种情况下,bin 目录会变得没有意义。如果设置了有效的 GOBIN 环境变量以后,bin 目录就变得没有意义;如果 GOPATH 里面包含多个工作区路径的时候,必须设置 GOBIN 环境变量,否则就无法安装 Go 程序的可执行文件。

  • pkg 目录是用来存放通过 go install 命令安装后的代码包的归档文件(.a 文件)。归档文件的名字就是代码包的名字。所有归档文件都会被存放到该目录下的平台相关目录中,即在 $GOPATH/pkg/$GOOS_$GOARCH 中,同样以代码包为组织形式。这里有两个隐藏的环境变量,GOOS 和 GOARCH。这两个环境变量是不用我们设置的,系统就默认的。GOOS 是 Go 所在的操作系统类型,GOARCH 是 Go 所在的计算架构。平台相关目录是以$GOOS_$GOARCH 命名的,Mac 平台上这个目录名就是 darwin_amd64。

  • src目录是以代码包的形式组织并保存 Go 源码文件的。每个代码包都和 src 目录下的文件夹一一对应。每个子目录都是一个代码包。这里有一个特例,命令源码文件并不一定必须放在 src 文件夹中的。

Go 的源码文件分类:

  • 命令源码文件,声明自己属于 main 代码包、包含无参数声明和结果声明的 main 函数。命令源码文件是 Go 程序的入口,命令源码文件应该是被单独放在一个代码包中。命令源码文件是可以单独运行的。可以使用 go run 命令直接运行,也可以通过 go build 或 go install 命令得到相应的可执行文件。所以命令源码文件是可以在机器的任何目录下运行的。

  • 库源码文件,库源码文件就是不具备命令源码文件上述两个特征的源码文件。存在于某个代码包中的普通的源码文件。库源码文件被安装后,相应的归档文件(.a 文件)会被存放到当前工作区的 pkg 的平台相关目录下。

  • 测试源码文件,名称以 _test.go 为后缀的代码文件,并且必须包含 Test 或者 Benchmark 名称前缀的函数。名称以 Test 为名称前缀的函数,只能接受 *testing.T 的参数,这种测试函数是功能测试函数。名称以 Benchmark 为名称前缀的函数,只能接受 *testing.B 的参数,这种测试函数是性能测试函数。

  1. GOBIN 该环境变量的值为 Go 程序的可执行文件的目录。

可以使用

$go env 查看go相关的环境变量。

Go 程序是通过 package 来组织的。main.main() 函数(这个函数位于主包)是每一个独立的可运行程序的入口点。

package (假设我们的例子中是 package main)这一行告诉我们当前文件属于哪个包,而包名 main 则告诉我们它是一个可独立运行的包,它在编译后会产生可执行文件。除了 main 包之外,其它的包最后都会生成 *.a 文件(也就是包文件)并放置在 $GOPATH/pkg/$GOOS_$GOARCH中(以 Mac 为例就是 $GOPATH/pkg/darwin_amd64 )。

Go 的编译过程

和编译相关的有 build、get、install、run 这4个,罗列一下通用的命令标记,以下这些命令都可适用的:

 名称   说明 
-a 用于强制重新编译所有涉及的 Go 语言代码包(包括 Go 语言标准库中的代码包),即使它们已经是最新的了。该标记可以让我们有机会通过改动底层的代码包做一些实验。
-n 使命令仅打印其执行过程中用到的所有命令,而不去真正执行它们。如果不只想查看或者验证命令的执行过程,而不想改变任何东西,使用它正好合适。
-race 用于检测并报告指定 Go 语言程序中存在的数据竞争问题。当用 Go 语言编写并发程序的时候,这是很重要的检测手段之一。
-v 用于打印命令执行过程中涉及的代码包。这一定包括我们指定的目标代码包,并且有时还会包括该代码包直接或间接依赖的那些代码包。这会让你知道哪些代码包被执行过了。
 -work 用于打印命令执行时生成和使用的临时工作目录的名字,且命令执行完成后不删除它。这个目录下的文件可能会对你有用,也可以从侧面了解命令的执行过程。如果不添加此标记,那么临时工作目录会在命令执行完毕前删除。
 -x 使命令打印其执行过程中用到的所有命令,并同时执行它们。

go run

专门用来运行命令源码文件的命令,注意,这个命令不是用来运行所有 Go 的源码文件的!命令只能接受一个命令源码文件以及若干个库源码文件(必须同属于 main 包)作为文件参数,且不能接受测试源码文件。它在执行时会检查源码文件的类型。如果参数中有多个或者没有命令源码文件,那么 go run 命令就只会打印错误提示信息并退出,而不会继续执行。

最终go run命令是生成了2个文件,一个是归档文件,一个是可执行文件。

go build

当代码包中有且仅有一个命令源码文件的时候,在文件夹所在目录中执行 go build 命令,会在该目录下生成一个与目录同名的可执行文件。

go build 用于编译我们指定的源码文件或代码包以及它们的依赖包。,但是注意如果用来编译非命令源码文件,即库源码文件,go build 执行完是不会产生任何结果的。这种情况下,go build 命令只是检查库源码文件的有效性,只会做检查性的编译,而不会输出任何结果文件。

go build 使用-o标记可以指定输出文件(在这个示例中指的是可执行文件)的名称。它是最常用的一个 go build 命令标记。但需要注意的是,当使用标记-o的时候,不能同时对多个代码包进行编译。

go install

go install 命令是用来编译并安装代码包或者源码文件的。

go get

go get 命令用于从远程代码仓库(比如 Github )上下载并安装代码包。注意,go get 命令会把当前的代码包下载到 $GOPATH 中的第一个工作区的 src 目录中,并安装。

如果在 go get 下载过程中加入-d 标记,那么下载操作只会执行下载动作,而不执行安装动作。比如有些非常特殊的代码包在安装过程中需要有特殊的处理,所以我们需要先下载下来,所以就会用到-d 标记。

还有一个很有用的标记是-u标记,加上它可以利用网络来更新已有的代码包及其依赖包。如果已经下载过一个代码包,但是这个代码包又有更新了,那么这时候可以直接用-u标记来更新本地的对应的代码包。如果不加这个-u标记,执行 go get 一个已有的代码包,会发现命令什么都不执行。只有加了-u标记,命令会去执行 git pull 命令拉取最新的代码包的最新版本,下载并安装。

go fmt

gofmt是一个cli程序,会优先读取标准输入,如果传入了文件路径的话,会格式化这个文件,如果传入一个目录,会格式化目录中所有.go文件,如果不传参数,会格式化当前目录下的所有.go文件。

1
2
3
4
5
6
7
8
9
10
usage: gofmt [flags] [path ...]
  -cpuprofile string
    	write cpu profile to this file
  -d	display diffs instead of rewriting files
  -e	report all errors (not just the first 10 on different lines)
  -l	list files whose formatting differs from gofmt's
  -r string
    	rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')
  -s	simplify code
  -w	write result to (source) file instead of stdout

go -w /path/to/go/file ,带上-w标志才会修改文件,其它时候只会输出

go fmt命令是gofmt的简单封装,go fmt [path …] 相当于 gofmt -l -w [path …] 。

go fmt命令本身只有两个flags,-n和-x,-n仅打印出内部要执行的go fmt的命令,-x命令既打印出go fmt命令又执行它。

静态链接 or 动态链接

Go 在最初刚刚发布的时候,静态链接被当做优点宣传,只须编译后的一个可执行文件,无须附加任何东西就能部署。将运行时、依赖库直接打包到可执行文件内部,简化了部署和发布的操作,无须事先安装运行环境和下载诸多第三方库。不过最新版本却又加入了动态链接的内容了。

普通的 go build 、go install 用的都是静态链接。

目前最新版的 Go 是如何支持动态链接的呢?

在 go build 、go install 的时候加上 -buildmode 参数。

参考

初探 Go 的编译命令执行过程