我正在尝试分解Go调度程序的工作方式,我在runtime/proc.go中看到的是:
schedule
函数调用execute
来运行goroutine execute
的注释明确表示此函数永不返回。它调用在汇编文件之一中定义的gogo
函数。gogo
函数跳转到新goroutine的第一条指令的地址。schedule
函数,因此我们返回到步骤1。如果我的理解是正确的,那么该方案如何避免堆栈溢出? 它是否与自动增加其大小的“无限”堆栈有关,还是我在这里丢失了一些东西?
答案 0 :(得分:0)
因此,我花了一些时间研究该主题,现在可以尝试回答我自己的问题。整个goroutine生命周期变得更加复杂:
g0
的特殊goroutine中创建的,它是线程的主要goroutine。对go func
的任何调用都会将堆栈从被调用的当前goroutine更改为g0
(在proc.go:newproc
中完成)。proc.go:newproc1
中,其栈(和/或程序计数器,PC)的构造看起来像{{ 1}}函数。这样做是为了确保goroutine完成并返回时,它将返回到goexit
。goexit
并选择运行goroutine时,schedule
函数将执行该例程(==通过execute
汇编函数跳转至其地址)。gogo
函数,该函数在汇编中实现。goexit
(不确定为什么需要在汇编中执行此额外步骤)。proc.go:goexit1
函数将当前堆栈更改为goexit1
。这是通过调用g0
(“机器线程调用”)完成的,该调用执行参数中接收到的任何函数。在这种情况下,提供给mcall
的函数是mcall
。goexit0
跳转到mcall
的堆栈帧(SP)的地址,并对g0
执行CALL
。goexit0
函数在goexit0
的上下文中执行。它将完整的goroutine放入空闲的goroutine列表中,并在以前增加了堆栈的情况下释放其堆栈。g0
再次调用goexit0
,这将选择要运行的goroutine,因此我们回到步骤3。因此,这里确实似乎没有递归。计划的goroutine本身从不调用schedule
:这是通过特殊的goroutine schedule
完成的。
我仍然不确定我是否捕获了所有详细信息,因此请多加评论和其他答案。