golang调度程序如何以及为什么在runtime/proc.go:execute中递归运行goroutines?

golang调度程序如何以及为什么在runtime/proc.go:execute中递归运行goroutines?,go,programming-languages,Go,Programming Languages,我正在尝试分解Go调度器的工作原理,我在中看到的是: schedule函数调用execute运行goroutine execute的注释明确表示此函数永远不会返回。它调用一个程序集文件中定义的gogo函数 gogo函数跳转到新goroutine的第一条指令的地址 完成此goroutine后,将再次调用schedule函数,因此我们返回到步骤1 如果我的理解是正确的,那么这个方案如何避免堆栈溢出? 它是否与自动增加其大小的“无限”堆栈有关,或者我在这里遗漏了什么?因此我花了一些时间研究这个主题,现

我正在尝试分解Go调度器的工作原理,我在中看到的是:

  • schedule
    函数调用
    execute
    运行goroutine
  • execute
    的注释明确表示此函数永远不会返回。它调用一个程序集文件中定义的
    gogo
    函数
  • gogo
    函数跳转到新goroutine的第一条指令的地址
  • 完成此goroutine后,将再次调用
    schedule
    函数,因此我们返回到步骤1
  • 如果我的理解是正确的,那么这个方案如何避免堆栈溢出?
    它是否与自动增加其大小的“无限”堆栈有关,或者我在这里遗漏了什么?

    因此我花了一些时间研究这个主题,现在可以尝试回答我自己的问题。整个goroutine生命周期变得更加复杂:

  • 新的goroutine是在一个名为
    g0
    的特殊goroutine中创建的,它是线程的一种主goroutine。对
    go func
    的任何调用都会将堆栈从调用它的当前goroutine更改为
    g0
    (这在
    proc.go:newproc
    中完成)
  • 创建goroutine(在
    proc.go:newproc1
    中)时,其堆栈(和/或程序计数器,PC)的构造方式看起来像是由
    goexit
    函数调用的。这样做是为了保证当goroutine完成并返回时,它会返回到
    goexit
  • 调用
    schedule
    并选择运行goroutine时,
    execute
    函数执行它(=通过
    gogo
    汇编函数跳转到它的地址)
  • goroutine完成后,返回到在汇编中实现的
    goexit
    函数
  • 该汇编函数调用
    proc.go:goexit1
    (不确定为什么需要汇编中的这个额外步骤)
  • goexit1
    函数将当前堆栈更改为
    g0
    。这是通过调用
    mcall
    (“机器线程调用”)完成的,它执行参数中接收到的任何函数。在这种情况下,提供给
    mcall
    的函数是
    goexit0
  • 在汇编中实现的
    mcall
    跳转到
    g0
    的堆栈帧(SP)的地址,并对
    goexit0
    执行
    调用
  • g0
    的上下文中执行
    goexit0
    函数。它将一个完成的goroutine放在一个空闲goroutine列表中,如果先前增加了,则释放其堆栈
  • 然后,
    goexit0
    再次调用
    schedule
    ,它选择一个goroutine来运行,所以我们回到步骤3
  • 所以这里似乎没有递归。计划的goroutine本身从不调用
    schedule
    :这是由一个特殊的goroutine
    g0
    完成的。
    我仍然不确定我是否捕捉到了所有细节,因此我们非常感谢您的评论和补充回答。

    您所说的“彼此之间的goroutines”是什么意思?所有goroutine都独立运行,没有嵌套。此外,跳转到一个新的goroutine需要切换堆栈。每个goroutine都有自己的堆栈。没错,“彼此内部”部分没有意义,它们是独立运行的。但是,当一个goroutine完成时,会再次调用“schedule”函数,该函数将运行另一个goroutine(可能与前一个goroutine完全无关),因此该进程是递归的。该进程不是递归的,因为调用
    schedule
    时堆栈不同。进入
    schedule
    时的堆栈与跳转到新goroutine后的堆栈不同。虽然理解调度器可能是一个有趣的学术练习,但新开发人员认为他们可以猜测它是没有帮助的。语言规范和内存模型应该是足够的文档。如果使用goroutines的程序的行为不受影响,这就允许Go开发人员随时重写调度程序的细节,@Rick-777当然,我理解这一点。我只是出于好奇,把它当作一种锻炼。