前面大概说过bison与yacc的语法,那么其实go也是用bison来实现的,下面来说说怎么阅读go语言源代码。要看代码首先得找到代码的入口,怎么找呢?你可能首先想到grep main是的,一般来说这是一个没有争议的办法,但是对于go语言来说,这个办法对,也不对。怎么说呢?我们用gdb来进行debug就会看到所有的go语言程序的入口居然都在src/lib9/main.c里的main函数。
#include <u.h> #define NOPLAN9DEFINES #include <libc.h> extern void p9main(int, char**); int main(int argc, char **argv) { p9main(argc, argv); exits("main"); return 99; }
从go/include/libc.h中里我们可以看到这么一句
#ifndef NOPLAN9DEFINES #define main p9main #endif
也就是说,在go语言源代码里main函数都重命名了,所有的函数入口都变成了src/lib9/mina.c,也实际的入口各自的代码里,因此说入口是各自的main这句话,对也不对。
那么先大概说一下go语言的代码结构
. |-- bin//我自己定义的安装目录 |-- doc//相关文档,godoc就是利用这个目录 | |-- codelab | | `-- wiki | |-- codewalk | |-- devel | |-- gopher | |-- progs | `-- talks | `-- io2010 |-- include//一些全局的定义文件 |-- lib//一些lib9相关的全局库 | |-- codereview | `-- godoc |-- misc//一些工具 | |-- arm | |-- bash | |-- bbedit | |-- cgo | | |-- gmp | | |-- life | | `-- stdio | |-- dashboard | | `-- godashboard | |-- emacs | |-- kate | |-- nacl | |-- vim | | |-- ftdetect | | `-- syntax | `-- xcode |-- pkg//编译好的go库 | `-- linux_amd64 | |-- archive | |-- compress | |-- container | |-- crypto | |-- debug | |-- encoding | |-- exp | |-- go | |-- hash | |-- http | |-- image | |-- io | |-- os | |-- rpc | |-- runtime | `-- testing |-- src | |-- cmd//go语言相关的程序 | | |-- 5a | | |-- 5c | | |-- 5g | | |-- 5l | | |-- 6a//amd64的汇编器 | | |-- 6c//amd64的c编译器 | | |-- 6g//amd64的编译器 | | |-- 6l//amd64的链接器 | | |-- 8a | | |-- 8c | | |-- 8g | | |-- 8l | | |-- cc | | |-- cgo | | |-- cov | | |-- ebnflint | | |-- gc | | |-- godefs | | |-- godoc | | |-- gofmt | | |-- goinstall | | |-- gopack | | |-- gotest | | |-- goyacc | | |-- hgpatch | | |-- ld | | |-- nm | | `-- prof | |-- lib9//lib9相关的库 | | |-- fmt | | `-- utf | |-- libbio//全局库 | |-- libcgo//cgo的库 | |-- libmach//库 | `-- pkg//go库 | |-- archive | |-- asn1 | |-- big | |-- bufio | |-- bytes | |-- cmath | |-- compress | |-- container | |-- crypto | |-- debug | |-- ebnf | |-- encoding | |-- exec | |-- exp | |-- expvar | |-- flag | |-- fmt | |-- go | |-- gob | |-- hash | |-- html | |-- http | |-- image | |-- io | |-- json | |-- log | |-- math | |-- mime | |-- net | |-- netchan | |-- nntp | |-- once | |-- os | |-- patch | |-- path | |-- rand | |-- reflect | |-- regexp | |-- rpc | |-- runtime | |-- scanner | |-- sort | |-- strconv | |-- strings | |-- sync | |-- syscall | |-- syslog | |-- tabwriter | |-- template | |-- testing | |-- time | |-- unicode | |-- unsafe | |-- utf16 | |-- utf8 | |-- websocket | `-- xml `-- test//测试 |-- bench |-- bugs |-- chan |-- fixedbugs | |-- bug083.dir | |-- bug088.dir | |-- bug106.dir | |-- bug133.dir | |-- bug160.dir | |-- bug191.dir | |-- bug222.dir | |-- bug226.dir | |-- bug248.dir | `-- bug282.dir |-- garbage |-- interface |-- ken |-- nilptr `-- syntax
我们先来看一个go语言的hello world文件hello.go
package main func main(){ println("Hello World"); }
那么这个文件要被编译链接成一个可执行文件,那么要执行如下命令:
6g hello.o 6l hello.6 -o hello
很显然最关键的就是6g和6l两个程序,那么我们就从6g开始
首先gdb -tui打开tui界面的gdb程序
然后输入file 6g载入6g程序,就可以看到
/home/hoping/go/src/lib9/main.c,然后输入start后,step进入了/home/hoping/go/src/cmd/gc/lex.c的main函数
因此我们只需要看这个文件就大概可以看到程序的执行流程了。接下来暂时不需要gdb了,我们换vi和ctags。
在go目录执行ctags -R *来生成tag,然后用vi打开lex.c进入main函数,其实即使你看了这个函数也不会有太多收获。
正如我们前面说过的那样,6g是用bison的,那么显然我们需要找到bison文件和yylex函数即可。既然入口在gc文件夹,那么猜想y文件也在gc文件夹,于是进入ls *.y之后可以看到go.y。而go.y的输入全是由yylex函数来提供的。所以首先我们来看看yylex函数,在go文件夹下执行vim -t yylex,然后选择gc文件夹那个。