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。
阅读全文