Goroutine调度器初始化过程详解

本文深入剖析了Go语言程序启动时调度器的初始化过程,包括从磁盘加载到内存、创建进程和主线程、栈的分配与调整,以及Go汇编语言在其中的作用。详细解释了如何通过g0、m0和p的关联建立调度系统,并展示了调度器初始化后的结构。此外,还探讨了goroutine的创建前的栈状态,为后续的goroutine分析奠定了基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package mainimport "fmt"func main() {
  
      fmt.Println("Hello World!")}

通过跟踪上述经典案例从启动到退出的完整运行流程,来分析Go调度器的初始化,以及goroutine的创建和退出,还有工作线程的调度循环和goroutine的切换等内容。

先从程序开始启动时分析调度器初始化。

在此之前先看下程序在执行第一条指令之前栈的初始状态。

任何由编译型语言(C、C++、Go以及汇编语言)编写的程序在被操作系统加载执行的顺序都会经过如下几个阶段:

  1. 从磁盘上将可执行程序读入内存。

  2. 创建进程和主线程。

  3. 为主线程分配栈空间。

  4. 将用户在命令行输入的参数拷贝到主线程的栈。

  5. 将主线程放入操作系统的运行队列,等待被调度起来执行。

在主线程被第一次调度起来执行第一条指令之前,主线程函数栈的状态如下所示:

之后在Linux平台运行go build编译hello.go得到可执行hello文件,再使用gdb调试。

在gdb中首先使用info files找到程序入口(Entry point)地址为0x452270,然后使用b*0x452270在0x452270处下个断点,gdb就会显示出来入口对应的源码为文件runtime/rt0_linux_amd64.s第8行。

go$ go build hello.go go$ gdb helloGNU gdb (GDB) 8.0.1(gdb) info filesSymbols from "go/main".Local exec file:`go/main', file type elf64-x86-64.Entry point: 0x4522700x0000000000401000 - 0x0000000000486aac is .text0x0000000000487000 - 0x00000000004d1a73 is .rodata0x00000000004d1c20 - 0x00000000004d27f0 is .typelink0x00000000004d27f0 - 0x00000000004d2838 is .itablink0x00000000004d2838 - 0x00000000004d2838 is .gosymtab0x00000000004d2840 - 0x00000000005426d9 is .gopclntab0x0000000000543000 - 0x000000000054fa9c is .noptrdata0x000000000054faa0 - 0x0000000000556790 is .data0x00000000005567a0 - 0x0000000000571ef0 is .bss0x0000000000571f00 - 0x0000000000574658 is .noptrbss0x0000000000400f9c - 0x0000000000401000 is .note.go.buildid(gdb) b *0x452270Breakpoint 1 at 0x452270: file /usr/local/go/src/runtime/rt0_linux_amd64.s, line 8.

使用代码编辑器找到runtime/rt0_linux_amd64.s文件,它是由Go汇编语言编写的源码文件,可参考《试着理解下Golang中的汇编语言》来阅读和理解以下代码,来看第8行:

TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8    JMP_rt0_amd64(SB)

上述代码第一行定义了_rt0_amd64_linux符号,并不是真正的CPU指令,下面的JMP才是主线程的第一条指令,这条指令意为跳到_rt0_amd64符号处继续执行(相当于Go/C的goto关键字)。

_rt0_amd64定义在runtime/asm_amd64.s文件第14行,代码如下:

TEXT _rt0_amd64(SB),NOSPLIT,$-8    MOVQ0(SP), DI// argc     LEAQ8(SP), SI // argv    JMPruntime·rt0_go(SB)

前两行指令将操作系统内核传递过来的argc和argv两个参数地址分别放在DI和SI寄存器中,第三行指令意为跳到rt0_go符号处继续执行。

rt0_go完成了Go程序启动时所有初始化工作,此函数比较长,也很复杂,所以只看调度器相关的初始化。

先来看runtime/asm_amd64.s文件第87行定义的符号rt0_go:

TEXT runtime·rt0_go(SB),NOSPLIT,$0    // copy arguments forward on an even stack
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luyaran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值