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

问题描述 投票:0回答:1

我正在尝试分解Go调度程序的工作方式,在runtime/proc.go中看到的是:

  1. schedule函数调用execute运行goroutine
  2. execute的注释明确表示此函数永不返回。它调用在汇编文件之一中定义的gogo函数。
  3. gogo函数跳转到新goroutine的第一条指令的地址。
  4. 此goroutine完成后,再次调用schedule函数,因此我们返回到步骤1。

如果我的理解是正确的,那么该方案如何避免堆栈溢出?它是否与自动增加其大小的“无限”堆栈有关,还是我在这里丢失了一些东西?

go programming-languages
1个回答
0
投票

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

  1. 新goroutine是在称为g0的特殊goroutine中创建的,它是线程的主要goroutine。对C0的任何调用都会将堆栈从当前调用的Goroutine更改为C0(在go func中完成)。
  2. 创建goroutine时(在g0中),其堆栈(和/或程序计数器,PC)的构造方式类似于proc.go:newproc函数所调用的方式。这样做是为了确保goroutine完成并返回时,它将返回到proc.go:newproc1[当调用goexit并选择运行goroutine时,goexit函数将执行它(==通过schedule汇编函数跳转到其地址)。
  3. goroutine完成后,将它
  4. 返回
  5. execute功能,以汇编形式实现。该汇编函数调用gogo(不确定为什么需要在汇编中执行此额外步骤)。
  6. goexit函数将当前堆栈更改为当前线程的主goroutine,在内部称为proc.go:goexit1。这是通过调用goexit1(“机器线程调用”)完成的,该调用执行参数中接收到的任何功能。在这种情况下,g0功能将提供给mcall
  7. 以汇编形式实现的goexit0跳转到mcall的堆栈帧(SP)的地址,并执行mcallg0
  8. CALL函数在goexit0的上下文中被调用。已完成的goroutine放入空闲的goroutine列表中,如果以前增加了堆栈,则将其堆栈释放。
  9. goexit0函数再次调用g0,它选择要运行的goroutine,因此我们回到步骤1。
  10. 所以确实似乎这里没有递归。计划的goroutine本身从不调用goexit0:这是通过特殊的goroutine schedule完成的。我仍然不确定是否捕获了所有详细信息,因此请多加评论和其他答案。
© www.soinside.com 2019 - 2024. All rights reserved.