2 minutes
Golang Init
初始化根据平台的不同而不同, 这里参考 adm64 的汇编. 入口参考 runtime#asm_amd64.s, _rt0_amd64(SB)、main(SB)、_rt0_amd64_lib(SB) 分别对应了 内部链接、外部连接以及共享库的三种不同的启动方式. 但是, 最终都会调用 rt0_go.
TEXT runtime·rt0_go(SB),NOSPLIT,$0
// 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)
// 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)
....
ok:
// set the per-goroutine and per-mach "registers"
get_tls(BX)
LEAQ runtime·g0(SB), CX
MOVQ CX, g(BX)
LEAQ runtime·m0(SB), AX
// 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)
CALL runtime·osinit(SB)
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)
POPQ AX
POPQ AX
// start this 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
DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) // runtime·mainPC指向了 runtime.main, 也就是第一个goroutine
主要执行了下面几件事情:
CALL runtime·args(SB) // 命令行参数传递, 参考 runtime1.go
CALL runtime·osinit(SB) // 平台相关, 获取cpu数目, physPageSize, 参考os_freebsd.go
CALL runtime·schedinit(SB) // 负责了 栈 、内存分配和垃圾回收、 并发等的初始化
MOVQ $runtime·mainPC(SB), AX // 指出了第一只goroutine
CALL runtime·newproc(SB) // 启动新的goroutine
CALL runtime·mstart(SB) // schedule: 发现可运行的goroutine并进行执行, 循环永远不返回
这里面, 重点是 mstart, 通过后面的 golang调度器分析可以知道, schedule是通过自旋线程来避免频繁的线程切换的, 并保证至少一个自旋线程->意味着至少有一个空闲线程可以用来处理任务, 初始化的时候, 只有一个线程, 当准备进行goroutine执行的时候, 就会创建新的线程. 参看方法 #resetspinning->#wakep->#startm, 通过代码发现, 想要仅仅创建自旋m的话, 还需要有p. 创建m的方法参看 #newm
这里面, newproc 也是golang内部创建 goroutine的函数入口.
第一个执行的goroutine: runtime#main, 负责了 runtime_init、gcenable、用户main函数入口. 其中 runtime_init 通过看连接可以知道, 实际上是启动了新的goroutine进行 forcegchelper, 通过定时器触发gc操作. gcenable 和gc有关, 启动新的goroutine进行bgsweep
286 Words
2019-04-21 12:04 +0800