Go program 编译过程
   
package main
import "fmt"
func main() {
 fmt.Println("Hello Go")
}
    
     编译前端
    
   
- 
     词法分析
- 将源代码翻译成 Token(最小语义结构)
 
- 
     句法分析
- Token 序列经过处理,变成语法树 AST
 
- 
     语义分析
- 类型检查
- 类型推断
- 函数调用内联
- 逃逸分析
 
    
     编译后端
    
   
- 
     中间代码生成(SSA)
- 处理不同平台的差异,生成中间代码
 
- 代码优化
- 
     机器码生成
- 生成 Plan9 汇编代码
 
- 
     链接
- 将各个包(.a 文件)进行链接,包括 runtime
 
    
    
    查看从代码到 SSA 中间码的过程
   
# windows
$env:GOSSAFUNC="main"
# linux
export GOSSAFUNC="main"
    执行
    
     go build main.go
    
    ,得到 ssa.html 文件。
    
    
    
    
    
    从 sourses -> ast -> … -> genssa
   
    
    
    查看 Plan9 汇编代码
   
go build -gcflags -S main.go
     
   
    
    
    Go program 的入口
   
tips: 第一行代码并非为用户自定义的 main 方法
- 
     首先进入
 
 %GOROOT%\src\runtime
 
 下的对应的
 
 rt0_xxx.s
 
 汇编文件,如在 windows 系统下,进入
 
 rt0_windows_amd64.s
 
 ,执行
 
 JMP _rt0_amd64(SB)
 
 :
 
  
 
   
- 
     调用
 
 _rt0_amd64(SB)
 
 方法,该方法位于
 
 %GOROOT%\src\runtime\asm_amd64.s
 
 :
 
   
- 
     该方法将调用命令参数 argc,argc 放入寄存器,随后调用
 
 runtime·rt0_go(SB)
 
 :
 
   
- 读取命令行参数。复制参数数量 argc 和参数值 argv 到栈上,参数可能有也可能没有,取决于启动程序时参数是否输入。
- 
     初始化
 
 g0
 
 执行栈。启动
 
 g0
 
 协程,
 
 g0
 
 是 Go 程序的第一个协程,且不归调度器管理,是为了调度协程而产生的协程。
- 
     经过一系列判断后,开始运行时检测
 
 runtime.check
 
 ,该检测方法位于
 
 %GOROOT%\src\runtime\runtime1.go
 
 。主要检测包括:
 
   - 检查各种类型的长度
- 检查指针操作
- 检查结构体字段的偏移量
- 检查 atomic 原子操作
- 检查 CAS 操作
- 检查栈大小是否为 2 的幂次
 
- 初始化 runtime.args。对命令行中的参数进行处理,拷贝参数到 go 代码,参数数量赋值给 argc int32,参数值赋值给 argv **byte。
- 
     执行
 
 runtime·osinit(SB)
 
 判断 cpu 核数,用于后续调度器的初始化
 
 runtime·schedinit(SB)
 
 :- 全局占空间内存分配
- 加载命令行参数到 os.Args
- 堆内存空间的初始化
- 加载操作系统环境变量
- 初始化当前系统线程
- 垃圾回收器的参数初始化
- 算法初始化
- 设置 process 数量
 
- 
     取 runtime 主函数地址
 
 MOVQ $runtime·mainPC(SB), AX
 
 ,创建启动主协程
 
 runtime·newproc(SB)
 
 ,初始化调度器 M
 
 runtime·mstart(SB)
 
 ,调度主协程用于运行 runtime.main,runtime 的 main 方法位于
 
 %GOROOT%\src\runtime\proc.go
 
 。
- 
     主协程执行主函数。执行 runtime 中的 init 方法,启动垃圾回收器,执行用户包依赖的 init 方法,最后执行用户自定义的主函数 main.main():
 
  
 
   
 
版权声明:本文为by6671715原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
