事实上,编译好的可执⾏⽂件真正执⾏时并⾮我们所写的 main.main 函数,因为编译器
总是会插⼊⼀段引导代码,完成诸如命令⾏参数、运⾏时初始化等⼯作,然后才会进⼊⽤
户逻辑。
程序的入口因平台而异:
1 | rt0_android_arm.s rt0_dragonfly_amd64.s rt0_linux_amd64.s ... |
rt0_linux_amd64.s:
1 | TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8 |
asm_amd64.s:
1 | TEXT runtime·rt0_go(SB),NOSPLIT,$0 |
runtime1.go:
1 | func args(c int32, v **byte) { |
os_windows.go:
1 | func osinit() { |
proc.go: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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62// The bootstrap sequence is:
//
// call osinit
// call schedinit
// make & queue new G
// call runtime·mstart
//
// The new G calls runtime·main.
func schedinit() {
// raceinit must be the first call to race detector.
// In particular, it must be done before mallocinit below calls racemapshadow.
_g_ := getg() //获取的是g0
if raceenabled {
_g_.racectx, raceprocctx0 = raceinit()
}
//最大系统线程数量限制
sched.maxmcount = 10000
tracebackinit()
moduledataverify()
//栈、内存分配器和调度器的相关初始化
stackinit()
mallocinit()
mcommoninit(_g_.m)
alginit() // maps must not be used before this call
modulesinit() // provides activeModules
typelinksinit() // uses maps, activeModules
itabsinit() // uses activeModules
msigsave(_g_.m)
initSigmask = _g_.m.sigmask
//处理命令行参数和环境变量
goargs()
goenvs()
//处理 GODEBUG、GOTRACEBACK 调试相关的环境变量设置
parsedebugvars()
//垃圾回收器初始化
gcinit()
sched.lastpoll = uint64(nanotime())
//通过 CPU核心数和GOMAXPROCS环境变量确定P的数量,P用于调度g到m上
procs := ncpu
if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
procs = n
}
if procs > _MaxGomaxprocs {
procs = _MaxGomaxprocs
}
if procresize(procs) != nil {
throw("unknown runnable goroutine during bootstrap")
}
if buildVersion == "" {
// Condition should never trigger. This code just serves
// to ensure runtime·buildVersion is kept in the resulting binary.
buildVersion = "unknown"
}
}
1 | // Called to start an M. |
1 | func mstart1() { |
1 | // go程序编译时,会在main包生成init函数,该函数内调用所有依赖的包的init函数,如果同一个包被程序重复引入多次,他的init函数只会执行一次 |
总结
• 所有 init 函数都在同⼀个 goroutine 内执⾏
• 所有 init 函数结束后才会执⾏ main.main 函数
参考
- 雨痕的 Go 1.5源码剖析