这里我们已经知道了go程序的入口,这篇文章主要是介绍go在启动时是怎么初始化的。先来看一下go启动函数rt0_go()
的汇编代码。
启动流程
//go程序启动时初始化工作
TEXT runtime·rt0_go(SB),NOSPLIT,$0
// 拷贝argc、argv
// 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 //sp16字节对齐
MOVQ AX, 16(SP) //argc放在sp+16处
MOVQ BX, 24(SP) //argv放在sp+24处
// create istack out of the given (operating system) stack.
// _cgo_init may update stackguard.
//初始化g0
//下面这段代码从主线程栈中分出一部分作为g0栈
MOVQ $runtime·g0(SB), DI//g0的地址放入DI寄存器
LEAQ (-64*1024+104)(SP), BX//BX=SP- 64*1024 + 104
MOVQ BX, g_stackguard0(DI)//g0.stackguard0 =SP- 64*1024 + 104
MOVQ BX, g_stackguard1(DI)//g0.stackguard1 =SP- 64*1024 + 104
MOVQ BX, (g_stack+stack_lo)(DI)//g0.stack.lo =SP- 64*1024 + 104
MOVQ SP, (g_stack+stack_hi)(DI)//g0.stack.hi =SP g0栈大小约64kb
// find out information about the processor we're on
#ifdef
...... // xxxx,一些cpu型号检查相关代码先忽略
#endif
//设置线程本地变量thread local storage,完后会有m0.tls[0]=&g0
LEAQ runtime·m0+m_tls(SB), DI//取m0.tls到DI寄存器
CALL runtime·settls(SB)//调用settls
// store through it, to make sure it works
get_tls(BX)
MOVQ $0x123, g(BX)//m0.tls[0]=0x123
MOVQ runtime·m0+m_tls(SB), AX//AX=m0.tls[0]
CMPQ AX, $0x123//
JEQ 2(PC)
CALL runtime·abort(SB)
ok:
// set the per-goroutine and per-mach "registers"
get_tls(BX)
LEAQ runtime·g0(SB), CX//CX=&g0
MOVQ CX, g(BX)//m0.tls[0]=&g0
LEAQ runtime·m0(SB), AX//AX=&m0
//绑定g0和m0
// save m->g0 = g0
MOVQ CX, m_g0(AX)//m0.g0=g0
// save m0 to g0->m
MOVQ AX, g_m(CX)//g0.m=m0
CLD // convention is D is always left cleared
CALL runtime·check(SB)
MOVL 16(SP), AX // copy argc
MOVL AX, 0(SP) //m0放入sp
MOVQ 24(SP), AX // copy argv
MOVQ AX, 8(SP) //g0放入sp+8
CALL runtime·args(SB)//除以操作系统传来的参数和env,忽略
CALL runtime·osinit(SB)//设置全局变量ncpu的值
CALL runtime·schedinit(SB)//调度器初始化
// create a new goroutine to start program
MOVQ $runtime·mainPC(SB), AX // entry
PUSHQ AX
PUSHQ $0 // arg size
CALL runtime·newproc(SB) //创建第一个goroutine,习惯成为main goroutine
POPQ AX
POPQ AX
// start this M
CALL runtime·mstart(SB) //主线程调度上面创建的main goroutine
//mstart永远不会返回,如果返回了则立马abort
CALL runtime·abort(SB) // mstart should never return
RET
go启动过程初始化可以通过下面这个流程图来加深理解(忽略了一些不重要环节)