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