go - golang 调度程序如何以及为什么在 runtime/proc.go:execute 中递归运行 goroutine?
问题描述
我试图分解 Go 调度程序的工作原理,而我在runtime/proc.go中看到的是:
- 运行 goroutine的
schedule
函数调用execute
execute
明确表示此函数永远不会返回的注释。它调用gogo
在其中一个程序集文件中定义的函数。- 该
gogo
函数执行跳转到新 goroutine 的第一条指令的地址。 - 这个 goroutine 完成后,
schedule
再次调用该函数,所以我们回到第 1 步。
如果我的理解是正确的,那么这个方案是如何避免堆栈溢出的呢?它是否与自动增加其大小的“无限”堆栈有关,或者我在这里遗漏了什么?
解决方案
所以我花了一些时间研究这个主题,现在可以尝试回答我自己的问题。整个 goroutine 生命周期变得更加复杂:
- 新的 goroutine 是在一个名为 的特殊 goroutine 中创建的
g0
,它是一种线程的主 goroutine。任何调用都会go func
将堆栈从调用它的任何当前 goroutine 更改为g0
(在 中完成proc.go:newproc
)。 - 当 goroutine 被创建(in
proc.go:newproc1
)时,它的堆栈(和/或程序计数器,PC)以一种看起来像是被goexit
函数调用的方式构造。这样做是为了保证当 goroutine 完成并返回时,它会返回到goexit
. - 当
schedule
被调用并选择运行一个 goroutine 时,execute
函数会执行它(== 通过gogo
汇编函数跳转到它的地址)。 - goroutine 完成后,它返回
goexit
函数,在汇编中实现。 - 该汇编函数调用
proc.go:goexit1
(不知道为什么需要汇编中的这个额外步骤)。 - 该
goexit1
函数将当前堆栈更改为g0
. 这是通过调用mcall
(“机器线程调用”)来完成的,该调用执行在参数中接收到的任何函数。在这种情况下,提供给的函数mcall
是goexit0
。 - 在
mcall
汇编中实现的 跳转到 的g0
堆栈帧 (SP) 的地址并执行CALL
到goexit0
。 - 该
goexit0
函数在 的上下文中执行g0
。它将一个已完成的 goroutine 放入空闲 goroutine 列表中,如果之前增加了堆栈,则释放其堆栈。 - 然后再次
goexit0
调用schedule
,它选择了一个 goroutine 来运行,所以我们回到第 3 步。
所以确实这里似乎没有递归。调度的 goroutine 本身从不调用schedule
:这是由一个特殊的 goroutine 完成的g0
。我仍然不确定我是否捕获了所有细节,因此感谢评论和其他答案。
推荐阅读
- python - VS 代码 python 格式化程序不工作(autopep8,黑色,yapf)
- http - System.ArgumentException: „提供的 URI 方案“http”无效;预期的“https”。参数名称:via”
- amazon-web-services - 从实例配置文件元数据服务检索凭证时出错。我们得到“Aws \ Exception \ CredentialsException
- python - 通过组合框选择图表类型
- java - 如何将初始化值与运行时存储的值进行比较
- java - 升级Gradle后出现这样的错误如何解决
- angular - 每当记录数稍微多一点时,网格就会花费更多时间来呈现
- xml - serde 可以用平面键值对反序列化 XML 吗?
- python - 如何使这个 Tkinter 和 OpenCV 程序更具可扩展性?
- excel - 仅隐藏与显示的用户表单相关的工作表