为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
米琪卡哇伊 2023-07-31 15:43:36
我试图分解 Go 调度程序的工作原理,我在runtime/proc.go中看到的是:该schedule函数调用execute运行一个 goroutine的注释execute明确指出该函数永远不会返回。它调用gogo汇编文件之一中定义的函数。该gogo函数执行跳转到新 goroutine 的第一条指令的地址。当这个 goroutine 完成后,schedule函数会被再次调用,所以我们回到了步骤 1。如果我的理解是正确的,那么这个方案是如何避免堆栈溢出的呢?它与自动增加其大小的“无限”堆栈有关,还是我在这里遗漏了一些东西?
查看完整描述

1 回答

?
幕布斯6054654

TA贡献1876条经验 获得超7个赞

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

  1. 新的 goroutine 在一个名为 的特殊 goroutine 中创建g0,它是线程的主 goroutine。任何对 的调用都会go func将堆栈从调用它的当前 Goroutine 更改为g0(这是在 中完成的proc.go:newproc)。

  2. 当创建 goroutine 时(在 中),它的堆栈(和/或程序计数器,PC)以看起来像是由函数调用的proc.go:newproc1方式构建。这样做是为了保证当 goroutine 完成并返回时,它会返回到.goexitgoexit

  3. schedule调用 并选择运行一个 goroutine 时,该execute函数将执行它(== 通过gogo汇编函数跳转到其地址)。

  4. Goroutine 完成后,它返回goexit在汇编中实现的函数。

  5. 该汇编函数调用proc.go:goexit1(不确定为什么需要汇编中的这个额外步骤)。

  6. goexit1函数将当前堆栈更改为g0. 这是通过调用mcall(“机器线程调用”)来完成的,它执行参数中接收到的任何函数。在这种情况下,提供给的函数mcallgoexit0

  7. 在汇编中实现的 跳转到的堆栈帧 (SP)mcall的地址并执行to 。g0CALLgoexit0

  8. goexit0函数在 的上下文中执行g0。它将一个已完成的 goroutine 放入空闲 goroutine 列表中,并释放其堆栈(如果之前已增加)。

  9. 然后再次goexit0调用schedule,选择一个 goroutine 来运行,所以我们回到步骤 3。

所以这里确实似乎没有递归。调度的 goroutine 本身从不调用schedule:这是由一个特殊的 goroutine 完成的g0。我仍然不确定我是否捕获了所有细节,因此欢迎评论和其他答案。


查看完整回答
反对 回复 2023-07-31
  • 1 回答
  • 0 关注
  • 59 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信