這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
前面大概說過bison與yacc的文法,那麼其實go也是用bison來實現的,下面來說說怎麼閱讀go語言原始碼。要看代碼首先得找到代碼的入口,怎麼找呢?你可能首先想到grep main是的,一般來說這是一個沒有爭議的辦法,但是對於go語言來說,這個辦法對,也不對。怎麼說呢?我們用gdb來進行debug就會看到所有的go語言程式的入口居然都在src/lib9/main.c裡的main函數。
#include #define NOPLAN9DEFINES#include extern void p9main(int, char**);intmain(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 mainfunc main(){ println("Hello World");}
那麼這個檔案要被編譯連結成一個可執行檔,那麼要執行如下命令:
6g hello.o6l 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檔案夾那個。