Go runtime 调度器初始化过程如何为?
摘要:原创文章,欢迎转载,转载请注明出处,谢谢。 0. 前言 上一讲 介绍了 Go 程序初始化的过程,这一讲继续往下看,进入调度器的初始化过程。 接着上一讲的执行过程,省略一些不相关的代码,执行到 runtimeasm_amd64.s:rt0_
原创文章,欢迎转载,转载请注明出处,谢谢。
0. 前言
上一讲 介绍了 Go 程序初始化的过程,这一讲继续往下看,进入调度器的初始化过程。
接着上一讲的执行过程,省略一些不相关的代码,执行到 runtime/asm_amd64.s:rt0_go:343L:
(dlv) si
asm_amd64.s:343 0x45431c* 8b442418 mov eax, dword ptr [rsp+0x18] // [rsp+0x18] 存储的是 argc 的值,eax = argc
asm_amd64.s:344 0x454320 890424 mov dword ptr [rsp], eax // 将 argc 移到 rsp,[rsp] = argc
asm_amd64.s:345 0x454323 488b442420 mov rax, qword ptr [rsp+0x20] // [rsp+0x20] 存储的是 argv 的值,rax = [rsp+0x20]
asm_amd64.s:346 0x454328 4889442408 mov qword ptr [rsp+0x8], rax // 将 argv 移到 [rsp+0x8],[rsp+0x8] = argv
asm_amd64.s:347 0x45432d e88e2a0000 call $runtime.args // 调用 runtime.args 处理栈上的 argc 和 argv
asm_amd64.s:348 0x454332 e8c9280000 call $runtime.osinit // 调用 runtime.osinit 初始化系统核心数
asm_amd64.s:349 0x454337 e8e4290000 call $runtime.schedinit
上述指令调用 runtime.args 处理函数参数,接着调用 runtime.osinit 初始化系统核心数。runtime.osinit 在 runtime.os_linux.go 中定义:
func osinit() {
ncpu = getproccount()
physHugePageSize = getHugePageSize()
osArchInit()
}
runtime.osinit 主要初始化系统核心数 ncpu,该核心是逻辑核心数。
接着进入到本文的正题调度器初始化 runtime.schedinit 函数。
1. 调度器初始化
调度器初始化的代码在 runtime.schedinit:
// The bootstrap sequence is:
//
// call osinit
// call schedinit
// make & queue new G
// call runtime·mstart
//
// The new G calls runtime·main.
func schedinit() {
// step1: 从 TLS 中获取当前执行线程的 goroutine,gp = m0.tls[0] = g0
gp := getg()
// step2: 设置最大线程数
sched.maxmcount = 10000
// step3: 初始化线程,这里初始化的是线程 m0
mcommoninit(gp.m, -1)
// step4: 调用 procresize 创建 Ps
procs := ncpu
if procresize(procs) != nil {
throw("unknown runnable goroutine during bootstrap")
}
}
省略了函数中不相关的代码。
首先,step1 调用 getg() 获取当前线程执行的 goroutine。runtime 中随处可见 getg(),它是一个内联的汇编函数,用于直接从当前线程的寄存器或栈 TLS 中获取当前线程执行的 goroutine。
