go程序启动过程
[[toc]]
一、go启动流程
1.1 第一个go程序
- 首先,我们来看一个简单的go程序启动流程
1 | // main.go |
- 对上面的go程序进行编译,生成main
1 | go build -gcflags="-l -N" # -l 禁止内联, -N 禁止优化 |
- 查看编译出来的二进制和汇编代码
1 | go tool objdump -s "main.main" -S ./main # 用户实现的main函数,在源码中是main.main |
该方法在控制台输出汇编代码,阅读和调试起来不方便,我们可以用lensm工具查看汇编代码
1.2 生产力工具lensm
为了方便看汇编和源代码,在go源码之间方便跳转,我们可以安装一个可交互的源码分析器:lensm,通过lensm可以方便地将编译出来的golang可执行文件映射到源码中,并点击某个函数就可以跳转到对应实现上面
- git地址:
https://github.com/loov/lensm - 如下进行安装:
1
go install loov.dev/lensm@main
- 利用该工具分析上面生成的二进制文件的源码
1
2lensm -watch main
# lensm -watch -filter main main # -filter用来直接过滤到main函数,也可以在页面内选择 - 该二进制文件用lensm打开如下:
1.3 启动流程
在lensm的左上角的搜索栏可以搜索到,go进程的入口函数(在mac M1平台)是_rt0_arm64_darwin(SB),对应代码段如下:
1 | // src/runtime/rt0_darwin_arm64.s |
其实,无论是哪个平台启动go程序,最终都会跳转到runtime.rt0_go(SB),只是在不同平台该函数实现不同,lensm中搜索该函数,跳转到具体平台的具体实现文件之中
1 | // src/runtime/asm_arm64.s |
从rt0_go函数可知,初始化协程调度器之后,我们首先构造首个协程(即执行main函数的协程)所需的参数,将runtime.mainPC函数作为参数,调用创建协程的函数runtime.newproc(fn *funcval)来创建首个协程,该协程的运行函数为runtime.mainPC,即runtime.main函数,我们跳转到runtime.main函数中继续分析
1 | // src/rumtime/proc.go |
可以看到,在runtime.main函数中执行了用户实现的main.main函数,至此,整个go进程启动逻辑就很清晰了,整个启动过程核心步骤如下图所示
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 爱开源GoGo!