runtime·main
[TOC]
@TODO
runtime·main 汇编真入口
以amd64架构为例,入口函数是在runtime/asm_amd64.s
中定义的main函数,main函数会调用 runtime·rt0_go
来执行程序的初始化和启动调度系统。
在 runtime·rt0_go
中,通过 runtime·args(SB)
处理参数,runtime·osinit(SB)
进行 os 初始化,调用 runtime·schedinit(SB)
进行调度系统初始化,进行栈分配,将全局变量 g0 和 m0 绑定,调用 runtime·mstart(SB) 启动线程。
TEXT main(SB),NOSPLIT,$-8
JMP runtime·rt0_go(SB)
TEXT runtime·rt0_go(SB),NOSPLIT,$0
...
// set the per-goroutine and per-mach "registers"
get_tls(BX) // tls thread-local storage
LEAQ runtime·g0(SB), CX
MOVQ CX, g(BX)
LEAQ runtime·m0(SB), AX
// m0和g0互相绑定
// save m->g0 = g0
MOVQ CX, m_g0(AX)
// save m0 to g0->m
MOVQ AX, g_m(CX)
CLD // convention is D is always left cleared
CALL runtime·check(SB)
MOVL 16(SP), AX // copy argc
MOVL AX, 0(SP)
MOVQ 24(SP), AX // copy argv
MOVQ AX, 8(SP)
CALL runtime·args(SB) // 将argc, argv设置到static全局变量中了
CALL runtime·osinit(SB) // os初始化, 设置runtime.ncpu,不同平台实现方式不一样
CALL runtime·schedinit(SB) // 调度系统初始化, 根据GOMAXPROCS设置使用的procs等
// 创建一个goroutine,然后开启执行程序
MOVQ $runtime·mainPC(SB), AX // entry
PUSHQ AX
PUSHQ $0 // arg size
CALL runtime·newproc(SB)
POPQ AX
POPQ AX
// 启动 OS 线程 M
CALL runtime·mstart(SB)
CALL runtime·abort(SB) // mstart should never return
RET
// Prevent dead-code elimination of debugCallV1, which is
// intended to be called by debuggers.
MOVQ $runtime·debugCallV1(SB), AX
RET
// m0和g0互相绑定
CALL runtime·args(SB) // 处理args
CALL runtime·osinit(SB) // os初始化
CALL runtime·schedinit(SB) // 调度系统初始化
runtime.main() main goroutine
在系统栈上调用 newm(sysmon, nil, -1)
,分配一个OS 线程运行后台监控 sysmon
,通过 sysmon
可以在程序启动阶段进行死锁检查,定期垃圾回收,调度抢张等工作。
调用 runtime.doInit 执行内部 init 函数,这个必须要在defer 之前执行,为何?
调用 runtime.gcenable 启动一个goroutine进行gc清扫。
调用 runtime.doInit 执行各种package的 init 函数
执行用户定义的包级别的 init 函数
执行 main_main 函数,也就是用户代码中的 main 函数
runtime内存init函数的执行,
runtime_init
是由编译器动态生成的,里面包含了 runtime 包中所有的 init 函数 。执行
main_init
函数,编译器动态生成的,包括用户定义的所有的init函数。若是 exit(0) 后,进程没退出,由接下来的代码确保进程一会退出,就是for循环一直访问非法地址,正常情况下,一但出现非法地址访问,系统就会把该进程杀死,用这样的方法来确保进程退出。
// The main goroutine.
func main() {
g := getg()
// Racectx of m0->g0 is used only as the parent of the main goroutine.
// It must not be used for anything else.
g.m.g0.racectx = 0
// Max stack size is 1 GB on 64-bit, 250 MB on 32-bit.
// Using decimal instead of binary GB and MB because
// they look nicer in the stack overflow failure message.
if sys.PtrSize == 8 {
maxstacksize = 1000000000
} else {
maxstacksize = 250000000
}
// Allow newproc to start new Ms.
mainStarted = true
if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon
systemstack(func() {
newm(sysmon, nil, -1)
})
}
// Lock the main goroutine onto this, the main OS thread,
// during initialization. Most programs won't care, but a few
// do require certain calls to be made by the main thread.
// Those can arrange for main.main to run in the main thread
// by calling runtime.LockOSThread during initialization
// to preserve the lock.
lockOSThread()
if g.m != &m0 {
throw("runtime.main not on m0")
}
doInit(&runtime_inittask) // must be before defer
if nanotime() == 0 {
throw("nanotime returning zero")
}
// Defer unlock so that runtime.Goexit during init does the unlock too.
needUnlock := true
defer func() {
if needUnlock {
unlockOSThread()
}
}()
// Record when the world started.
runtimeInitTime = nanotime()
gcenable()
main_init_done = make(chan bool)
if iscgo {
if _cgo_thread_start == nil {
throw("_cgo_thread_start missing")
}
if GOOS != "windows" {
if _cgo_setenv == nil {
throw("_cgo_setenv missing")
}
if _cgo_unsetenv == nil {
throw("_cgo_unsetenv missing")
}
}
if _cgo_notify_runtime_init_done == nil {
throw("_cgo_notify_runtime_init_done missing")
}
// Start the template thread in case we enter Go from
// a C-created thread and need to create a new thread.
startTemplateThread()
cgocall(_cgo_notify_runtime_init_done, nil)
}
doInit(&main_inittask)
close(main_init_done)
needUnlock = false
unlockOSThread()
if isarchive || islibrary {
// A program compiled with -buildmode=c-archive or c-shared
// has a main, but it is not executed.
return
}
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()
if raceenabled {
racefini()
}
// Make racy client program work: if panicking on
// another goroutine at the same time as main returns,
// let the other goroutine finish printing the panic trace.
// Once it does, it will exit. See issues 3934 and 20018.
if atomic.Load(&runningPanicDefers) != 0 {
// Running deferred functions should not take long.
for c := 0; c < 1000; c++ {
if atomic.Load(&runningPanicDefers) == 0 {
break
}
Gosched()
}
}
if atomic.Load(&panicking) != 0 {
gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1)
}
exit(0)
for {
var x *int32
*x = 0
}
}