[TOC] [https://github.com/golang](https://github.com/golang) 手册 [https://cloud.tencent.com/developer/section/1142075](https://cloud.tencent.com/developer/section/1142075) 下载地址 [http://mirrors.ustc.edu.cn/golang/](http://mirrors.ustc.edu.cn/golang/) # 优势 * 可以直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去 * 跨平台编译,如果你写的代码不包含cgo,那么就可以做到windows系统编译linux的应用.go引用了plan9的代码,这就是不依赖系统的信息 * 内嵌c支持,Go里面也可以直接包含c代码,利用现有的丰富c库 自动搜索所有函数并集中到一个.a文件 生成library时候是会自动把`$GOPATH/src/github.com/cyent/golang/example/stringutil/`下面所有.go文件里包含的函数集中到`stringutil.a`中(根据这些.go文件中的package xxx来自动加入到一个.a),因此如果存在相同的函数,会报错提示重复 # 学习资料 go语言官网: https://golang.org/ go中文社区: https://studygolang.com/ go中文在线文档: https://studygolang.com/pkgdoc # 命令源码文件 如果一个源码文件声明属于main包,并且包含一个无参数声明且无结果声明的main函数,那么它就是命令源码文件 ~~~ package main import "fmt" func main() { fmt.Println("Hello, world!") } ~~~ # 命令行 * 编译go代码,生成一个可执行程序 ~~~ go build xxx.go ~~~ 然后这个程序就可以执行了 * 不生成可执行文件,直接运行 ~~~ go run xxx.go ~~~ * `go install` 配置GOPATH到src同级的绝对目录`src/xx` 需要配置GOBIN 到src目录执行go install就行,会生成2个目录在src同级 自动生成pkg和bin pkg放平台,bin放可执行程序 # 环境变量 * `src`存放源代码(比如:.go .c .h .s等)   按照golang默认约定,go run,go install等命令的当前工作路径(即在此路径下执行上述命令)。 * `pkg`编译时生成的中间文件(比如:.a)  golang编译包时 * `bin`编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中,如果有多个gopath,那么使用`${GOPATH//://bin:}/bin`添加所有的bin目录) 代码目录结构: GOPATH下的src目录就是接下来开发程序的主要目录,所有的源码都是放在这个目录下面,那么一般我们的做法就是一个目录一个项目, 例如: $GOPATH/src/mymath 表示mymath这个应用包或者可执行应用,这个根据package是main还是其他来决定,main的话就是可执行应用,其他的话就是应用包,这个会在后续详细介绍package `go install/go get`和 go的工具等会用到GOPATH环境变量 goroot在上面 ~~~ export GOPATH=/Users/hopkings/www/Go export GOBIN=$GOPATH/bin export PATH=$PATH:$GOBIN ~~~ **go get** 会做两件事: 1. 从远程下载需要用到的包 2. 执行go install `go get = git clone + go install` 从指定源上面下载或者更新指定的代码和依赖,并对他们进行编译和安装 **go install** go install 会生成可执行文件直接放到bin目录下,当然这是有前提的 你编译的是可执行文件,如果是一个普通的包,会被编译生成到pkg目录下该文件是.a结尾 # go build 1. 运行go build命令时加入标记-x,这样可以看到go build命令具体都执行了哪些操作。另外也可以加入-n,只查看具体操作不执行他们 2. 运行go build命令加入标记-v,这样可以看到go build命令编译源码包的名称,和-a标记搭配很有用 3. 执行该命令而且不加参数,会试图把当前目录作为代码包编译. `-a`所有涉及到代码包都被编译,不加只会编译归档文件而不是最新的代码包 # go get 从github下载后安装到GOPATH第一个工作区的相应目录中.如果存在GOBIN,那么仅包含命令元am文件代码会装到GOBIN那 -u: 下载并安装代码包,不论是否存在 -d: 只下载不安装 -fix: 下载代码包后先运行一个用于根据当前go版本修正代码的工具,然后再安装代码包 -t: **同时下载测试所需的代码包** -insecure: 允许通过非安全的网络协议下载,比如http **uses insecure protocol** ~~~ export url='' export project='' git config --global url."git@${url}:".insteadOf "http://${url}/" go get -v -insecure "${url}${project}" ~~~ # go env ![](https://box.kancloud.cn/305b2452adbacf4e2dfdaa6087f4990f_804x728.png) # 查看编译的依赖 mac上没有ldd和strace mac对应的是otool和strace,需要root 查看动态链接库`otool -L` ~~~ ldd 可执行文件 ~~~ ![](https://box.kancloud.cn/722e10006afbec8b3bad4a20439aa565_1350x582.png) 上图中的三列数据分别代表: 1. 第一列:程序依赖的库 2. 第二列:系统提供的对应库 3. 第三列:库加载的开始地址 通过对比第一列和第二列数据,可以分析程序依赖的库和系统实际提供的库,看两者是否相匹配。 通过第三列数据,可以知道在当前的库中的符号在对应的进程的地址空间中的开始位置 # 交叉编译 ![](https://box.kancloud.cn/284d319c47a6d8154bdf737056ae901c_1268x404.png) 有效的`$GOOS`和`$GOARCH`组合如下 ~~~ $GOOS $GOARCH android arm darwin 386 darwin amd64 darwin arm darwin arm64 dragonfly amd64 freebsd 386 freebsd amd64 freebsd arm linux 386 linux amd64 linux arm linux arm64 linux ppc64 linux ppc64le linux mips linux mipsle linux mips64 linux mips64le netbsd 386 netbsd amd64 netbsd arm openbsd 386 openbsd amd64 openbsd arm plan9 386 plan9 amd64 solaris amd64 windows 386 windows amd64 ~~~ 交叉编译主要是两个编译环境参数 `$GOOS` 和 `$GOARCH` 的设定 `$GOOS`代表编译的目标系统,`$GOARCH`代表编译的处理器体系结构 `$GOOS`可选值如下 ~~~ darwin dragonfly freebsd linux netbsd openbsd plan9 solaris windows ~~~ `$GOARCH`可选值如下 ~~~ 386 amd64 arm ~~~ Mac 下编译 Linux 和 Windows 64位可执行程序 ~~~ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go ~~~ 在Linux系统下跨平台编译 ~~~shell CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build xxx.go ~~~ 在Windows系统下跨平台编译 ~~~shell set CGO_ENABLED=0 set GOARCH=386 set GOOS=windows go build xxx.go ~~~ 选择性编译 虽然golang 可以跨平台编译,但却无法解决系统的差异性。总在一些时候我们会直接调用操作系统函数。相同功能编写类似xxx_windows.go xxx.Linux.go文件,根据操作系统编译对应源文件。而不是在文件中用if else规划执行路径。 要实现选择性编译需要在文件顶部增加构建标记。 ~~~ // +build ~~~ 此标记必须出现在文件顶部,仅由空行或其他注释行开头。也就是必须在Package 语句前。此标记后接约束参数,格式为 // +build A,B !C,D 逗号为且,空格为或,!为非。代表编译此文件需符合 (A且B) 或 ((非C)且D) 。A和C的可选参数可参见本文上面的 $GOOS参数,B和D的可选参数可参见$GOARCH 。比如 ~~~ // +build !windows,386 //此文件在非windows操作系统 且386处理器时编译 ~~~ 最后, Golang为跨系统运行的确是已经做得够好了。没有C C++的历史包袱,但充分吸取了他们的诸多特点,为加快工作效率损失的软件性能是值得的 # 编译器 在网络上的诸多教程中可能会看到下面的编译命令 ~~~ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build hello.go ~~~ 其中CGO\_ENABLED=0的意思是使用C语言版本的GO编译器,参数配置为0的时候就关闭C语言版本的编译器了。自从golang1.5以后go就使用go语言编译器进行编译了。在golang1.9当中没有使用CGO\_ENABLED参数发现依然可以正常编译。当然使用了也可以正常编译。比如把CGO\_ENABLED参数设置成1,即在编译的过程当中使用CGO编译器,我发现依然是可以正常编译的。 实际上如果在go当中使用了C的库,比如`import "C"`默认使用go build的时候就会启动CGO编译器,当然我们可以使用CGO\_ENABLED=0来控制go build是否使用CGO编译器。 # GOPROXY ~~~ export GOPROXY=https://goproxy.io ~~~ or ~~~ $env:GOPROXY = "https://goproxy.io" ~~~ ![](https://box.kancloud.cn/ee6a7034d5a6cc9d1e0d3023b0d50987_964x474.png) # 压缩可执行文件 首先加上编译参数`-ldflags` `-s`相当于strip掉符号表, 但是以后就没办法在gdb里查看行号和文件了。 `-w` 告知连接器放弃所有debug信息 ~~~ $ go build -ldflags '-w -s' ~~~ 使用upx压缩,Linux、Mac和Win都有,这里以Mac为例 upx就是对可执行文件进行压缩,然后可以已极快的速度解压并运行 ~~~ $ brew install upx $ upx etcd-cli ~~~ Golang开发的程序都会比较大,这是因为Golang是静态编译的,编译打包之后基本就不会再对其他类库有依赖了,所以会比较大。举个例子:C++程序可以调用dll,所以打包的时候可以不把dll打进去,包自然就小了。之前还有看到过有人使用`GO -> C -- dll --> C -> GO`的方式间接实现了Golang的伪动态链接 # go cache 通过运行`go env GOCACHE`命令来查看缓存目录的路径。缓存的数据总是能够正确地反映出当时的各种源码文件、构建环境、编译器选项等等的真实情况。 一旦有任何变动,缓存数据就会失效,go 命令就会再次真正地执行操作。所以我们并不用担心打印出的缓存数据不是实时的结果。go 命令会定期地删除最近未使用的缓存数据,但是,如果你想手动删除所有的缓存数据,运行一下`go clean -cache`命令就好了 # goland调试 菜单依次选择“Run”->“Edit configurations”如下图所示: ![](https://box.kancloud.cn/ad7b0561ffefc65d8219dc39324116e7_431x329.png) ![](https://box.kancloud.cn/47da02127e84f69d4eabf1ee1117482e_1098x583.png)