31

Go程序启动过程 - OverStack.Me

 3 years ago
source link: https://overstack.me/202007/go-program-startup-process.html?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文基于 go version go1.14.3 darwin/amd64, 不同操作系统启动流程有差异

Go语言启动时首先需要初始化自己的运行时(runtime), 入口文件为 src/runtime/rt0_darwin_amd64.s

1
2
TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8
JMP _rt0_amd64(SB)

紧接着,跳转到 src/runtime/asm_amd64.s 中的 _rt0_amd64 函数.

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

函数前两行指令将 argc, argc 两个操作系统传入的参数分别存储到了 DI, SI 寄存器中,然后跳转到 runtime·rt0_go 中继续执行.

初始化函数

runtime·rt0_go 函数完成了go启动时所需的所有初始化工作

参数初始化

1
2
3
4
5
6
7
// copy arguments forward on an even stack
MOVQ DI, AX // argc
MOVQ SI, BX // argv
SUBQ $(4*8+7), SP // 2args 2auto
ANDQ $~15, SP
MOVQ AX, 16(SP)
MOVQ BX, 24(SP)

将存储的argc, argv 参数分别放到 AX, BX 寄存器中,同时减小栈指针且将栈指针进行16位对齐, 最后将参数从寄存器放回栈中.

初始化g0栈信息

1
2
3
4
5
6
7
8
// create istack out of the given (operating system) stack.
// _cgo_init may update stackguard.
MOVQ $runtime·g0(SB), DI
LEAQ (-64*1024+104)(SP), BX
MOVQ BX, g_stackguard0(DI)
MOVQ BX, g_stackguard1(DI)
MOVQ BX, (g_stack+stack_lo)(DI)
MOVQ SP, (g_stack+stack_hi)(DI)

此部分程序首先将全局变量 g0 地址存入寄存器 DI 中, 然后在系统线程的栈中为 g0 申请栈空间(64*1024 + 104),初始化g0的栈信息和stackgard.

CPU信息相关

1
2
3
4
5
6
7
// find out information about the processor we're on
MOVL $0, AX
CPUID
MOVL AX, SI
CMPL AX, $0
JE nocpuinfo
(...)

这段代码主要是与CPU相关的查找和初始化

TLS 与 m0 g0

1
2
3
4
5
6
7
8
9
10
LEAQ	runtime·m0+m_tls(SB), DI
CALL runtime·settls(SB)

// store through it, to make sure it works
get_tls(BX)
MOVQ $0x123, g(BX)
MOVQ runtime·m0+m_tls(SB), AX
CMPQ AX, $0x123
JEQ 2(PC)
CALL runtime·abort(SB)

调用 runtime·settls 函数来初始化主线程的TLS,目的是把 m0 与主线程进行关联,并检查是否正常执行.

1
2
3
4
5
6
7
8
9
get_tls(BX)
LEAQ runtime·g0(SB), CX
MOVQ CX, g(BX) // 将g0地址保存到TLS中
LEAQ runtime·m0(SB), AX

// save m->g0 = g0
MOVQ CX, m_g0(AX)
// save m0 to g0->m
MOVQ AX, g_m(CX)

将TLS地址加载到 BX 寄存器中, 将 g0 的地址保存到TLS中, 也就是 m0.tls[0]=&g0

通过 m0.g0 = &g0, g0.m = &m0m0g0 进行关联, 这样之后在主线程中通过 get_tls 就可以找到 g0, 通过 g0.m 就可以找到 m0 , 这样就实现了 m0g0 与主线程之间的关联.

运行时类型检查

1
CALL	runtime·check(SB)

check 函数主要是进行运行时类型检查, 源码在 src/runtime/runtime1.go 中,可自行参阅.

调度器相关

1
CALL	runtime·schedinit(SB)

schedinit 函数进行运行时组件的初始化工作,主要包括:

  • 栈、内存分配器、调度器相关初始化
  • 限制最大系统线程数量
  • 初始化执行栈
  • 初始化内存分配器
  • 初始化当前系统线程
  • 垃圾回收器初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(...)

// create a new goroutine to start program
MOVQ $runtime·mainPC(SB), AX // entry
PUSHQ AX
PUSHQ $0 // arg size
CALL runtime·newproc(SB)
POPQ AX
POPQ AX

// start this M
CALL runtime·mstart(SB)

CALL runtime·abort(SB) // mstart should never return
RET
(...)

调用 runtime·newproc 函数创建一个新的 goroutine 来启动 runtime.mainPC 所指向的函数 runtime.main.

runtime·mstart 启动调度器的循环调度并对刚刚创建的 goroutine 进行调度.

runtime.main

runtime.main 函数中会加载用户main函数 main.main, 并在同一个 goroutine 上执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func main() {
g := getg()

...
// 执行栈最大限制:1GB(64位系统)或者 250MB(32位系统)
if sys.PtrSize == 8 {
maxstacksize = 1000000000
} else {
maxstacksize = 250000000
}
...

// 启动系统后台监控(定期垃圾回收、抢占调度等等)
if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon
systemstack(func() {
newm(sysmon, nil)
})
}
...

// 初始化init
// initTask 的实现见 src/cmd/compile/internal/gc/init.go 中 fninit
doInit(&main_inittask)

// 启动GC清扫工作
gcenable()
...

// 执行用户 main 包中的 main 函数
fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
fn()

...
// 直接退出
exit(0)
...
}



1
2
3
4
5
6
7
8
9
10
11
12
13
_rt0_amd64_darwin -> _rt0_amd64 -> runtime·rt0_go -> runtime.main  -> exit(0)
↓ ↓
runtime·check doInit
↓ ↓
runtime·args gcenable
↓ ↓
runtime·osinit main.main

runtime·schedinit

runtime·newproc

runtime·mstart

本文链接: https://overstack.me/202007/go-program-startup-process.html

版权声明: 本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK