go run main.go
一个 Go 程序就启动了。然而这背后操作系统如何执行到 Go 代码的,Go 为了运行用户 main 函数,又做了什么?
一 编译
go build main.go
我们写的 go 代码都是编译成可执行文件去机器上直接执行的,在 linux 平台上是 ELF 格式的可执行文件,linux 能直接执行这个文件。
- 编译器:将 go 代码生成 .s 汇编代码,go 中使用的是 plan9 汇编
- 汇编起:将汇编代码转成机器代码,即目标程序 .o 文件
- 链接器:将多个 .o 文件合并链接得到最终可执行文件
go程序汇编代码.o目标程序可执行文件写代码编译器汇编器链接器结束
二 操作系统加载
./main
经上述几个步骤生成可执行文件后,二进制文件在被操作系统加载起来运行时会经过如下几个阶段:
- 从磁盘上把可执行程序读入内存;
- 创建进程和主线程;
- 为主线程分配栈空间;
- 把由用户在命令行输入的参数拷贝到主线程的栈;
- 把主线程放入操作系统的运行队列等待被调度执起来运行;
START_THREAD(elf_ex, regs, elf_entry, bprm->p)
启动线程传入了 elf_entry
参数,这是程序的入口地址。
这个 elf_entry
被写在 elf 可执行文件的 header 中
1 | bash复制代码$ readelf -h main |
并且通过反编译 可执行文件,可以看到这个地址对应的就是 _rt0_amd64_linux。
1 | shell复制代码$ objdump ./main -D > tmp |
从此进入了 Go 程序的启动过程
Go 程序启动
Go 程序启动位置, 把栈上的入口参数存到寄存器中,接下来跳转到 rt0_go
启动函数
1 | scss复制代码TEXT _rt0_amd64(SB),NOSPLIT,$-8 |
rt0_go
代码比较长,可分为两个部分,第一部分是系统参数获取和运行时检查。第二部分是 go 程序启动的核心,这里只详细介绍第二部分,总体启动流程如下
rt0_gogo runtime 核心schedinitnewprocmstartruntime.main入口参数复制tls 线程本地存储初始化,关联当前线程,m0与g0check:运行时类型检查args:argc,argv,内存物理页系统参数的获取osinit:cpu核心获取schedinit:调度器初始化newproc:创建新的 goroutinemstart:开始调度栈的初始化堆内存分配的初始化当前G系统线程初始化垃圾回收器参数初始化通过cpu核心数和gomaxprocs创建P创建新的协程,入口地址 runtime.main放到P的队列中,等待调度初始化M,开始调度调度获取到newproc创建的G, 入口方法:runtime.main新建sysmon监视线程执行所有runtime init方法启动 gc 收集器执行所有用户包依赖的init方法执行main.main
go runtime 核心:
schedinit
:进行各种运行时组件初始化工作,这包括我们的调度器与内存分配器、回收器的初始化newproc
:负责根据主 goroutine(即 main)入口地址创建可被运行时调度的执行单元,这里的main还不是用户的main函数,是runtime.main
mstart
:开始启动调度器的调度循环, 执行队列中 入口方法是runtime.main
的 G
1 | c复制代码TEXT runtime·rt0_go(SB),NOSPLIT,$0 |
shedinit
包括了所有核心组件的初始化工作
1 | go复制代码// src/runtime/proc.go |
执行 runtime.main
, 主要进行了
- 启动系统后台监控sysmon 线程
- 执行 runtime 包内 init
- 启动gc
- 用户包依赖 init 的执行
- 执行用户main.mian 方法
1 | go复制代码// The main goroutine. |
总结
启动一个 Go 程序时,首先要经过操作系统的加载,通过可执行文件中 Entry point address
记录的地址,找到 go 程序启动入口: _rt0_amd64
-> rt0_go
。rt0_go
中 先进行了 go 程序的 runtime 的初始化,其中包括:调度器,栈,堆内存空间初始化,垃圾回收器的初始化,最后最后通过newproc
和mstart
调度执行runtime.main
,完成一系列初始化过程,再然后才是执行用户的主函数。
参考
- www.bookstack.cn/read/qcrao-…
- eddycjy.com/posts/go/go…
- loulan.me/post/golang…
- juejin.cn/post/694250…
本文转载自: 掘金