研究Go的内部实现,这里介绍一些基本的技巧。
Go语言的源代码布局是有一些规律的。假定读者在$GOROOT下:
- ./misc 一些工具 - ./src 源代码 - ./src/cmd 命令工具,包括6c, 6l, 6g等等。最后打包成go命令。 - ./src/pkg 各个package的源代码 - ./src/pkg/runtime Go的runtime包,本书分析的最主要的部分 - AUTHORS — 文件,官方 Go语言作者列表 |– CONTRIBUTORS — 文件,第三方贡献者列表 |– LICENSE — 文件,Go语言发布授权协议 |– PATENTS — 文件,专利 |– README — 文件,README文件,大家懂的。提一下,经常有人说:Go官网打不开啊,怎么办?其实,在README中说到了这个。该文件还提到,如果通过二进制安装,需要设置GOROOT环境变量;如果你将Go放在了/usr/local/go中,则可以不设置该环境变量(Windows下是C:\go)。当然,建议不管什么时候都设置GOROOT。另外,确保$GOROOT/bin在PATH目录中。 |– VERSION — 文件,当前Go版本 |– api — 目录,包含所有API列表,方便IDE使用 |– doc — 目录,Go语言的各种文档,官网上有的,这里基本会有,这也就是为什么说可以本地搭建”官网”。这里面有不少其他资源,比如gopher图标之类的。 |– favicon.ico — 文件,官网logo |– include — 目录,Go 基本工具依赖的库的头文件 |– lib — 目录,文档模板 |– misc — 目录,其他的一些工具,相当于大杂烩,大部分是各种编辑器的Go语言支持,还有cgo的例子等 |– robots.txt — 文件,搜索引擎robots文件 |– src — 目录,Go语言源码:基本工具(编译器等)、标准库 `– test — 目录,包含很多测试程序(并非_test.go方式的单元测试,而是包含main包的测试),包括一些fixbug测试。可以通过这个学到一些特性的使用。
学习Go语言的内部实现,主要依靠对源代码的分析,所以阅读源代码是很好的方式。linus谈到如何学习Linux内核时也说过"Read the F**ing Source code"。
通过gdb下断点,跟踪程序的行为。调试跟代码的方式是源代码阅读的一种辅助手段。
用户代码入口是在main.main,runtime库中的函数可以通过runtime.XXX断点捕获。比如写一个test.go:
package main import ( "fmt" ) func main() { fmt.Println("hello world!") }
编译,调试
go build test.go gdb test
可以在main.main处下断点,单步执行,你会发现进入了一个runtime·convT2E的函数。这个就是由于fmt.Println接受的是一个interface,而传入的是一个string,这里会做一个转换。以这个为一个突破点去跟代码,就可以研究Go语言中具体类型如何转为interface抽象类型。
有时候分析会需要研究生成的汇编代码,这里介绍生成汇编代码的方法。
go tool 6g -S hello.go
-S参数表示打印出汇编代码,更多参数可以通过-h参数查看。
go tool 6g -h
或者可以反汇编生成的可执行文件:
go build test.go go tool 6l -a test | less
本机是amd64的机器,如果是i386的机器,则命令是8g
需要注意的是用6g的-S生成的汇编代码和6l -a生成的反汇编代码是不太一样的。前者是直接对源代码进行汇编,后者是对可执行文件进行反汇编。在6l进行链接过程中,可能会在原汇编文件基础上插入新的指令。所以6l反汇编出来的是最接近真实代码的。
不过Go的汇编语法跟常用的有点不太一致,可能读起来会不太习惯。还有另一种方式,就是在用gdb调试的过程中查看汇编。
gdb test b main.main disas
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8