Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go编译运行

图文详解Go程序如何编译并运行起来的

作者:RdrB1te

Go语言这两年在语言排行榜上的上升势头非常猛,Go语言虽然是静态编译型语言,但是它却拥有脚本化的语法,下面这篇文章主要给大家介绍了关于Go程序如何编译并运行起来的相关资料,需要的朋友可以参考下

Go程序是如何编译的

从hello RdrB1te开始

package main  
  
import "fmt"  
  
func main() {  
   fmt.Println("hello RdrB1te")  
}

不实际编译它,只输出它的编译过程:

go build -n

简单的编译过程分析:

上面的过程确认了两个事情:

Go 编译过程

词法分析

句法分析

语义分析

中间码生成:

查看从代码到中间码(SSA)生成的整个过程

$env:GOSSAFUNC="main" # windows powershell
export GOSSAFUNC=main # linux
go build

会看到如下输出:

用浏览器打开ssa.html文件:

sources就是你的源代码,AST就是生成的语法树,genssa就是生成的与平台无关的中间码SSA,当然中间还有很多的其它步骤,这里不再列举,可以点击展开查看

机器码生成:

查看Plan9汇编代码

go build -gcflags -S main.go

链接:

Go程序是如何运行起来的

Go程序的入口?

是下面的main方法吗?当然不是

func main() {  
   fmt.Println("hello RdrB1te")  
}

是runtime包下面的rt0_xxx.s文件,下面以Linux x86芯片架构上面运行的rt0_linux-amd64.s举例:

TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
	JMP	_rt0_amd64(SB)

只要用了x86芯片架构都要进入到_rt0_amd64这个方法中去,这个方法调到了哪里呢,选中双击shift,打开在文件中查找:找到下面这行

asm_amd64.s这个文件中的这段代码:

TEXT _rt0_amd64(SB),NOSPLIT,$-8  
   MOVQ   0(SP), DI  // argc  
   LEAQ   8(SP), SI  // argv  
   JMP    runtime·rt0_go(SB)

意思是读取命令行参数,复制参数数量argc和参数值argv到栈上,然后调用了runtime·rt0_go这个方法,这方法的位置就在这个文件的下面:

TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0  
   // copy arguments forward on an even stack  
   MOVQ   DI, AX    // argc  
   MOVQ   SI, BX    // argv  
   SUBQ   $(5*8), SP    // 3args 2auto  
   ANDQ   $~15, SP  
   MOVQ   AX, 24(SP)  
   MOVQ   BX, 32(SP)  
  
   // create istack out of the given (operating system) stack.  
   // _cgo_init may update stackguard.   MOVQ   $runtime·g0(SB), DI

上面这段的意思时初始化g0执行栈,g0是为了调度协程而产生的协程,g0是每个Go程序的第一个协程。继续往下面看,找到下面这段:

	CALL	runtime·check(SB)

这行是第一次调用的go语言方法,要找到这个方法可以选中双击shift,找到下面这行:

进入:

func check(){

}

check方法主要是做运行时检测:

继续往下看,可以通过Ctrl+Alt+左右箭头进行快速跳转回退或前进,退到这个位置:

CALL    runtime·check(SB)  
  
MOVL   24(SP), AX    // copy argc  
MOVL   AX, 0(SP)  
MOVQ   32(SP), AX    // copy argv  
MOVQ   AX, 8(SP)  
CALL   runtime·args(SB)  
CALL   runtime·osinit(SB)  
CALL   runtime·schedinit(SB)  
  
// create a new goroutine to start program  
MOVQ   $runtime·mainPC(SB), AX       // entry  
PUSHQ  AX  
CALL   runtime·newproc(SB)  
POPQ   AX

runtime·args(SB):参数初始化runtime.args,对命令行中的参数进行处理,参数数量赋值给argc int32,参数值复制给argv **byteruntime·osinit:判断操作系统,执行相应的初始化组件,供调度器初始化所用
runtime·schedinit: 初始化Go调度器。初始化调度器会做哪些事情:

继续往下看:

    // create a new goroutine to start program  
   MOVQ   $runtime·mainPC(SB), AX       // entry  
   PUSHQ  AX  
   CALL   runtime·newproc(SB)  
   POPQ   AX  
  
   // start this M  
   CALL   runtime·mstart(SB)  
  
   CALL   runtime·abort(SB)  // mstart should never return  
   RET  
  
// mainPC is a function value for runtime.main, to be passed to newproc.  
// The reference to runtime.main is made via ABIInternal, since the  
// actual function (not the ABI0 wrapper) is needed by newproc.  
DATA   runtime·mainPC+0(SB)/8,$runtime·main<ABIInternal>(SB)

MOVQ $runtime·mainPC(SB):取mainPC的地址,这个mainPC的地址就是runtime·main这个方法的地址
CALL runtime·newproc:创建一个新的协程(主协程),执行runtime·main这个方法(主函数),放入调度器等待调度
CALL runtime·mstart(SB):初始化一个M,用来调度主协程,主协程开始执行主函数。

看下runtime·main这个方法里面干了什么,选中双击shift,找到下面这行:

进入:

// The main goroutine.
func main() {  
   doInit(&runtime_inittask) // 执行runtime包中的init方法
   gcenable() // 启动GC垃圾回收器
   doInit(&main_inittask) //执行用户包依赖的init方法
   fn := main_main // 执行用户主函数main.mian() 
   fn()
}

按住ctrl进入main_main:

//go:linkname main_main main.main
func main_main()

主协程执行主函数:

总结

到此这篇关于Go程序如何编译并运行起来的文章就介绍到这了,更多相关Go编译运行内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文